Vue2.6之——组件化

这是我第20篇。

1、组件通信

(1)父子通信

① props-$emit

② $refs

短信验证码、图形验证码组件我经常用$refs

this.$refs.captcha= 'xxx'

③ 子组件$children[0]
并不保证顺序,所以从来不用这个方法,除非只有一个子组件。

// parent 
this.$children[0].xx = 'xxx'
(2)兄弟通信

通过共同的祖辈组件搭桥,$parent$root
$root$parent 都能够实现访问父组件的属性和方法,两者的区别在于,如果存在多级子组件,通过$parent访问得到的是它最近的一级的父组件,通过$root得到的是它的根父组件。

brother1:
this.$parent.$emit('foo');
brother2:
this.$parent.$on('foo', handle) 
(3)祖先与后代通信

用于组件库的开发,只能祖先给后代传值.。
这时用props属性就会嵌套太多props,不是很合适。

祖先: provide() {
        return {hi: 'hello 后代'}
      }
后代:inject:['hi'] 
-----------------------------------
可直接返回this,子组件直接拿祖先的数据:
祖先: 
  provide() {
      return {hi: this}
  },
  data() {
    return {
        grandfa:'dxl'
    }
  }
后代:
  

{{hi.grandfa}}

inject:['hi']
(4)任意两个组件之间通信:事件总线 或 vue

事件总线:创建一个Bus类负责事件派发、监听和回调管理

// Bus:事件派发、监听和回调管理 
class Bus{ 
     constructor(){    
    // {    
    //   eventName1:[fn1,fn2],   
    //   eventName2:[fn3,fn4],   
    // }    
    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()
以上自定义bus类实现观察者模式
或者直接用vue实例即可。
Vue.prototype.$bus = new Vue()
// child1
this.$bus.$on('foo', handle) 
// child2
this.$bus.$emit('foo')

(5)$attrs$listeners(基本被Vuex替代了)
  • $listeners
    包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on=”$listeners” 传入内部组件——在创建更高层次的组件时非常有用。
  • $attrs
    包含了父作用域中不被认为 (且不预期为) props 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 props 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind=”$attrs” 传入内部组件——在创建更高层次的组件时非常有用。

举例一:
想象一下,你打算封装一个自定义input组件——MyInput,需要从父组件传入type,placeholder,title等多个html元素的原生属性。此时你的MyInput组件props如下:

props:['type','placeholder','title',...]

很繁琐不是吗?$attrs专门为了解决这种问题而诞生,这个属性允许你在使用自定义组件时更像是使用原生html元素。比如:

// 父组件


// 子组件


让MyInput组件实现focus事件:

// 父组件


// 子组件




举例二:
三个组件:Grandfa、Father、Son
Grandfa => Son: (爷爷给孙子传值)
通过$attrs传值

// Grandfa  传了一个静态placeholder值: 请输入
{{value}}
// Father Vue.component("Wrapper",{ template:`
` }); // Son Vue.component("son",{ template:`
`, });

Son => Grandfa: (孙子通知爷爷)


 computed: {
      rewriteListener() {
          const vm = this;
          return Object.assign({},
              this.$listeners,
              {
                input: (event) =>
                vm.$emit("input", event.target.value)
              }
          )
      }
  }

2、内容分发slot插槽

插槽语法是Vue实现的内容分发API,用于复合组件开发,在通用组件库开发中大量应用。
(注:Vue 2.6.0之后采用全新v-slot语法取代之前的slotslot-scope

(1)匿名插槽
// comp1
 
// parent hello
(2)具名插槽
// comp2 
// parent
(3)作用域插槽

以上两个子组件的插槽值只能由父组件决定安排,但是我们的实际业务中,往往是儿子安排老子...这时就要用到作用域插槽。

// comp3 
 // 子组件决定值是'your name is XXX'
// parent // slotProps这个命名可以随便起,或者直接解构:

3、sync修饰符

sync修饰符添加于v2.4,类似于v-model,它能⽤于修改传递到⼦组件的属性,可以简化子组件通知父元素更新传入参数这个动作的代码逻辑。
场景:⽗组件传递的属性⼦组件想修改
所以sync修饰符的控制能⼒都在⽗级,事件名称也相对固定update:xx

// 父组件将value传给子组件并使用.sync修饰符。






 


// 子组件触发事件:
this.$emit('update:obj1', "it is new key by my-com");
 

4、实战1:自定义表单组件

做几个自定义组件来更好的巩固知识。



index.vue:







KInput.vue:

  • 重点:v-bind="$attrs
    把父组件的 type="password"传了过来,但是此时会影响到div,这时就要用到 inheritAttrs,将其设为false避免顶层容器继承属性。
  • 实现:
    双向绑定::value@input
    派发校验事件






elementUI form表单input部分源码:

  // 派发,就是子组件向父组件派发事件
    dispatch (componentName, eventName, params) {
      // 获取当前组件的父组件
      var parent = this.$parent || this.$root
      // 拿到父组件名称
      var name = parent.$options.componentName
      // 通过循环的方式不断向父组件查找目标组件
      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent
        if (parent) {
          name = parent.$options.componentName
        }
      }
      // 当循环结束,证明目标父组件已找到(如果存在),就通知父组件触发相应事件
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params))
      }
    },

KCheckBox.vue:

还可以通过设置model选项修改默认行为:





KFormItem.vue:

  • 实现:
    给Input预留插槽 - slot
    能够展示label和校验信息
    能够进行校验



5、实战2:弹窗类组件

弹窗类组件的特点:

  • 在当前vue实例之外独立存在,通常挂载与body
  • 通过js动态创建,不需要在任何组件中声明

----------- create.js:--------------

import Vue from 'vue'
// 创建指定组件实例并挂载于body上
/**
* Component 组件
* props 属性值
*/
export default function create(Component, props) {
   // 0. 先创建vue实例
   const vm = new Vue({
     // render方法提供给我们一个h函数,它可以渲染VNode
     render(h) {
         return h(Component, {props})
     }
   }).$mount(); // 更新操作
   // 1. 上面vm帮我们创建组件实例
   // 2. 通过$children获取该组件实例
   cosole.log(vm.$root);
   const comp = vm.$children[0];
   // 3.追加至body
   document.body.appendChild(vm.$el);
   // 4.清理函数
   comp.remove = () => {
     document.body.removeChild(vm.$el);
     vm.$destroy();
   }
   return comp;
}

----------- notice.vue:--------------




未完待续 。。。

你可能感兴趣的:(Vue2.6之——组件化)