vue01-组件化实践


组件通信常用方式

props

eventbus

vuex

自定义事件

边界情况

$parent

$children

$root

$refs

provide/inject

非prop特性

$attrs

$listeners

组件通信

props

父给子传值

// child 
props: { msg: String } 

// parent 

子给父传值

自定义事件

// child 
this.$emit('add', good) 

// parent 

事件总线

任意两个组件之间传值常用事件总线 或 vuex的方式。

// 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')

实践中通常用Vuex代替Bus,因为Vue已经实现了相应接口

vuex

创建唯一的全局数据管理者store,通过它管理数据并通知组件状态变更。

$parent/ $root

兄弟组件之间通信可通过共同祖辈搭桥,$parent或?$root。

// brother1 
this.$parent.$on('foo', handle) 
// brother2 
this.$parent.$emit('foo')

$children

父组件可以通过$children访问子组件实现父子通信。

// parent 
this.$children[0].xx = 'xxx'

注意:$children不能保证子元素顺序

$attrs/$listeners

包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 ( class 和 style 除外)。当一个组件没有

声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外),并且可以通过 v-

bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用

// child:并未在props中声明foo 

{{$attrs.foo}}

// parent

文档(web-link)

refs

获取子节点引用

// parent 
 

mounted() { this.$refs.hw.xx = 'xxx' }

provide/inject

能够实现祖先和后代之间传值

// ancestor 
provide() { return {foo: 'foo'} }

// descendant 
inject: ['foo']

范例:组件通信

组件通信范例代码请参考components/communication\

插槽

插槽语法是Vue 实现的内容分发 API,用于复合组件开发。该技术在通用组件库开发中有大量应用。

匿名插槽

// comp1 
// parent hello

具名插槽

将内容分发到子组件指定位置

// comp2 
// parent

作用域插槽

分发内容要用到子组件中的数据

// comp3 
// parent

范例

插槽相关范例请参考components/slots中代码

组件化实战

通用表单组件

收集数据、校验数据并提交

需求分析

实现KForm

指定数据、校验规则

KformItem

执行校验

显示错误信息

KInput

维护数据

最终效果:Element表单

范例代码查看components/form/ElementTest.vue

KInput

创建components/form/KInput.vue

 



**使用 KInput **

创建components/form/index.vue,添加如下代码:

 


实现****KFormItem

创建components/form/KFormItem.vue

 



使用****KFormItem

components/form/index.vue,添加基础代码:



实现****KForm

 



使用****KForm

components/form/index.vue,添加基础代码:

 



数据校验

Input通知校验

onInput(e) { 
    // ... 
    // $parent指FormItem 
    this.$parent.$emit('validate'); 
}

FormItem监听校验通知,获取规则并执行校验

inject: ['form'], 
// 注入 
mounted(){
    // 监听校验事件 
    this.$on('validate', () => { 
        this.validate()
    }) 
},
methods: { 
    validate() { 
        // 获取对应FormItem校验规则 
        console.log(this.form.rules[this.prop]); 
        // 获取校验值 
        console.log(this.form.model[this.prop]); 
    } 
},

安装async-validator: npm i async-validator -S

import Schema from "async-validator"; 
validate() { 
    // 获取对应FormItem校验规则 
    const rules = this.form.rules[this.prop]; 
    // 获取校验值 
    const value = this.form.model[this.prop]; 
    // 校验描述对象 
    const descriptor = { [this.prop]: rules }; 
    // 创建校验器 
    const schema = new Schema(descriptor); 
    // 返回Promise,没有触发catch就说明验证通过 
    return schema.validate({ [this.prop]: value }, errors => { 
        if (errors) { 
            // 将错误信息显示 
            this.error = errors[0].message; 
        } else { 
            // 校验通过 
            this.error = ""; 
        } 
    }); 
}

表单全局验证,为Form提供validate方法

validate(cb) { 
    // 调用所有含有prop属性的子组件的validate方法并得到Promise数组 
    const tasks = this.$children
    .filter(item => item.prop) 
    .map(item => item.validate());
    // 所有任务必须全部成功才算校验通过,任一失败则校验失败 P
    romise.all(tasks) 
        .then(() => cb(true)) 
        .catch(() => cb(false)) 
}

实现弹窗组件

弹窗这类组件的特点是它们在当前vue实例之外独立存在,通常挂载于body;它们是通过JS动态创建

的,不需要在任何组件中声明。常见使用姿势:

this.$create(
    Notice, { 
        title: '社会你杨哥喊你来搬砖',
        message: '提示信息', 
        duration: 1000 
    }).show();

create函数

import Vue from "vue"; 
// 创建函数接收要创建组件定义
function create(Component, props) { 
    // 创建一个Vue新实例 
    const vm = new Vue({ 
        render(h) {
            // render函数将传入组件配置对象转换为虚拟dom 
            console.log(h(Component, { props })); 
            return h(Component, { props }); } 
    }).$mount(); 
    //执行挂载函数,但未指定挂载目标,表示只执行初始化工作 
    // 将生成dom元素追加至body 
    document.body.appendChild(vm.$el); 
    // 给组件实例添加销毁方法 
    const comp = vm.$children[0]; 
    comp.remove = () => { 
        document.body.removeChild(vm.$el); vm.$destroy(); 
    };
    return comp; 
}
// 暴露调用接口 
export default create;

另一种创建组件实例的方式: Vue.extend(Component)

通知组件

建通知组件,Notice.vue

  

使用****create api

测试,components/form/index.vue