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属性调用子组件这几种方式,其他的都没看过了解过,看完文章再手动敲代码测一遍,增加了好些不知道的知识,多谢大佬了(^_^)

  • 相关阅读:
    mysql 函数 存储过程 事件(event) job 模板
    protobuf 无proto 解码 decode 语言 java python
    mitmproxy fiddler 抓包 填坑
    android adb 常用命令
    android机器人 模拟 踩坑过程
    RabbitMQ添加新用户并支持远程访问
    Windows下RabbitMQ安装及配置
    Java mybatis mysql 常用数据类型对应关系
    easyExcel 踩坑
    linux防火墙查看状态firewall、iptable
  • 原文地址:https://www.cnblogs.com/secretAngel/p/9705809.html
Copyright © 2011-2022 走看看