前置

大小 vue 项目都离不开组件通讯, 在这里总结一下vue组件通讯方式并列出, 都是简单的例子. 适合像我这样的小白。如有错误,欢迎指正。

温馨提示: 下文没有列出 vuex, vuex 也是重要的组件通讯方式。

props

* 最常用的组件通讯方式
* 值可以是数组或对象,使用对象时可以配置高级选项,如类型检测、自定义验证和设置默认值
* 方向:父 -> 子
Son.vue
export default { props: { text: { type: String, required: true, }, },
mounted() { console.log(this.text) // 我是父组件提供给子组件的值 }, }
App.vue
<template> <Son text='我是父组件提供给子组件的值'/> </template> <script> import Son from
'./components/dispatch/Son' export default { name: 'app', components: { Son, }
}, </script>
$refs

* 常用的方式
* 返回注册过 ref 特性的所有 DOM 元素和组件实例
* 可以用来操作 DOM
* 可以用来传值
* 方向:子 -> 父
Son.vue
export default { methods: { sonFunc() { console.log('我是子组件的值') }, }, }
App.vue
<template> <Son ref="sonref"/> </template> <script> import Son from
'./components/dispatch/Son' export default { name: 'app', components: { Son, },
mounted() { this.$refs.sonref.sonFunc() }, } </script>
控制台打印: 我是子组件的值

$emit

* $emit 用来触发当前实例上的事件
* 方向:父 -> 子
* 参数一:来触发的当前实例上的事件函数
* 参数二:附加参数,传给监听器回调
Son.vue
export default { mounted() { this.$emit('customFunc', '我是子组件传给父组件的值') }, }
App.vue
<template> <Son v-on:customFunc="fatherFunc" /> </template> <script> import
Son from './components/dispatch/Son' export default { name: 'app', components:
{ Son, }, methods: { fatherFunc(value) { console.log(value) // 我是子组件传给父组件的值 },
}, } </script>
@update

* 需要配合 .sync 使用
* 与上面的 $emit 写法类似
* 不同之处在于$emit 的第一个参数不在是当前实例上的事件函数
* 方向:子 -> 父
Son.vue
export default { mounted() { this.$emit("update:text", '我是子组件传给父组件的值') } }
App.vue
<template> <Son :text.sync='text'/> </template> <script> import Son from
"./components/dispatch/Son" export default { data() { return { text: '' } },
mounted() { console.log(this.text); // 我是子组件传给父组件的值 } } </script>
接下来看下面的写法,上面这种写法是对如下方式的简写, 或者称之为语法糖。可以不借助 .sync。

Son.vue
export default { mounted () { this.$emit('update:text','我是子组件传给父组件的值') } }
App.vue
<Son @update:text="v => (this.value = v)" /> import Son from
"./components/dispatch/Son" export default { mounted() {
console.log(this.value) // 我是子组件传给父组件的值 } }
v-model

* v-model 常用来给 input 实现双向数据绑定
* v-model 也可以用来传值
* 有局限性,只能传 input value <input v-model="text">
等价于:
<input v-bind:value="text" v-on:input="text = $event.target.value" >
接下来看如何通过 v-model 传值。

Son.vue
<template> <input v-bind:value="value" v-on:input="$emit('input',
$event.target.text)" /> </template> <script> export default { data() { return {
value: '我是子组件传给父组件的值', } } } </script>
App.vue
<template> <Son v-model="text" /> </template> <script> import Son from
'./components/dispatch/Son' export default { name: 'app', components: { Son, }
} </script>


$parent $childred

* $parent: 父实例,如果当前实例有的话
* $children: 当前实例的直接子组件
* $parent $childred 通过封装可以实现不同方向的传值
$children 并不保证顺序,也不是响应式的。可以使用一个数组配合 v-for 来生成子组件,使用 Array 作为真正的来源。

App.vue
export default { data() { return { value: '我是父组件的值', } },
Son.vue
export default { mounted: { console.log(this.$parent.value) // 我是父组件的值
this.$parent.value = 666 console.log(this.$parent.value) // 666 }, }
简单封装一下即可实现$parent 配合 $emit 实现跨级向上传值。

main.js
Vue.prototype.$dispatch = function(event, value) { let parent = this.$parent
while (parent) { parent.$emit(event, value) parent = parent.$parent } }
这样使用: this.$dispatch('event',value)

简单封装一下即可实现$children 配合 $emit 实现向下传值。
Vue.prototype.$broadcast = function(event, value) { const broadcast = children
=> { children.forEach(child => { child.$emit(event, value) if (child.$children)
{ broadcast(child.$children) } }) } broadcast(this.$children) }
这样使用: this.$broadcast('event',value)

$attrs

* 获取父组件通过 v-bind 传过去的所有值
* class 和 style 除外
* 可以通过 v-bind="$attrs" 传入内部组件
* 只能在 <template> 中使用
* 方向:子 -> 父
App.vue
<template> <Son :value1="123" :value2="456" /> </template> import Son from
'./components/dispatch/Son' export default { name: 'app', components: { Son, },
}
Son.vue
<template> <div>{{$attrs}}</div> </template> <script> export default {
inheritAttrs: false, } </script>


$listener

* 获取父作用域中的 () v-on 事件监听器。
* 不含 .native 修饰器修饰的时间监听器。
* 可以通过 v-on="$listeners" 传入内部组件(孙子组件)。
* 方向:父 -> 子
App.vue
<template> <Son @customFunc="fatherFunc"/> </template> <script> import Son
from './components/dispatch/Son' export default { name: 'app', components: {
Son, }, methods: { fatherFunc() { console.log('666') }, }, } </script>
Son.vue
<template> <button @click="$listeners.customFunc()">看</button> </template>
provide inject

* provide 和 inject 不推荐直接用于应用程序代码中
* 与 React
的上下文特性很相似。这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效
* provide 选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的属性。在该对象中你可以使用 ES2015 Symbols 作为
key,但是只在原生支持 Symbol 和 Reflect.ownKeys 的环境下可工作
* provide 和 inject 绑定并不是可响应的。这是 vue 刻意为之
* 如果你传入了一个可监听的对象,那么其对象的属性还是可响应的
这里有一个简单的示例:

App.vue
<template> <Son /> </template> <script> import Son from
'./components/dispatch/Son' export default { name: 'app', components: { Son, },
provide() { return { text: '我是父组件的值', } }, } </script>
Son.vue
export default { inject: ['text'], mounted() { console.log(this.text) //
我是父组件的值 }, }
事件总线

* EventBus 又称为事件总线
* 不是一个具体的 API,EventBus 代表一种思路
* 可以看作 vuex 的究极压缩版
App.vue
<template> <div> <Son /> </div> </template> <script> import Son from
'./components/dispatch/Son' export default { name: 'app', components: { Son, },
mounted() { this.$EventBus.$emit('event', 'app.vue') }, } </script>
Son.vue
export default { mounted() { this.$EventBus.$on('event', function(v) {
console.log(v) }) }, }
Observable

* observable 可以让一个对象可响应
* vue 内部会用它来处理 data 函数返回的对象
* 返回的对象可以直接用于渲染函数和计算属性内,并且会在发生改变时触发相应的更新
* 可以作为最小化的跨组件状态存储器,用于简单的场景
store.js
import Vue from 'vue' export const store = Vue.observable({ text: '我是store里的'
}) export const mutations = { setText(text) { store.text = text }, }
App.vue
import { store, mutations } from '../store' export default { mounted() {
console.log(store.text) //我是store里的 mutations.setText('我在App.vue中将你改变')
console.log(store.text) //我在App.vue将你改变 }, }
composition-api

* composition-api 包含 vue3 的新特性
* provide 和 inject 可以实现嵌套组件之间的数据传递
* 这两个函数只能在 setup 函数中使用
* 父级组件中使用 provide 函数向下传递数据
* 子级组件中使用 inject 获取上层传递过来的数据
* 不限层级。
App.vue
<template> <provideAndInject /> </template> <script> import { provide } from
"@vue/composition-api" import provideAndInject from
"./components/provideAndInject" export default { name: "app", components: {
provideAndInject }, setup() { // provide('数据名称', 要传递的数据) provide("customVal",
"我是父组件向子组件传递的值"); } }; </script>
Son.vue
<template> <h3>{{ customVal }}</h3> </template> <script> import { inject }
from "@vue/composition-api"; export default { setup() { //调用 inject
函数,通过指定的数据名称,获取到父级共享的数据 const customVal = inject("customVal"); return {
customVal }; } }; </script>
父组件可以通过 ref 创建响应式数据通过 provide 共享给子组件。