• Vue组件通信整理


    总结

    常见使用场景可以分为三类:

    父子组件通信: props$parent / $childrenprovide / injectref$attrs / $listeners

    兄弟组件通信: eventBusvuex

    跨级通信: eventBusvuexprovide / inject$attrs / $listeners

    Props

    注意:HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名。

    <!-- 父组件 -->
    <children v-bind:props-id="id" ></children>
    
    <!-- 父组件数据data -->
    id:3
    
    
    <!-- 子组件 -->
    
    <!-- 方式一 -->
    props:['propsId']
    
    <!-- 方式二 -->
    props:{
      propsId:{
        type:number,
        requeired:true
      }
    }
    
    

    $emit / $on

    详细介绍 https://www.cnblogs.com/Scooby/p/14775547.html

    $children / $parent

    注意 $children 并不保证顺序,也不是响应式的。在 3.x 中,$children property 已被移除,且不再支持。

    <!-- 子组件 -->
    children-one   data {title:'Children 1'}
    children-two   data {title:'Children 2'}
    children-three data {title:'Children 3'}
    
    <!-- 父组件 -->    
    <children-one></children-one>
    <children-two></children-two>
    <children-three></children-three>
    
    <!-- 父组件数据 -->  
    <!-- 特别注意:如果你需要访问子组件实例,我们建议使用 $refs。 -->
    <!-- 特别注意:返回类型为Array<Vue instance> 。-->
    
    this.$children    //[object Object],[object Object],[object Object]
    
    <!-- 子组件数据 可以获取到父组件的数据和方法 -->   
    this.$parent      //[object Object]
    
    

    节制地使用 $parent$children - 它们的主要目的是作为访问组件的应急方法。更推荐用 props 和 events 实现父子组件通信。

    特别注意边界情况。如在#app上拿$parent得到的是new Vue()的实例,在这实例上再拿$parent得到的是undefined,而在最底层的子组件拿$children是个空数组。也要注意得到$parent$children的值不一样,$children 的值是数组,而$parent是个对象。

    provide / inject

    provide / inject是非响应式。

    通常,当我们需要从父组件向子组件传递数据时,我们使用 props。想象一下这样的结构:有一些深度嵌套的组件,而深层的子组件只需要父组件的部分内容。在这种情况下,如果仍然将 prop 沿着组件链逐级传递下去,可能会很麻烦。

    对于这种情况,我们可以使用一对 provideinject。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。这个特性有两个部分:父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这些数据。

    provide链。当子孙组件上层父组件重复定义provide提供的值。它的行为跟原型链是一致的——沿着链向上寻找,只要找到就停止。

    注意:provideinject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。既如果在祖辈组件里更改provide的值,在孙子组件里是拿不到更新后的值。

    /**
    * 数据格式:
    * provide:Object | () => Object
    * inject:Array<string> | { [key: string]: string | Symbol | Object }
    *
    **/
    
    //祖先组件
    //该写法会报错,当需要传递是data中的数据时。
    //Uncaught TypeError: Cannot read property 'color' of undefined 。因为访问不到Vue实例 。   
    provide: {
      color: this.color, 
    }
      
    //正确写法,但是此种写法依然不是可响应式的。 
    provide() {
      return {
        color: this.color,
      };
    }
        
        
    /**
    * 子孙组件
    
    *inject用来指定一个数组或者一个对象,数组的话就放provide里字段的名称,而对象的话可以指定
    *  当前实例中的字段名
    *  对应provide里的字段名
    *  默认值或者返回默认值的函数
    *
    **/
    
    // 写法一
    const Child = {
      inject:['bar']
    }  
    
    // 写法二
    const Child = {
      inject: {
        foo: {
          from: 'bar',       //如果它需要从一个不同名字的 property 注入,则使用 from 来表示其源 property。
          default: () => "默认值" //在 vue 2.5.0+ 的注入可以通过设置默认值使其变成可选项 
        }
      }
    }     
        
      
        
    

    provide / inject 可响应式

    第一种方法:把值转为函数,记得要用箭头函数,不然不能正确获取this。
    //祖先组件
    data{ demoData:"red" } 
    
    provide() {
      return {
        test: () => {
          return this.demoData;
        },
      };
    }
    
    //子孙组件
    <template>
      <div :style="{'color':test()}">测试数据{{test()}}</div>
    </template>
    
    
    第二种方法:把provide所在的Vue实例给传递下去。
    /**
    * 推荐此种主流写法。
    * 可以看到很多UI组件库就是通过这个方式来传递属性的,因为有可能在不确定层级的子组件里要获得祖组件里的值。
    *
    **/
    
    //祖先组件
    data{ demoData:"red" } 
    
    provide() {
      return {
        test: this,
      };
    }
    
    //子孙组件
    <template>
      <div :style="{'color':test.demoData}">测试数据{{test.demoData}}</div>
    </template>
    
    

    函数式组件使用inject

    由于函数式组件的一切都是通过context来传递的,不能像普通组件一样来写inject配置,所以对于函数式组件来说,要拿到provide提供的值就要从context.injections字段里去取。

    ref / $refs

    ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例:

    <!-- 如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素 -->
    <p ref="p">hello</p>
    
    
    
    <!-- 如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据 -->
    
    <!-- child-component组件 -->
    data{ test: "demoData" } 
    
    <!-- 父组件组件 -->
    <child-component ref="child"></child-component>
    this.$refs.child.test    // demoData
    
    

    v-for 用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组。

    关于 ref 注册时间的重要说明:因为 ref 本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们 - 它们还不存在!$refs 也不是响应式的,因此你不应该试图用它在模板中做数据绑定。

    eventBus

    在vue项目中,父子组件间的通讯很方便。但兄弟组件或多层嵌套组件间的通讯,就会比较麻烦。这时,使用eventBus通讯,就可以很便捷的解决这个问题。

    eventBus可以在全局定义,实现全项目通讯,使用方法也很简单。

    全局定义,可以将eventBus绑定到vue实例的原型上,也可以直接绑定到window对象上。

    eventBus绑定到vue实例的原型

    // eventName为自定义事件名称
    // 初始化-全局定义。mian.js
    Vue.prototype.$EventBus = new Vue();
    
    // 触发事件
    this.$EventBus.$emit('eventName', param1,param2,...)
    
    // 监听事件
    this.$EventBus.$on('eventName', (param1,param2,...)=>{
        //需要执行的代码
    })
    
    // 移除事件
    this.$EventBus.$off('eventName');
    

    eventBus绑定到window对象

    // eventName为自定义事件名称
    // 初始化-全局定义。mian.js
    window.eventBus = new Vue();
    
    // 触发事件
    EventBus.$emit('eventName', param1,param2,...)
    
    // 监听事件
    EventBus.$on('eventName', (param1,param2,...)=>{
        //需要执行的代码
    })
    
    // 移除事件
    EventBus.$off('eventName');
    

    VUEX

    详情文章链接 https://www.cnblogs.com/Scooby/p/15214335.html

    $attrs / $listeners

    多级组件嵌套需要传递数据时,通常使用的方法是通过vuex。如果仅仅是传递数据,而不做中间处理,使用 vuex 处理,这就有点大材小用了。所以就有了 $attrs / $listeners ,通常配合 inheritAttrs 一起使用。

    inheritAttrs:默认值为 true。

    默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrsfalse,这些默认行为将会被去掉。而通过 (同样是 2.4 新增的) 实例 property $attrs 可以让这些 attribute 生效,且可以通过 v-bind 显性的绑定到非根元素上。查 看 官 网

    注意:这个选项不影响 classstyle 绑定。

    感觉还是挺晦涩难懂的,简单的说就是 inheritAttrs:true 继承除props之外的所有属性;inheritAttrs:false 只继承class属性。

    $attrs:包含了父作用域中不被认为 (且不预期为) props 的特性绑定 (class 和 style 除外),并且可以通过 v-bind=”$attrs” 传入内部组件。当一个组件没有声明任何 props 时,它包含所有父作用域的绑定 (class 和 style 除外)。

    $listeners:包含了父作用域中的 (不含 .native 修饰符) v-on 事件监听器。它可以通过 v-on=”$listeners” 传入内部组件。它是一个对象,里面包含了作用在这个组件上的所有事件监听器,相当于子组件继承了父组件的事件。

    祖先组件

    <template>
       <child :name="name" :age="age" :infoObj="infoObj" @updateInfo="updateInfo" @delInfo="delInfo" />
    </template>
    <script>
        import Child from '../components/child.vue'
    
        export default {
            name: 'father',
            components: { Child },
            data () {
                return {
                    name: 'Lily',
                    age: 22,
                    infoObj: {
                        from: '上海',
                        job: 'policeman',
                        hobby: ['reading', 'writing', 'skating']
                    }
                }
            },
            methods: {
                updateInfo() {
                    console.log('update info');
                },
                delInfo() {
                    console.log('delete info');
                }
            }
        }
    </script>
    

    父组件:

    <template>
        <grand-son :height="height" :weight="weight" @addInfo="addInfo" v-bind="$attrs" v-on="$listeners"  />
        // 通过 $listeners 将父作用域中的事件,传入 grandSon 组件,使其可以获取到 father 中的事件
    </template>
    <script>
        import GrandSon from '../components/grandSon.vue'
        export default {
            name: 'child',
            components: { GrandSon },
            props: ['name'],
            data() {
              return {
                  height: '180cm',
                  weight: '70kg'
              };
            },
            created() {
                // 结果:age, infoObj, 因为父组件共传来name, age, infoObj三个值,由于name被 props接收了,所以只有age, infoObj属性
                console.log(this.$attrs);        
                console.log(this.$listeners); // updateInfo: f, delInfo: f
            },
            methods: {
                addInfo () {
                    console.log('add info')
                }
            }
        }
    </script>
    

    子组件:

    <template>
        <div>
            {{ $attrs }} --- {{ $listeners }}
        <div>
    </template>
    <script>
        export default {
            ... ... 
            props: ['weight'],
            created() {
                console.log(this.$attrs); // age, infoObj, height 
                console.log(this.$listeners) // updateInfo: f, delInfo: f, addInfo: f
                this.$emit('updateInfo') // 可以触发 father 组件中的updateInfo函数
            }
        }
    </script>
    

    参考链接

    vue中8种组件通信方式: https://juejin.cn/post/6844903887162310669

    eventBus详细原文链接: https://blog.csdn.net/qq_26834399/article/details/106387585

    vue中的$attrs和$listeners: https://www.cnblogs.com/dhui/p/12931953.html

    provide/inject详情文章链接: https://zhuanlan.zhihu.com/p/184967263

  • 相关阅读:
    C# 16进制字节转Int(涉及:Base64转byte数组)
    c# CRC-16 / MODBUS 校验计算方法 及 异或校验算法
    SqlSugar 用法大全
    SQL Server-聚焦NOLOCK、UPDLOCK、HOLDLOCK、READPAST你弄懂多少?
    使用 tabindex 配合 focus-within 巧妙实现父选择器
    DataX 3.0 源码解析一
    Golang必备技巧:接口型函数
    PID控制
    dockerfile,拷贝文件夹到镜像中(不是拷贝文件夹中的内容到镜像)
    什么是PKI?主要作用是什么?
  • 原文地址:https://www.cnblogs.com/Scooby/p/16244705.html
Copyright © 2020-2023  润新知