兄弟组件之间的通信同样是在项目中经常会遇到的组件间的通信问题之一, 这种问题的最根本方法就是: 把兄弟组件内部的变量提升到一个中央仓库。
借助父级组件链式交互
使子组件1 通过 $emit 通知父级, 父级再通过响应 子组件1 的事件去触发子组件2的事件,这样的链式操作,在子组件不多的时候,但是一个不错的解决方法
子组件1
<template> <div> <p @click="$emit('fromFirst','来自A组件')">first组件</p> </div> </template> <script> export default { name: 'first' } </script>
子组件2
<template> 子组件2 <div>{{secondInfo}}</div> </template> <script> export default { name: 'second', data() { return { this.secondInfo: null } }, created(){ this.$on('fromFather', (info) => { this.secondInfo = info }) } } </script>
父组件
<template> <first @fromFirst='handleFromFirst' /> <second ref='second' /> </template> <script> import First from './first' import Scond from './second' export default { components: {First, Second}, data() { return { this.secondInfo: null } }, methods:{ handleFromFirst(val) { let second = this.$refs.second second.$emit('fromFather', val) } } } </script>
子组件1 触发父组件的 fromFirst 事件, 在事件中又触发了子组件2的 fromFather事件,并将从子组件1 传递过来的参数传递给了该事件, 当子组件2 执行该事件的时候,将内部的 secondInfo 改变。这就实现了一个兄弟组件的交互。
这个方式在 react 里面同样也是适用的, 但是如果父组件内包含了多个子组件并包含了复杂的逻辑, 有没有更好的方式来解决这种方式呢。
大部分第一个想到的是 vuex, 当然这在一个业务逻辑、数据复杂的项目中是一个很好的解决方法, 但是想象我们要编写一个通用组件,这个组件可能被用到不同的项目中来, 如果使用 vuex 这就要求每一个使用这个组件的项目中都要使用 vuex, 这显然是不好的。
借助中间文件,充当中央仓库
还好 ES6 的模块机制天然就支持建立一个中央仓库, 当 A 文件使用 import value from './b.js' 来引用 B 文件里面的 value 的时候, 这时就会赋值给 A 文件一个 B 文件的 value 的 只读引用, 当 B 文件里面的 value 的值发生变化的时候, A 文件里面的 value 也会跟着改变。
// lib.js export let counter = 3; export function incCounter() { counter++; } // main.js import { counter, incCounter } from './lib'; console.log(counter); // 3 incCounter(); console.log(counter); // 4
定义一个额外的实例进行一个事件的中转,对于ES6 模块的运行机制已经有了一个讲解,当模块内部发生变化的时候,引入模块的部分同样会发生变化,当又一个额外的实例对加载机制进行引入进行emit与emit与emit与on进行绑定通信,能轻而易举解决问题,通过b->a->c的模式直接过渡。
解决 vue 兄弟组件之间的通信我们同样也可以使用中央仓库的方式来实现。
// store.js 作为中央仓库 import Vue from 'vue' export default new Vue()
通过 new 一个 vue 的实例当作兄弟组件交互的中央仓库。
父级组件
<template> <first/> <second/> </template> <script> import First from './first' import Scond from './second' export default { components: {First, Second} } </script>
父组件只是引入子组件, 不再作为中央仓库来过渡交互。
子组件1
<template> <div @click='hanleClick'>子组件1</div> </template> <script> import Store from './store' export default { name: 'first', methods: { handleClick() { Store.$emit('fromFirst', '来自子组件1的传值') } } } </script>
因为我们的目的就是把 Store 作为一个中央仓库,这里我们把 fromFirst 事件添加到了 Store 上面而不是当前组件 this 上。
子组件2
<template> 子组件2 <div>{{secondInfo}}</div> </template> <script> import Store from './store' export default { name: 'second', data() { return { this.secondInfo: null } }, created(){ Store.$on('fromFirst', (info) => { this.secondInfo = info }) } } </script>
子组件2 里面同样也是使用 Store 实例来监听 fromFirst 事件, 因为子组件1和子组件2里面添加事件和监听事件的是同一个实例,根据我们在上文中分析的 ES6 中的情况, 当 Store 添加了 fromFirst 这个时间之后, Store实例的 $on 就可以监听到这个事件并执行回调。
跨组件深层次交互
上面讲的组件之间的关系是这样的:
我们可以实现 子组件之间的交互, 但是如果我们遇到这种情况呢?
孙组件需要跟子组件3 进行交互,还是使用上述的方法可以做到吗? 答案是肯定的,只要能够使用同一个中央仓库,那么不管什么层级的组件复杂度,都是可以实现两者的交互的。
补充知识:Vue组件跨层级通信
正常组件间通信
父->子组件 是通过属性传递
子->父组件 是通过this.$emit()传递
this.$emit()返回的是this,如果需要一些值 可使用callback方式传递
provide 和 inject
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,
不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
provide 和 inject 绑定并不是可响应的。这是刻意为之的。
然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
provide提供数据,多层子组件 向上层寻找,只要找到 就不在向上层寻找了.
inject 向子组件注入数据
使用方式
第一种方式(传递对象,使用字符串数组接收)
// 父级组件提供 'foo' var Provider = { provide: { foo: 'bar' }, // ... } // 子组件注入 'foo' var Child = { inject: ['foo'], created () { console.log(this.foo) // => "bar" } // ... }
第二种方式(传递返回对象的函数, 使用对象接收)
provide() { return { // 2.6.0 版本之前 通常传递this. 但这样的话 会传递很多用不到的属性 theme: { color: 'xxx' //如果传入可响应的数据,这里的属性还是可响应的 } }; } inject: { //这里可以换成其它名字 theme: { from: "theme", // 数据来源 default: () => ({}) //降级情况下使用的 value //可以是 普通值 //可以是 对非原始值使用一个工厂方法 } } //正常子组件 this.theme //即可访问 //子组件是函数式组件的使用方式 injections.theme.color
Vue.observable( object )
让一个对象可响应。Vue 内部会用它来处理 data 函数返回的对象。
可以作为最小化的跨组件状态存储器,用于简单的场景
提供数据可改为
provide() { //这时提供的theme 则为可响应的数据 this.theme = Vue.observable({ color: "blue" }); return { theme: this.theme }; },
以上这篇vue 解决兄弟组件、跨组件深层次的通信操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。
vue 解决兄弟组件、跨组件深层次的通信操作
- Author -
夏天丫丫声明:登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。
Reply on: @reply_date@
@reply_contents@