美文网首页
Vue组件通信

Vue组件通信

作者: A郑家庆 | 来源:发表于2019-10-02 18:48 被阅读0次

    attrs

    背景

    随着项目复杂度的提高,组件嵌套的层级越来越深,之前的组件通信一般使用v-bind和props组合使用,但是我们发现这种方式只是适用于父子组件,如果是孙子组件的话就需要将父组件的数据传递给子组件,子组件的数据再传递给孙组件,这样子就需要写很多props,有没有哪种方式可以直接将父组件直接传递给孙组件,让代码更加简洁?这就是$attrs的由来,解决跨组件数据传递,注意只对孙子组件有效,而且class和style数据传递除外。

    使用

    首先我们有三个嵌套组件父A-子B-孙C,然后我们想让A中的数据传入C中,用props的做法是这样子的:

      <div id="app">
        A{{msg}}
        <component-b :msg="msg"></component-b>
      </div>
    
      <script>
        let vm = new Vue({
          el: '#app',
          data: {
            msg: '100'
          },
          components: {
            'ComponentB': {
              props: ['msg'],
              template: `<div>B<component-c :msg="msg"></component-c></div>`,
              components: {
                'ComponentC': {
                  props: ['msg'],
                  template: '<div>C{{msg}}</div>'
                }
              }
            },
            
          }
        })
      </script>
    

    组件B并没有使用父组件传递过来的msg,而是直接传递给组件C,除了这样子有没有什么方式直接将数据传递给C呢,下面我们来看$attrs的写法:

      <script>
        let vm = new Vue({
          el: '#app',
          data: {
            msg: '100'
          },
          components: {
            'ComponentB': {
              template: `<div>B<component-c v-bind="$attrs"></component-c></div>`,
              components: {
                'ComponentC': {
                  props: ['msg'],
                  template: '<div>C{{msg}}</div>'
                }
              }
            },
            
          }
        })
      </script>
    

    总结:为了解决跨组件通信,避免多写props,而提出了$attrs,props和$attrs都可以用来父子组件通信,接收父组件传递过来的数据,但是props的优先级高于$attrs,如果子组件中props、$attrs都有写,那么数据只会被props接收,注意$attrs不能接收class和style传过来的数据。

    inheritAttrs

    背景
    <template>
      <div class="home">
        <mytest  :title="title" :massgae="massgae"></mytest>
      </div>
    </template>
    <script>
    export default {
      name: 'home',
      data () {
        return {
          title:'title1111',
          massgae:'message111'
        }
      },
      components:{
        'mytest':{
          template:`<div>这是个h1标题{{title}}</div>`,
          props:['title'],
          data(){
            return{
              mag:'111'
            }
          },
          created:function(){
            console.log(this.$attrs)//注意这里
          }
        }
      }
    }
    </script>
    

    上边的代码,我们在组件里只是用了title这个属性,massgae属性我么是没有用的,那么下浏览器渲染出来是什么样呢?如下图:


    image.png

    我们看到:组件内未被注册的属性将作为普通html元素属性在子组件的根元素上渲染,虽然在一般情况下不会对子组件造成影响,但是就怕遇到一些特殊情况,比如:

    <template>
        <childcom :name="name" :age="age" type="text"></childcom>
    </template>
    <script>
    export default {
        'name':'test',
        props:[],
        data(){
            return {
                'name':'张三',
                'age':'30',
                'sex':'男'
            }
        },
        components:{
            'childcom':{
                props:['name','age'],
                template:`<input type="number" style="border:1px solid blue">`,
            }
        }
    }
    </script>
    
    image.png

    我们看到父组件的type="text"覆盖了input上type="number",这不是我想要的,我需要input上type=number类型不变,但是我还是想取到父组件上的type="text"的值,这时候inheritAttrs就派上用场了。

    <template>
        <childcom :name="name" :age="age" type="text"></childcom>
    </template>
    <script>
    export default {
        'name':'test',
        props:[],
        data(){
            return {
                'name':'张三',
                'age':'30',
                'sex':'男'
            }
        },
        components:{
            'childcom':{
                inheritAttrs:false,
                props:['name','age'],
                template:`<input type="number" style="border:1px solid blue">`,
                created () {
                    console.log(this.$attrs.type)
                }
            }
        }
    }
    </script>
    
    image.png

    总结:默认情况下父组件传递数据给子组件但是没被props的特性绑定将会回退且作为普通的html特性应用在子组件的根元素上。inheritAttrs属性用来去掉这种默认行为,来避免不可预知的影响。注意 inheritAttrs: false 选项不会影响 style 和 class 的绑定。

    $listeners

    背景

    上面讲了attrs是为了跨组件传递数据,那如果想通过孙子组件来给父组件传递数据呢?之前的做法也是一层一层的向上传递,比如用emit方法,但是子组件如果用不到,只是想改变父组件的数据,这时候我们就可以使用$listeners。

    <template>
        <div>
            <childcom :name="name" :age="age" :sex="sex" @testChangeName="changeName"></childcom>
        </div>
    </template>
    <script>
    export default {
        'name':'test',
        props:[],
        data(){
            return {
                'name':'张三',
                'age':'30',
                'sex':'男'
            }
        },
        components:{
            'childcom':{
                props:['name'],
                template:`<div>
                    <div>我是子组件   {{name}}</div>
                    <grandcom v-bind="$attrs" v-on="$listeners"></grandcom>
                </div>`,
               
                components: {
                    'grandcom':{
                        template:`<div>我是孙子组件-------<button @click="grandChangeName">改变名字</button></div>`,
                        methods:{
                            grandChangeName(){
                               this.$emit('testChangeName','kkkkkk')
                            }
                        }
                    }
                }
            }
        },
        methods:{
            changeName(val){
                this.name = val
            }
        }
    }
    </script>
    

    listeners是一个对象,里面包含了作用在这个组件上的所有监听器。 **`总结:listeners可以让你在孙子组件改变父组件的值,也是属于跨组件通信。`**

    参考文章:https://www.jianshu.com/p/ce8ca875c337

    相关文章

      网友评论

          本文标题:Vue组件通信

          本文链接:https://www.haomeiwen.com/subject/tovcpctx.html