前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue非父子组件之间的传值

Vue非父子组件之间的传值

作者头像
砖业洋__
发布2023-05-06 20:02:41
1.6K0
发布2023-05-06 20:02:41
举报
文章被收录于专栏:博客迁移同步

我们可以把一个网页拆分成很多部分,每个部分就是我们代码中的一个组件,左侧整个区域代表方块1,拆分成3个灰色区域代表方块2,左下区域又分成更小的2个深色区域,分别用2个方块3表示,右侧拆分成更小的3个深色区域用3个方块3表示

所以,左侧的网页就可以用右侧的图来表示,一个复杂的网页,最终都可以拆分成小的组件。

右边的图,左上角的红线是表示父子组件传值,父组件通过props向子组件传值,子组件通过$emit触发向父组件传值。

中间的红线表示非父子传值(爷孙也是非父子),当然可以组件1通过props向子组件2传值,组件2通过props向子组件3传值。子组件3通过emit触发向父组件2传值,子组件2通过emit触发向父组件1传值。但是这种传值也很麻烦。

最下面这根红线表示非父子传值,当然你也可以通过和上面一样的方法一层一层的传值,但是代码将会变得无比复杂!

而官方对vue定义是轻量级的视图层框架,当出现了非常复杂的数据传递的时候,光靠着vue是解决不了的!

非父子组件传值一般2种方式:

官方提供的数据层框架vuex

利用发布订阅模式来解决(在vue中称为总线机制)

我们这里讲解第二种

直接来看代码例子

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>非父子组件间的传值(Bus/总线/发布订阅模式/观察者模式)
        </title>
        <script src="https://unpkg.com/vue"></script>
    </head>
    <body>
        <div id="app">
            <child content="lcy"></child><!-- 两个child是兄弟组件,不是父子关系 -->
            <child content="真的帅"></child>
        </div>
        <script> 
            Vue.prototype.bus = new Vue();
            Vue.component('child', {
                props: {
                    content: String
                },
                template: '<div @click="handleClick">{{content}}</div>',
                methods: {
                    handleClick() {
                        this.bus.$emit('change', this.content);
                    }
                },
                mounted() {
                    this.bus.$on('change', function(msg){
                        alert(msg);
                    })
                }
            })
            var vm = new Vue({
                el: "#app",
                data: {

                },
            })
        </script>
    </body>
</html>

运行结果

点击lcy之后弹出两次alert对话框"lcy"

点击真的帅之后弹出两次alert对话框"真的帅"

为什么是两次呢?

每个组件都是vue实例,我们在Vue原型中定义bus属性,这是一个vue实例,相当于全局总线,等同在ES6的class Vue中定义,只要以后new Vue实例或者创建组件的时候,每个组件上都会有bus这个属性,指向同一个Vue实例。

因为每个组件都会去挂载,挂载完之后会执行生命周期方法mounted方法,而在mounted方法里,我们的全局总线bus实例注册了对change事件的监听,所以每个组件都有对change事件的监听,on监听当前实例bus上的自定义事件change。事件可以由vm.emit触发。触发后执行这里的回调函数,回调函数会接收所有传入事件触发函数的额外参数。

子组件child绑定了点击事件,点击后执行handleClick方法,方法this.bus.emit('change', this,content)的执行会触发当前实例bus上监听的事件change,后面的附加参数this.content会传给监听器回调函数。而总线bus是每个组件都有的,所以触发了所有组件上监听的change事件,change事件的回调函数获取参数content,弹出alert框。这里如果点的"lcy",

现在的目标是希望点击其中一个组件的时候,另一个组件跟着改变自己的内容

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>非父子组件间的传值(Bus/总线/发布订阅模式/观察者模式)
        </title>
        <script src="https://unpkg.com/vue"></script>
    </head>
    <body>
        <div id="app">
            <child content="lcy"></child>
            <child content="真的帅"></child>
        </div>
        <script> 
            Vue.prototype.bus = new Vue();
            Vue.component('child', {
                props: {
                    content: String
                },
                data() {
                    return {
                        myContent: this.content
                    }
                },
                template: '<div @click="handleClick">{{myContent}}</div>',
                methods: {
                    handleClick() {
                        this.bus.$emit('change', this.myContent);
                    }
                },
                mounted() {
                    this.bus.$on('change', (msg) => {
                       this.myContent = msg;
                    })
                }
            })
            var vm = new Vue({
                el: "#app",
                data: {

                },
            })
        </script>
    </body>
</html>

运行结果

这里为什么要在data里面加myContent : content呢?我直接改props里面的content不就可以实现效果了吗?

效果是可以实现,但是会报错,如下

每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。所以才会建立一个副本(不是引用相同地址)myContent去解决这个警告。

尽管运行正常,为什么要报这个警告呢?

试想,父组件content传的不是字符串,传的是自定义对象{name : "xxx"},现在在子组件直接修改这个对象this.content.name="aaa",结果就影响了父组件,如果父组件其他地方还引用这个对象就出现了意料之外的结果。

所以需要一个副本(不是指向同一个引用)myContent : content

注意:data{}中定义的对象不会相等!就是上面这个例子。定义数字和字符串因为复用常量池数据,会相等。

官方文档参考见这里单向数据流

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-05-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档