组件化是一门艺术,如何用好组件化优雅的实现页面,是每一个前端开发的必修课,最近学了vue组件化的相关课程,本文就对经常使用的vue组件化技术的进行总结整理。前端
//父组件
<HelloWorld msg="Welcome to Your Vue.js App"/>
//子组件
props: { msg: String }
复制代码
//父组件
<HelloWorld ref="hw"/>
this.$refs.hw.xx
复制代码
//子组件
this.$emit('add', good)
//父组件
<Cart @add="cartAdd($event)"></Cart>
复制代码
经过共同的祖辈组件搭桥,$parent
或$root
。vue
//兄弟组件1
this.$parent.$on('foo', handle)
//兄弟组件2
this.$parent.$emit('foo')
复制代码
因为嵌套层数过多,传递props不切实际,vue提供了provide / inject API完成该任务。vuex
//祖辈组件
<template>
<div id="app">
<router-view v-if="isRouterAlive" />
</div>
</template>
<script>
export default {
name: 'App',
data () {
return {
isRouterAlive: true
},
// 父组件中返回要传给下级的数据
provide () {
return {
reload: this.reload
}
},
methods: {
reload () {
this.isRouterAlive = false
this.$nextTick(() => {
this.isRouterAlive = true
})
}
}
}
</script>
复制代码
//孙组件
<template>
<popup-assign
:id="id"
@success="successHandle"
>
<div class="confirm-d-tit"><span class="gray-small-btn">{{ name }}</span></div>
<strong>将被分配给</strong>
<a
slot="reference"
class="unite-btn"
>
指派
</a>
</popup-assign>
</template>
<script>
import PopupAssign from '../PopupAssign'
export default {
//引用vue reload方法
inject: ['reload'],
components: {
PopupAssign
},
methods: {
async successHandle () {
this.reload()
}
}
}
</script>
复制代码
provide / inject 的优势:数组
provide / inject 的缺点:bash
provide 和 inject 主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。app
// 定义一个dispatch方法,指定要派发事件名称和数据
function dispatch(eventName, data) {
let parent = this.$parent
// 只要还存在父元素就继续往上查找
while (parent) {
// 父元素用$emit触发
parent.$emit(eventName,data)
// 递归查找父元素
parent = parent.$parent
}
}
// 使用,HelloWorld.vue
<h1 @click="dispatch('hello', 'hello,world')">{{ msg }}</h1>
// App.vue
this.$on('hello', this.sayHello)
复制代码
建立一个Bus类负责事件派发、监听和回调管理async
// Bus:事件派发、监听和回调管理
class Bus{
constructor(){
this.callbacks = {}
}
$on(name, fn){
this.callbacks[name] = this.callbacks[name] || []
this.callbacks[name].push(fn)
}
$emit(name, args){
if(this.callbacks[name]){
this.callbacks[name].forEach(cb => cb(args))
}
}
}
// main.js
Vue.prototype.$bus = new Bus() // child1
this.$bus.$on('foo', handle) // child2
this.$bus.$emit('foo')
复制代码
建立惟一的全局数据管理者store,经过它管理数据并通知组件状态变动ide
插槽语法是Vue实现的内容分发API,用于复合组件开发,该技术在通用组件库开发中有大量应用。函数
Vue 2.6.0以后采用全新v-slot语法取代以前的slot、slot-scope组件化
// comp1
<div>
<slot></slot>
</div>
// parent
<comp>hello</comp>
复制代码
// comp2
<div>
<slot></slot>
<slot name="content"></slot>
</div>
// parent
<Comp2>
<!-- 默认插槽用default作参数 -->
<template v-slot:default>具名插槽</template>
<!-- 具名插槽用插槽名作参数 -->
<template v-slot:content>内容...</template>
</Comp2>
复制代码
// comp3
<div>
<slot :foo="foo"></slot>
</div>
// parent
<Comp3>
<!-- 把v-slot的值指定为做用域上下文对象 -->
<template v-slot:default="ctx">
来自子组件数据:{{ctx.foo}}
</template>
</Comp3>
复制代码
做用于插槽能够实现组件和业务的剥离,适合应用于至少包含三级以上的组件层级,是一种优秀的组件化方案。
有一个购物网站,网站里有诸如 “猜你喜欢”,“每日特价”等商品列表,咱们能够将这个页面进行组件化拆分。
当咱们想要实现点击具体商品跳转到相应的详情页面,并但愿该点击能在ColumnList组件中实现时,传统作法,会将在Commodity的点击事件$emit
层层上抛至组件ColumnList,并在ColumnList中进行监听。但这种方法,使得子组件与业务紧耦合,利用做用域插槽就能够优雅的解决这个问题。
v-slot
属性赋值上下文对象//父组件
<my-input type="text" autocomplete placeholder="Please input something"></my-input>
//子组件
<div>
<input v-bind="$attrs" />
</div>
export default {
inheritAttrs: false
}
//渲染结果
<div>
<input type="text" autocomplete placeholder="Please input something" />
</div>
复制代码
inheritAttrs
:默认状况下父做用域的不被认做 props 的特性绑定 (attribute bindings) 将会“回退”且做为普通的 HTML 特性应用在子组件的根元素上。经过设置 inheritAttrs 到 false,这些默认行为将会被去掉。$attrs
:包含了父做用域中不做为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含全部父做用域的绑定 (class 和 style 除外),而且能够经过 v-bind="$attrs" 传入内部组件——在建立高级别的组件时很是有用。针对例如弹窗等短暂出现的组件,能够经过一个实例建立函数进行调用。 优势:
import Vue from 'vue';
export default function create(component, props) {
const vm = new Vue({
render(h) {
return h(component, {props});
}
}).$mount();
//vm.$el为Vue实例使用的根DOM元素
document.body.appendChild(vm.$el);
//vm.$children为当前实例的直接子组件
const comp = vm.$children[0];
comp.remove = function() {
document.body.removeChild(vm.$el);
vm.$destroy();
}
return comp;
}
复制代码