zoukankan      html  css  js  c++  java
  • vue 父子组件、兄弟组件传值

    参考文章:
    Vue2.x子同级组件之间数据交互

    vue组件间通信六种方式(完整版)

    (一)父子组件之间的通信

    父组件给子组件传参,子组件通过props拿到参数

    父组件:

    <template>
      <div>
        <h1>父组件</h1>
      <!-- 引入子组件 --> <child :sendMsg="fatherMsg"></child> </div> </template> <script> import child from '@/components/child' export default { name: 'father', components: { child }, data() { return { fatherMsg: '嗨,儿子' // 传递给子组件的值 } } } </script>

    子组件:通过props拿到父组件传递过来的值

    <template>
      <div>
        <h1>子组件</h1>
        <span>获取父组件传值:{{sendMsg}}</span> 
      </div>
    </template>
    
    <script>
    export default {
      name: 'child',
      data() {
        return {
    
        }
      },
      props: ['sendMsg'] // 拿到父组件绑定到sendMsg的值,然后在子组件下显示出来
    }
    </script>

    子组件给父组件传值:通过触发事件传递值,子组件可以使用 $emit 触发父组件的自定义事件。

    关键字:$emit()

    以上面的示例代码作为基础修改,子组件:

    <template>
      <div>
        <h1>子组件</h1>
        <span>获取父组件传值:{{sendMsg}}</span><hr>
        <button @click="sendToFather">子组件给父组件传值</button>
      </div>
    </template>
    
    <script>
    export default {
      name: 'child',
      data() {
        return {
          childMsg: '这是来自子组件的数据'
        }
      },
      props: ['sendMsg'],
      methods: {
        sendToFather: function() {
          this.$emit('getChildValue', this.childMsg); // 参数1 getChildValue作为中间状态,参数2 this.childMsg即为传递给父组件的数据
        }
      }
    }
    </script>

    父组件:

    <template>
      <div>
        <h1>父组件</h1>
        <!-- 引入子组件 定义一个on的方法监听子组件的状态,然后通过getChild方法获取子组件传递的数据-->
        <child :sendMsg="fatherMsg" v-on:getChildValue="getChild"></child>
        <span>这是来自子组件的数据:{{childValue}}</span>
      </div>
    </template>
    
    <script>
    import child from '@/components/child'
    export default {
      name: 'father',
      components: {
        child
      },
      data() {
        return {
          fatherMsg: '嗨,儿子',
          childValue: ''
        }
      },
      methods: {
        getChild: function(data) { // 此时的参数data为子组件传递的值,即this.$emit()的第二个参数
          this.childValue = data;
        }
      }
    }
    </script>

    (二)同级组件传递数据

    对于同级组件传值用的较多的情况,推荐直接使用vuex进行状态管理会比较方便。

    补充:面试被问到同级组件传递参数,平时很少去用这个,没答上来(尴尬),回来百度了下,原来就是通过一个中间桥接的方式进行传递(遭不住,之前看到过,没引起重视)

    其原理是先建立一个中间事件总线center.js,放在tools文件夹下,如下:

    import Vue from 'vue'
    
    export default new Vue()

    center.js中我们只创建了一个新的Vue实例,以后它就承担起了组件之间通信的桥梁了,也就是中央事件总线

    然后创建第一个子组件first.vue:

    <template>
      <div class="first-vue-box">
        <p>this is firstChild vue</p>
        <button @click="sendMsg">发送</button>
      </div>
    </template>
    <script>
    import bridge from '../tools/center'
    export default {
      data () {
        return {}
      },
      methods: {
        sendMsg () {
          bridge.$emit('firstChildMsg', 'this is firstChild Msg')
        }
      }
    }
    </script>
    <style lang="scss" scoped>
    .first-vue-box {
      border: 1px solid blue;
    }
    </style>

    这里先引入事件总线,通过事件总线点击按钮后将first.vue的信息通过事件firstChildMsg的形式发布出去了(我个人的理解是相当于通过事件总线,将first.vue的firstChildMsg这个事件暴露出去),用法跟子组件向父组件传参的模式一样

    然后再创建第二个组件second.vue:

    <template>
      <div class="second-child">
        <h4>this is second child vue</h4>
        <p>从first.vue获取同级组件传递过来的信息:{{message}}</p>
      </div>
    </template>
    <script>
    import bridge from '../tools/center'
    export default {
      data () {
        return {
          message: '默认值'
        }
      },
      mounted () {
        let _this = this
        bridge.$on('firstChildMsg', function (msg) {
          _this.message = msg
        })
      }
    }
    </script>

    在second.vue中再引入事件总线,然后通过$on(functionName, callback)监听first.vue暴露出来的firstChildMsg事件,通过回调函数获取first.vue传递出来的值(我个人是这么理解的) 

    引入两个子组件:

    <template>
      <div class="detail-div">
        <h3>首页详情</h3>
        <first-child></first-child>
        <second-child></second-child>
      </div>
    </template>
    <script>
    import firstChild from './first'
    import secondChild from './second'
    export default {
      data () {
        return {}
      },
      components: {
        firstChild,
        secondChild
      },
      mounted () {
        // console.log('详情页面...')
      }
    }
    </script>

    第二个子组件通过中央事件总线bridge,监听事件first.vue发布的事件firstChildMsg,获取到子组件first.vue发送过来的信息,如下图所示:

    点击发送按钮后second.vue获取到first.vue传递过来的值:

    组件间其他通信方式补充(2021-08-13):

    (三)通过vuex

      vuex是vue的一个状态管理插件,可以实现单向数据流,在全局得到一个state对象,设置成功之后,可以在任意的组件中可以通过this.$store.state.name获取存储在store对象中的值,也可以通过vuex的mutation同步、action异步方法修改状态值,修改之后的,在任意组件再通过this.$store.state.name获取到的值就行修改之后的值,实现组件之间的通信(这里只是说明一种通信方式,具体的同步修改状态方法等需要自己去看vuex的文档操作即可)

      运用场景:组件多层嵌套,且存在中途修改传递的值的情况

    (四)通过$attrs和$listeners

      如果只是组件层层嵌套传递参数,不做其他处理这样的,还可以通过vue2.4中提供的$attrs和$listeners来实现,这两方法的作用如下:

      $attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。

      $listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件

      这里我创建了5个vue文件,然后层层嵌套,代码和显示效果如下:

    <template>
      <div>
        <h3>顶层的父组件</h3>
        <p>父组件传入的字段有-->boo:{{boo}},coo:{{coo}},doo:{{doo}},eoo:{{eoo}},foo:{{foo}}</p>
        <child1 :boo="boo" :coo="coo" :doo="doo" :eoo="eoo" :foo="foo" />
      </div>
    </template>
    <script>
    const child1 = () => import('./child1')
    export default {
      components: {
        child1
      },
      data () {
        return {
          boo: 'start',
          coo: '120',
          doo: '130',
          eoo: '140',
          foo: 'end'
        }
      }
    }
    </script>
    <template>
      <div>
        <h3>第一级子元素child1</h3>
        <p>child1通过props属性获取boo的值:{{boo}}</p>
        <p>child1的$attrs的值{{$attrs}}</p>
        <child2 v-bind="$attrs" />
      </div>
    </template>
    <script>
    const child2 = () => import('./child2')
    export default {
      components: {
        child2
      },
      props: {
        boo: {
          type: String,
          default: ''
        }
      },
      inheritAttrs: false,
      created () {
        console.log(this.$attrs)
      }
    }
    </script>
    <template>
      <div>
        <h3>第二级子元素child2</h3>
        <p>child2通过props属性获取doo的值:{{doo}}</p>
        <p>child2中的$attrs的值{{$attrs}}</p>
        <child3 v-bind="$attrs" />
      </div>
    </template>
    <script>
    const child3 = () => import('./child3')
    export default {
      components: {
        child3
      },
      props: {
        doo: {
          type: String,
          default: ''
        }
      },
      inheritAttrs: false,
      created () {
        console.log(this.$attrs)
      }
    }
    </script>
    <template>
      <div>
        <h3>第三级子元素child3</h3>
        <p>child3通过props属性获取doo的值:{{coo}}</p>
        <p>child3中的$attrs的值{{$attrs}}</p>
        <child4 v-bind="$attrs" />
      </div>
    </template>
    <script>
    const child4 = () => import('./child4')
    export default {
      components: {
        child4
      },
      props: {
        coo: {
          type: String,
          default: ''
        }
      },
      inheritAttrs: false,
      created () {
        console.log(this.$attrs)
      }
    }
    </script>
    <template>
      <div>
        <h3>第四级子元素child4</h3>
        <p>eoo的值:{{eoo}}</p>
        <p>child2中的$attrs的值{{$attrs}}</p>
      </div>
    </template>
    <script>
    export default {
      props: {
        eoo: {
          type: String,
          default: ''
        }
      },
      inheritAttrs: false,
      created () {
        console.log(this.$attrs)
      }
    }
    </script>

    各层组件的$attrs结果如下:

    由上面的显示结果可以看出,$attrs是一个对象,可以得到了上一级绑定的所有非prop属性的值,如果下一级还有需要,再接着通过v-bind将$attrs传递下去即可,$listeners的使用待查资料

    (五)通过provide和inject(提供/注入的方式跨级通信)

      运用场景:在子孙组件上通过inject注入,拿到祖父级组件provide提供的变量或对象值,实现跨级通信

      利用上面的代码,我再新建一个vue文件child5.vue,作为第五级子组件,嵌入到child4.vue中,然后在顶层的index.vue中通过provide提供需要传递的值,然后在第五级子组件child5.vue中通过inject获取注入的值,代码如下:

    // 这里是最顶层的index.vue,代码就简写了
    ...
    provide: {
        name: 'provide-inject组件通信方式'
    },
    // 这里是最底层的子组件,位于第五层
    <template>
      <div>
        <h3>第五级子组件child5</h3>
        <p>这里验证provide、inject组件通信模式</p>
        <p>获取越级顶层祖先组件的值:{{name}}</p>
      </div>
    </template>
    <script>
    export default {
      inject: ['name'],
      mounted () {
        console.log(this.name)
      }
    }
    </script>

    展示如下:

      如上所示,我们在新建第五级子组件中,拿到顶层组件index.vue中provide提供的值,实现了跨级通信,但是会有个问题,当我们在父组件中修改提供的name值的时候,发现在第五级子组件的值没有发生变化,就是他们之间不是一个响应式的

      要实现跨级响应式通信,可以通过vue2.6的api Vue.observeable()来进行优化

      下面是我根据大佬的代码写的验证代码和展示效果

    上层的祖级组件:

    provide () {
        this.name = Vue.observable({ // 通过vue2.6新提供的Vue.observable(),实现祖级组件提供的值发生改变,孙祖组件可以立马更新    
        Vue.observable()
          color: 'hi, bob' // 默认传值为'hi, bob',是一串字符串
        })
        return {
          name: this.name
        }
    }

    methods中的改变传入值方法,这里我传入0-10之间的随机数
      changeProvide () {
          this.name.color = Math.floor(Math.random() * 10) + 1
      }
     

    子孙级组件:

    inject: {
        name: {
          default: () => ({})
        }
    }

    进入页面时的原始值:

    点击顶层祖级组件按钮事件之后的值:

    (六)$parent/$childer与ref

    ref:在子组件上使用,可以调用子组件的方法和访问子组件的数据

    $parent/$children:通过$parent访问父组件/通过$children访问子组件实例

    这两种方式都是得到组件实例对象,然后通过实例对象的方式去调用组件中的方法和访问数据

    总结

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

    • 父子通信: 父向子传递数据是通过 props,子向父是通过 events($emit);通过父链 / 子链也可以通信($parent / $children);ref 也可以访问组件实例;provide / inject API;$attrs/$listeners
    • 兄弟通信: Bus;Vuex
    • 跨级通信: Bus;Vuex;provide / inject API、$attrs/$listeners

      这里很感谢原作者大大 浪里行舟,之前的时候只了解vuex、父子组件、事件总线以及父组件通过ref属性调用子组件这几种方式,其他的都没看过了解过,看完文章再手动敲代码测一遍,增加了好些不知道的知识,多谢大佬了(^_^)

  • 相关阅读:
    很难理解的三个设计模式
    设计模式思考(转)
    AOP
    CAP理论(摘)
    DDBS
    NoSql
    Enterprise Library 企业库
    padright padleft
    Process ProcessThread Thread
    053374
  • 原文地址:https://www.cnblogs.com/secretAngel/p/9705809.html
Copyright © 2011-2022 走看看