Vue组件化, 组件间通信方式
1.0 父子组件之间通信
1.1 父传子
1.1.1 props
方式传参.
props 通信的方式是 父级组件和子级组件之间通信的一种方式, 具体使用方法如下
- 子组件接收数据时的方式有 props: [’’] 数组的方式**
- 或者通过 props:{} 对象的方式接收数据
🏴 注意: 子组件中的函数或方法如果要操作父组件的数据的时候,通常是在父组件定义一个方法, 把这个方法通过标签属性props传给子组件, 在子组件中调用该方法把参数传递过去,完成子组件对父组件数据的操作
案例代码如下:
Parent 父组件:
<template> <div> <h1 style="color: pink">我是parent组件, 下面是Son组件</h1> <Son msg="我是props 方式给子组件传参"/> </div> </template> <script> import Son from './Son' export default { name: "Parent", components: { Son, }, methods: { }, } </script> <style scoped> </style>
children子组件:
<template> <div> <h3>我是son组件: {{msg}}</h3> </div> </template> <script> export default { name: "Son", props: ['msg'], mounted() { console.log(this.msg); } } </script> <style scoped> </style>
1.1.2 自定义函数传参
自定义函数的方式, 可以实现父组件的函数在子组件中触发, 这种方式可以在子组件中, 再调用函数的是候, 给父组件传递数据
// 1. 在 Vue中也可以通过自定义函数的方式和子组件之间通信, 一般会在自定义函数的函数名字之前添加一个 @ 符号, 写法如下. <!-- 父组件自定义一个函数, 通过标签属性传递给子组件 --> <Son @selfDefineEvent="selfDefineEvent"/> // 2. 这种方式, 在子组件中, 可以直接通过 this.$emit('自定义函数名', 参数) 方法分发事件. (相当于触发传递过来的事件) /** * 自定义事件通信方式的方式 * 1. 在父组件中定义一个事件函数, 通过标签属性的方式把该自定义事件传给子组件 * 2. 在子组件中直接通过 this.$emit() 来触发事件. * 3. 其本质是在子组件调用该自定义事件的时候, 通过传递参数的方式, 把数据传递给父组件 */
案例代码如下:
Parent父组件:
<template> <div> <h1 style="color: pink">我是parent组件, 下面是Son组件</h1> <!-- 父组件自定义一个函数 --> <Son @selfDefineEvent="selfDefineEvent"/> </div> </template> <script> import Son from './Son' export default { name: "Parent", components: { Son, }, methods: { selfDefineEvent(...params) { console.log(...params); } }, } </script> <style scoped> </style>
Son 子组件代码:
<template> <div> <h3>我是son组件</h3> <button @click="handleClick">Son 组件内点击触发父组件 自定义函数</button> </div> </template> <script> export default { name: "Son", methods: { handleClick() { // 在子组件内, 直接通过 $emit 触发父组件自定义的函数 this.$emit('selfDefineEvent', '我是自定义函数参数',123,'mm') } }, } </script> <style scoped> </style>
原理:
1. // 在vue中, 每一个组件, 包括 App 组件, 如果你在组件的声明周期函数中 打印 console.log(this), 会打印出是一个 VueComponent{} 对象, 代表当前的组件实例对象 2. // 而 在 vue 中, 所有的组件对象, 包括 App 等其他一些子组件的实例 都是 Vue 这个组件的实例对象的子对象, 他们之间存在继承的关系. 3. // 在 vue中, 通过 @事件名, 自定义的事件, 都会放在 Vue 这个组件的 prototype 中, 所以 Vue组件的实例 vm对象及其所有 子组件实例对象,都可以找到 这个自定义事件. 所以就可以在它的子组件中通过 原型链就可以找到 Vue 组件对象的 $emit 这个方法, 完成通信. this.$emit('自定义事件名字', 参数);
1.2 子传父
vue 组件之间通信之 - 子组件传递数据给父组件.
即: 父组件访问子组件的数据.
1.2.1 ref
实现: 父组件访问子组件数据
通过 ref 属性给子组件做个标识, 可以获取到子组件, 进而可以获取到子组件
data
中的数据, 也可以获取到子组件当中的相关方法.代码示例如下:
Parent组件
<template> <div> <h1 style="color: pink">我是parent组件, 下面是Son组件</h1> <Son ref="sonRef" msg="我是父组件传递给子组件的msg属性..."/> </div> </template> <script> import Son from './Son' export default { name: "Parent", components: { Son }, mounted() { console.log('父组件想要获取子组件数据---->> ',this.$refs.sonRef.sonData); // 获取子组件 data内的数据 console.log('父组件想要获取子组件数据---->> ',this.$refs.sonRef.msg); // 获取子组件 pros 内的数据 this.$refs.sonRef.sayHi() // 调用子组件的方法 } } </script> <style scoped> </style>
Son 组件
<template> <h3>我是son组件</h3> </template> <script> export default { name: "Son", props:['msg'], data() { return { sonData: '我是子组件的数据' } }, methods: { sayHi() { console.log('%c hello parents, I am your son ...', 'color:pink'); } } } </script> <style scoped> </style>
1.2.2 $children
实现: 父组件访问子组件数据
- 类型: Array<Vue instance> - 只读 - 详细: 当前实例的 "直接" 子组件. 需要注意的是, `$children`"并不保证顺序, 也不是响应式的.",如果你发现自己正在尝试使用 `$children` 来进行数据绑定, 考虑使用一个数组配合 `v-for` 来生成子组件, 并且使用 Array 作为真正的来源.
通过
$children
的方式获取到当前组件的所有子组件, 用索引获取指定子组件, 然后获取到对应子组件的props
,data
以及method
方法等但是: 使用
$children
的这种方式,vue
中不一定是按照顺序的,vue
中通过$children
获取到的子组件是不保证这里取到子组件的顺序的.
Parent组件代码如下
<template> <div> <h1 style="color: pink">我是parent组件, 下面是Son组件</h1> <Son ref="sonRef" msg="我是父组件传递给子组件的msg属性..."/> </div> </template> <script> import Son from './Son' export default { name: "Parent", components: { Son }, mounted() { console.log(this.$children[0].msg); // 访问子组件 props 属性 console.log(this.$children[0].sonData); // 访问子组件 data this.$children[0].sayHi() // 访问子组件 method 方法 } } </script> <style scoped> </style><template> <div> <h1 style="color: pink">我是parent组件, 下面是Son组件</h1> <Son ref="sonRef" msg="我是父组件传递给子组件的msg属性..."/> </div> </template> <script> import Son from './Son' export default { name: "Parent", components: { Son }, mounted() { console.log(this.$children[0].msg); // 访问子组件 props 属性 console.log(this.$children[0].sonData); // 访问子组件 data this.$children[0].sayHi() // 访问子组件 method 方法 } } </script> <style scoped> </style>
子组件代码同上面子组件的代码
1.2.3 $emit
事件派发与监听
注意: 事件派发与监听, 虽然能在父组件中获取到子组件派发事件所传递的数据, 但是实际上, 还是在 子组件上监听的事件. 即:
谁派发事件, 谁来监听事件
.Parent 组件如下
<template> <div> <h1 style="color: pink">我是parent组件, 下面是Son组件</h1> <!-- 监听子组件派发的事件 --> <Son @eventEmit="handleEventEmit" ref="sonRef" msg="我是父组件传递给子组件的msg属性..."/> </div> </template> <script> import Son from './Son' export default { name: "Parent", components: { Son }, mounted() { }, methods: { handleEventEmit(params) { console.log('父组件监听到子组件派发的数据 -->', params); } } } </script> <style scoped> </style>
Children组件如下
<template> <div> <!-- 子组件绑定派发事件 --> <h3 @click="$emit('eventEmit','我是事件派发想要传递的数据123.')">我是son组件</h3> </div> </template> <script> export default { name: "Son", props: ['msg'], data() { return { sonData: '我是子组件data 的数据...' } }, methods: { sayHi() { console.log('%c hello parents, I am your son ...', 'color:pink'); } } } </script> <style scoped> </style>
2.0 兄弟组件之间的通信
2.1 $emit
和 $on
, 通过共同父组件实现兄弟组件通信
借助共有父组件的方式来实现.
用事件的派发与监听, 通过共同父组件来派发和监听事件, 实现兄弟组件之间通信
下面案例实现
Son
组件与Son1
组件间通信, 共同父组件Parent
Parent 组件如下
<template> <div> <h1 style="color: pink">我是parent组件, 下面是Son组件</h1> <!-- 监听子组件派发的事件 --> <Son @eventEmit="handleEventEmit" ref="sonRef" msg="我是父组件传递给子组件的msg属性..."/> <Son1/> </div> </template> <script> import Son from './Son' import Son1 from './Son1' export default { name: "Parent", components: { Son, Son1 }, mounted() { }, methods: { handleEventEmit(params) { console.log('父组件监听到子组件派发的数据 -->', params); } } } </script> <style scoped> </style>
Son组件
<template> <div> <!-- 子组件绑定派发事件 --> <h3 @click="$emit('eventEmit','我是事件派发想要传递的数据123.')">我是son组件</h3> <button @click="sendMsg">给兄弟Son1组件打招呼</button> </div> </template> <script> export default { name: "Son", props: ['msg'], data() { return { sonData: '我是子组件data 的数据...' } }, methods: { sendMsg(){ /* 通过中间组件, 共同父组件来派发事件 */ this.$parent.$emit('sayHiToSiblings','大哥你好,我是你兄弟...') } } } </script> <style scoped> </style>
Son1组件
<template> <div> <h2>我是Son1组件</h2> </div> </template> <script> export default { name: "Son1", mounted() { /* 通过中间组件 共同父组件 监听事件 */ this.$parent.$on('sayHiToSiblings',(msg)=>{ console.log('%c我是Son1组件, 我接收Son兄弟组件的数据--->','color:pink',msg); }) } } </script> <style scoped> </style>
2.2 $bus
事件总线的方式实现兄弟组件通信
其原理就是通过借助第三方组件的方式实现通信的.
如果两个组件之间没有共同的直接父组件, 我们还可以通过事件总线的方式, 实现跨组件之间的数据通信.
事件总线的方式可以完成任意组件之间的通信.
事件总线通信方式的使用:
1. // 在 main.js 中, 手动在 Vue 的原型上添加一个事件总线 Vue.prototype.$bus = new Vue() // 通过事件总线的 $on 方法,绑定一个方法 // 第一个参数: 事件名字 // 第二个参数: 需要处理的响应函数 this.$bus.$on('toggleTodo',(todo) => { this.toggleTodo(todo) }) 2. // 在其他需要该方法的组件里通过 $emit() 触发该事件, 传入参数 // 第一个参数: 对应事件名字 // 第二个参数: 传给回调函数的 实参 this.$bus.$emit('toggleTodo',this.todoItem)
案例代码如下:
main.js
import Vue from "vue"; import App from "./App.vue"; Vue.config.productionTip = false; // 创建一个第三方实例挂到Vue原型, 用来实现事件总线通信 Vue.prototype.$bus = new Vue(); new Vue({ render: h => h(App) }).$mount("#app");
Son组件:
<template> <div> <h3>我是son组件</h3> <p>{{msg}}</p> </div> </template> <script> export default { name: "Son", data() { return { msg: '' } }, mounted() { /* 在组件挂载完的时候完成监听事件 */ this.$bus.$on('siblingContact', (msg) => { console.log(msg); this.msg = msg }) } } </script> <style scoped> </style>
Son1 组件
<template> <div> <h2>我是Son1组件</h2> <button @click="handleClick">Son1组件点击, 触发Son组件传递过来的方法 </button> </div> </template> <script> export default { name: "Son1", methods:{ handleClick(){ // 触发点击事件的时候, 完成分发事件 this.$bus.$emit('siblingContact','Hi,我是Son1, 我是你的兄弟啊...') } } } </script> <style scoped> </style>
2.3 通过 PubSub.js 实现任意组件间通信
通过第三方的 Js库, 可以实现任意组件之间的数据通信.
PubSub的使用
消息的 订阅与发布 就好比 事件的绑定和 触发事件
**特点:**PubSub 消息订阅与发布这种方式实现通信可以实现任意组件之间的通信.
1. 安装pubsub-js npm install pubsub-js 2. 在需要 消息订阅与发布的文件中, 首先引入 pubsub-js import PubSub from 'pubsub-js' // 注意 pubsub引入的时候, 完整的是 pubsub-js // 订阅消息, 传入一个 事件名字, 一个回调函数. 订阅事件就相当于定义的绑定事件 PubSub.subscribe('MY TOPIC', ()=>{}); // 发布消息, 传入一个与订阅消息相同的 事件名字, 和 数据 // 发布消息相当于 触发函数, (给定义的函数传递过去参数数据) PubSub.publish('MY TOPIC', 'hello world!');
对 pubsub 消息订阅机制的理解
#3. 对 pubsub 事件订阅与发布的理解 // 事件的订阅与发布可以类比传统的 给元素绑定事件 和 添加响应函数来理解 /** * 1. 在传统的 添加绑定事件时有两步 * >1. 给元素添加绑定事件函数, 函数传入参数. * >2. 给绑定的事件添加响应函数. (注意: 有函数) * * 2. 在消息的订阅与发布中 * >1. 发布消息(传入参数) ----> 相当于绑定事件 * >2. 订阅消息('',fn) ----> 相当于添加响应函数 * 因为订阅消息中需要两个参数, 第二个参数为回调函数, * 回调函数 就相当于我们所说的 事件的响应函数,也就是具体处理逻辑的函数 */
3.0 祖孙组件或嵌套组件之间通信
3.1 provide/inject
实现嵌套组件通信
注意: 通过
provide
和inject
方式实现的嵌套组件之间传递的数据, 不是响应式的.
类型:
provide:
Object | ()=> Object
inject:
Array<string> | {[key:string]:string | symbol | Object}
详细
provide 和 inject 主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。
provide
选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的 property。在该对象中你可以使用 ES2015 Symbols 作为 key,但是只在原生支持Symbol
和Reflect.ownKeys
的环境下可工作。
inject
选项应该是:
- 一个字符串数组,或
- 一个对象,对象的 key 是本地的绑定名,value 是:
- 在可用的注入内容中搜索用的 key (字符串或 Symbol),或
- 一个对象,该对象的:
from
property 是在可用的注入内容中搜索用的 key (字符串或 Symbol)default
property 是降级情况下使用的 value
案例代码如下:
Parent组件:
<template> <div> <h1 style="color: pink">我是parent组件, 下面是Son组件</h1> <button @click="handleClick">点击修改注入的数据</button> <Son/> </div> </template> <script> import Son from './Son' export default { name: "Parent", data() { return { msg: '我是给后代组件注入的数据' } }, provide() { return { msg: this.msg } }, components: { Son, }, methods: { handleClick() { console.log('修改前 的祖先组件 msg数据 --->> ', this.msg); this.msg = '我修改了祖先组件想要注入给后代组件的数据...' console.log('修改后的祖先组件 msg数据 --->> ', this.msg); } }, mounted() { }, } </script> <style scoped> </style>
Son组件:
<template> <div> <h3>我是son组件</h3> <GrandSon/> </div> </template> <script> import GrandSon from './GrandSon' export default { name: "Son", props: ['msg'], components: {GrandSon}, data() { return { sonData: '我是子组件data 的数据...' } }, methods: {} } </script> <style scoped> </style>
GrandSon组件:
<template> <div> <h2>我是GrandSon组件</h2> <p>{{msg}}</p> </div> </template> <script> export default { name: "Son1", inject:['msg'], mounted() { /* 通过中间组件 共同父组件 监听事件 */ this.$parent.$on('sayHiToSiblings', (msg) => { // 由此可见,祖先组件点击按钮, 修改数据后, 此处数据并没有修改, 所以数据是非响应式的 console.log('%c我是Son1组件, 我接收Son兄弟组件的数据--->', 'color:pink', msg); }) } } </script> <style scoped> </style>
4.0 插槽的方式实现通信
插槽常用语内容分发. 原理就是占位符的一个运用
插槽通信的方式后续会详细介绍.
4.1 普通插槽
4.2 具名插槽
4.3 作用域插槽
总结:
交流学习添加微信(备注技术交流学习):
Gene199302