003.(高级)组件通信

Posted Ruovan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了003.(高级)组件通信相关的知识,希望对你有一定的参考价值。


01. 祖先与后代

  • 通过 provideinject可以进行跨级组件通信

    只能祖先给后代传值

    这个注入的值,主要用于读取,不建议更改

    确实需要更改,可以从祖先传一个函数,在祖先内进行更改

    // 在祖先元素提供
    provide(){
        return {
            foo: '123123',
            app: this // 甚至可以直接返回这个祖先实例
        }
    }
    
    // 后代元素
    export default {
        // 在后代元素注入祖先元素提供的值
        inject: ['foo']
    }
    

02. 派发与广播

  • 通过$on$emit定义两个自定义事件进行父子组件间的通信

    $emit会在当前组件上触发事件,然后在父组件上用v-on监听这个事件

    也可以通过$on在当前组件自己监听自己

  • 通过向上/向下递归遍历,寻找到指定name的组件

    • 每个组件都应设置一个组件名name,且组件名应该是唯一的
    • 在子组件可以调用dispatch方法

      • 用于向上级查找指定的组件,并触发自定义事件和传递数据
      • 上级组件需要用$on监听这个事件
    • 在父组件可以调用broadcast方法

      • 用于向下级查找指定的组件,并触发自定义事件和传递数据
      • 下级组件需要用$on监听这个事件
    // componentName--组件名,eventName--事件名,params--传递参数
    function broadcast(componentName, eventName, params) {
        // 获取当前组件的 子组件数组,遍历查找指定组件名的 子组件
        this.$children.forEach(child => {
            // 获取当前子组件的组件名
            const name = child.$options.name;
    
            if (name === componentName) {
                // 如果当前子组件的组件名与查找的组件名一致,则在当前子组件派发事件
                child.$emit.apply(child, [eventName].concat(params));
            } else {
                // 否则,在当前子组件中查找其子组件,继续向下查找
                broadcast.apply(child, [componentName, eventName].concat([params]));
            }
        });
    }
    
    function dispatch(componentName, eventName, params) {
        // 获取当前组件的父组件 或者 根组件
        let parent = this.$parent || this.$root;
        // 获取其父组件的组件名
        let name = parent.$options.name;
    
        while (parent && (!name || name !== componentName)) {
            // 没有组件名 或者 组件名非查找的组件名,则继续向上查找
            parent = parent.$parent;
            if (parent) {
                name = parent.$options.name;
            }
        }
        // 跳出上面循环,则表示 没有父组件 | 找到对应的组件了
        if (parent) {
            // 父组件存在,则表示找到了指定组件名的组件
            parent.$emit.apply(parent, [eventName].concat(params));
        }
    },
    
        export default {
            methods: {
                dispatch(componentName, eventName, params) {
                    dispatch.call(this, componentName, eventName, params);
                },
                broadcast(componentName, eventName, params) {
                    broadcast.call(this, componentName, eventName, params);
                }
            }
        };
    

03.关于mixin混入

  • 使用mixin混入,可以分发Vue组件中的可复用功能

    • 混入对象可以包含任意组件选项,如生命周期、基础数据、实例方法
    • 当组件使用时,混入对象的所有选项都将被【混入】到组件中
    // mixin.js文件-----
    let mixin = {
        // 写法同组件内的写法一致
        data(){return {}},
        created(){},
        methods:{}
    }
    export default mixin
    
    // 组件中-----
    import mixin from './mixin'
    export default {
        // 局部混入
        mixins: [mixin]
    }
    
    // 全局混入
    Vue.mixin({ mixin })
    
    • 同名的钩子函数(createdmounted),将合并为一个数组,且混入对象的钩子将在组件钩子之前调用
    • 对象选项(methodscomponents),将被合并为同一个对象,当组件有冲突时,以组件优先

    • 混入不同组件的【混入对象】,他们的方法和参数不共享

    • 当我们需要全局去注入一些methodsfilterhooks时我们就可以使用mixin来实现

    • 注意:一旦使用全局混入,它将影响每一个之后创建的 Vue 实例

  • 可以通过混入mixin将上面的派发与广播方法混入需要的组件中

    ...
    export default {
        methods: {
            dispatch(componentName, eventName, params) {
                dispatch.call(this, componentName, eventName, params);
            },
            broadcast(componentName, eventName, params) {
                broadcast.call(this, componentName, eventName, params);
            }
        }
    };
    

04. 组件构造器Extend

  • Vue.extend 的作用,就是基于 Vue 构造器,创建一个【类】,用于扩展实例

    • 它的参数跟new Vue的基本一样
    • 但data要跟组件一样,是个函数
    • 再配合$mount,就可以渲染组件,并且挂载到任意指定的节点上

    extend创建的是一个组件构造器,而不是一个具体的组件实例

    可以理解为构造了一个和vue组件内部有一样结构的对象

    最终还是要通过Vue.components注册才可以使用

    let Profile = Vue.extend({
        template: '',
        data() {
            return {
            }
        }
    })
    // 方式一:创建 Profile 实例,并挂载到一个元素上
    new Profile().$mount('#app')
    // 方式二:
    new Profile({ el: "#app" })
    
    // 方式三:文档之外手动挂载
    let myProfile = new Profile().$mount();
    export default {
        mounted(){
            // 因为这里是通过获取节点的方式添加到某个元素内,所以一定要在钩子函数中挂载,确保当前页面的dom节点加载完成
            let app = document.getElementById('app')
            app.appendChild(myProfile.$el)
        }
    }
    
    // 注册组件
    Vue.component('myProfile', Profile)
    // 或者在局部组件
    export default {
        components: {
            'myProfile': Profile
        }
    }
    

05. 工具函数

  • 通过定义工具函数递归、遍历,找到指定组件的name,并返回匹配的组件实例

    工具函数实现的思路,与派发与广播的思路基本一致

    // assist.js
    // context--组件实例,一般传入this(即当前vue组件实例),components--要查找的组件实例的名称
    // 由一个组件,向上找到最近的指定组件
    function findComponentUpward (context, componentName) {
      let parent = context.$parent;
      let name = parent.$options.name;
    
      while (parent && (!name || [componentName].indexOf(name) < 0)) {
        parent = parent.$parent;
        if (parent) name = parent.$options.name;
      }
      return parent;
    }
    // 由一个组件,向上找到所有的指定组件
    function findComponentsUpward (context, componentName) {
      let parents = [];
      const parent = context.$parent;
    
      if (parent) {
        if (parent.$options.name === componentName) parents.push(parent);
        return parents.concat(findComponentsUpward(parent, componentName));
      } else {
        return [];
      }
    }
    // 由一个组件,向下找到最近的指定组件
    function findComponentDownward (context, componentName) {
      const childrens = context.$children;
      let children = null;
    
      if (childrens.length) {
        for (const child of childrens) {
          const name = child.$options.name;
    
          if (name === componentName) {
            children = child;
            break;
          } else {
            children = findComponentDownward(child, componentName);
            if (children) break;
          }
        }
      }
      return children;
    }
    // 由一个组件,向下找到所有指定的组件
    function findComponentsDownward (context, componentName) {
      return context.$children.reduce((components, child) => {
        if (child.$options.name === componentName) components.push(child);
        const foundChilds = findComponentsDownward(child, componentName);
        return components.concat(foundChilds);
      }, []);
    }
    // 由一个组件,找到指定组件的兄弟组件: except表示是否把本身除外
    function findBrothersComponents (context, componentName, exceptMe = true) {
      let res = context.$parent.$children.filter(item => {
        return item.$options.name === componentName;
      });
      let index = res.findIndex(item => item._uid === context._uid);
      if (exceptMe) res.splice(index, 1);
      return res;
    }
    export {
    	findComponentUpward,
        findComponentsUpward,
        findComponentDownward,
        findComponentsDownward,
        findBrothersComponents
    };
    

以上是关于003.(高级)组件通信的主要内容,如果未能解决你的问题,请参考以下文章

LEETCODE 003 找出一个字符串中最长的无重复片段

React教程:父子组件传值(组件通信)

Vue第四天学习笔记之组件化高级

[新增EA003考勤系统演示片段]全程字幕-22套UML+Enterprise Architect建模示范视频

003-代码补全,运行,调试

在tablayout片段之间进行通信[重复]