该应用场景多用于父子组件之间的通讯。
单向数据流html
全部的 prop 都使得其父子 prop 之间造成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,可是反过来则不行。这样会防止从子组件意外变动父级组件的状态,从而致使你的应用的数据流向难以理解。vue
额外的,每次父级组件发生变动时,子组件中全部的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。若是你这样作了,Vue 会在浏览器的控制台中发出警告。es6
v-bind
(缩写::
)的方式,将数据传递给该子组件props
来接收父组件传递的数据this.$emit
触发当前实例上的事件,附加参数都会传给监听器回调v-on
(缩写:@
)的方式,监听 this.$emit
触发的事件,在 methods
中定义监听器回调方法来获取数据<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>场景一:父子组件通讯</title> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <!-- 父组件挂载到 #app 元素下 --> <m-self></m-self> </div> </body> <script> // 声明子组件 Vue.component('m-son', { // 子组件接收父组件经过绑定属性(v-bind/:)绑定的属性 // 注意⚠️:通常 props 接收的数据做为初始化数据 props: { b: { type: String, default: '' } }, // :value="b",而不用 v-model="b" 是由于 vue 中 prop 的使用,形成其父子 prop 之间造成了一个单向下行绑定,父级 prop 的更新会向下流动到子组件中,可是反过来则不行。这样会防止从子组件意外变动父级组件的状态,从而致使你的应用的数据流向难以理解。 template: `<div> <input type="text" :value="b" @input="onInput" /> </div>`, methods: { onInput(e) { let data = e.target.value // 子组件中更改了数据,经过 $emit 触发当前实例上的事件。附加参数都会传给监听器回调。 this.$emit('onChangeB', data) } } }) // 声明父组件,并将父组件挂载到 #app 上 let mSelf = new Vue({ el: '#app', data() { return { a: '将要传递给子组件的数据' } }, // 父组件经过 v-bind(/:) 的方式,传递数据到子组件 // 经过 v-on(/@) 的方式,监听子组件中 $emit 触发的事件 template: `<div> <p>{{a}}</p> <m-son :b="a" @onChangeB="onChange"></m-son> </div>`, methods: { onChange(data) { this.a = data } } }) </script> </html>
$parent
子访问/绑定父 和 $children
父访问/绑定子$parent
,父实例,若是当前实例有的话。
$parent
,当前实例的直接子组件。 须要注意$children
并不保证顺序,也不是响应式的。若是你发现本身正在尝试使用$children
来进行数据绑定,考虑使用一个数组配合v-for
来生成子组件,而且使用 Array 做为真正的来源。
节制地使用 $parent
和 $children
- 它们的主要目的是做为访问组件的应急方法。更推荐用 props 和 events 实现父子组件通讯。vuex
$parent
子访问/绑定父this.$parent
获取父组件的实例(若是有父组件的话),实例上面固然能够获取到其挂载的数据v-model
绑定父组件的数据,利用数据响应式完成子传父$children
父访问/绑定子this.$parent
拿到父组件的数据,并赋值给子组件,完成数据初始化v-model
绑定子组件中的数据mounted
挂载后,经过 this.$children
获取到子组件实例数组,将某一子组件实例赋值给父组件的某个变量,利用对象的特性,实现数据响应式。$parent
子访问/绑定父<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>场景二:父子组件通讯</title> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <!-- 父组件挂载到 #app 元素下 --> <m-self></m-self> </div> </body> <script> // 声明子组件 Vue.component('m-son', { // 子组件中,经过 this.$parent 访问父组件实例 template: `<div> <input type="text" v-model="$parent.a" /> </div>` }) // 声明父组件,并将父组件挂载到 #app 上 let mSelf = new Vue({ el: '#app', data() { return { a: '将要传递给子组件的数据' } }, template: `<div> <p>{{a}}</p> <m-son></m-son> </div>` }) </script> </html>
$children
父访问/绑定子<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>场景二:父子组件通讯</title> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <!-- 父组件挂载到 #app 元素下 --> <m-self></m-self> </div> </body> <script> // 声明子组件 Vue.component('m-son', { data() { return { // 子组件中,经过 this.$parent 获取父组件实例,完成数据初始化 b: this.$parent.a } }, template: `<div> <input type="text" v-model="b" /> </div>` }) // 声明父组件,并将父组件挂载到 #app 上 let mSelf = new Vue({ el: '#app', data() { return { a: '将要传递给子组件的数据', children: null } }, template: `<div> <p>{{children && children.b}}</p> <m-son></m-son> </div>`, mounted() { // 实例挂载后,将子组件实例赋值给父组件中定义的数据 // 注意⚠️:$children 在挂载后才会取到值,并且不是响应式的。为了达到响应式的效果,这里采用从新赋值,利用了对象的特性 this.children = this.$children && this.$children[0] } }) </script> </html>
$root
请参考 $parent
$root
当前组件树的根 Vue 实例。若是当前实例没有父实例,此实例将会是其本身。
v-bind="$attrs"
祖传孙和 v-on="$listeners"
孙传子$attr
包含了父做用域中不做为 prop 被识别 (且获取) 的 attribute 绑定 (class
和style
除外)。当一个组件没有声明任何 prop 时,这里会包含全部父做用域的绑定 (class
和style
除外),而且能够经过v-bind="$attrs"
传入内部组件——在建立高级别的组件时很是有用。
$listeners
包含了父做用域中的 (不含.native
修饰器的)v-on
事件监听器。它能够经过v-on="$listeners"
传入内部组件——在建立更高层次的组件时很是有用。
该应用场景可用于更高层次组件之间的通讯。
v-bind="$attrs"
和 v-bind="$attrs"
的写法,不能采用简写。npm
v-bind
(简写::
) 的方式来传递数据$attrs
,即 v-bind="$attrs"
,获取父级的绑定属性$attrs
去获取祖组件传递的数据v-on
(简写:@) 的方式来接收孙组件回调数据$listeners
即 v-on="$listeners"
,获取父级的事件监听器this.$listeners
获取祖组件的事件监听器,在数据改变后,执行该监听器传递数据。或者采用 $emit
去触发<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>场景三:父子孙子等更高级别组件通讯</title> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <!-- 父组件挂载到 #app 元素下 --> <m-self></m-self> </div> </body> <script> // 声明曾孙组件 Vue.component('m-great-grandson', { // 孙组件经过 $attrs 获取祖组件传递的数据 template: `<div> <input type="text" :value="$attrs.b" @input="onInput" /> </div>`, methods: { onInput(e) { let data = e.target.value // 孙组件中更改了数据,经过 $emit 触发当前实例上的事件。附加参数都会传给监听器回调。 // this.$emit('onChangeB', data) // 或者经过 $listeners 获取事件监听器,执行监听器 this.$listeners.onChangeB(data) } } }) // 声明孙组件 Vue.component('m-grandson', { // 每层子组件都绑定 $attrs (v-bind="$attrs"),将数据传给子组件 // 每层子组件都绑定 $listeners (v-on="$listeners"),将事件监听器传给子组件 template: `<div> <m-great-grandson v-bind="$attrs" v-on="$listeners"></m-great-grandson> </div>` }) // 声明子组件 Vue.component('m-son', { // 每层子组件都绑定 $attrs (v-bind="$attrs"),将数据传给子组件 // 每层子组件都绑定 $listeners (v-on="$listeners"),将事件监听器传给子组件 template: `<div> <m-grandson v-bind="$attrs" v-on="$listeners"></m-grandson> </div>` }) // 声明父组件,并将父组件挂载到 #app 上 let mSelf = new Vue({ el: '#app', data() { return { a: '将要传递给曾孙组件的数据' } }, // 父组件内,经过 v-bind(/:) 绑定属性 (:b="a")。每层子组件都绑定 $attrs (v-bind="$attrs"),实现数据的祖传孙 // 父组件内,经过 v-on(/@) 绑定事件 (@onChangeB="onChange")。每层子组件都绑定 $listeners (v-on="$listeners"),实现事件监听器的祖传孙 template: `<div> <p>{{a}}</p> <m-son :b="a" @onChangeB="onChange"></m-son> </div>`, methods: { onChange(data) { this.a = data } } }) </script> </html>
经过一个公共的区域,来管理数据。可用于非父子组件通讯。
let bus = new Vue()
bus
,同时开启事件监听器。触发事件后,回调处理参数bus
数据赋值给 b 组件,初始化数据。在数据改变后经过 bus.$emit
触发事件监听器,回调数据,完成数据传递<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>场景四:非父子组件通讯</title> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <!-- 父组件挂载到 #app 元素下 --> <m-self></m-self> </div> </body> <script> // 中央事件管理 let bus = new Vue() // 声明子组件 a Vue.component('m-son-a', { data() { return { a: '传给组件 b 数据' } }, template: `<div> <p>{{a}}</p> </div>`, created() { // 将组件 a 数据赋值给 bus,并开启事件监听器。触发后,回调数据即为 组件 b 传递 bus.a = this.a bus.$on('onChangeA', (data) => { this.a = data }) }, methods: { } }) // 声明子组件 b Vue.component('m-son-b', { data() { return { b: '' } }, template: `<div> <input type="text" :value="b" @input="onInput" /> </div>`, created() { // 数据初始化 this.b = bus.a }, methods: { onInput(e) { let data = e.target.value // 组件 b 中更改了数据,经过 $emit 触发自定义事件。附加参数都会传给监听器回调。 bus.$emit('onChangeA', data) } } }) // 声明父组件,并将父组件挂载到 #app 上 let mSelf = new Vue({ el: '#app', template: `<div> <m-son-a></m-son-a> <m-son-b></m-son-b> </div>` }) </script> </html>
共用同一对象,可能会引发一些数据的混乱。不理解的状况下会形成不是预期的变化。数组
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>场景四:父子/非父子组件通讯</title> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <!-- 父组件挂载到 #app 元素下 --> <m-self></m-self> </div> </body> <script> // 声明一个公用对象,利用对象的特性完成数据共享。也能够经过 es6 模块 导出导入对象。index.js 中 export const data = { a: ''} ,import { data } from './index.js' // 将对象赋值给某个变量,这个变量修改了对象中的属性,则原对象也会改变(对象是地址引用,引用类型) let data = { a: '传给组件 b 数据' } // 声明子组件 a Vue.component('m-son-a', { data() { return { data: {} } }, template: `<div> <p>{{data && data.a}}</p> </div>`, created() { // 初始化数据 this.data = data } }) // 声明子组件 b Vue.component('m-son-b', { data() { return { data: {} } }, template: `<div> <input type="text" v-model="data.a" /> </div>`, created() { // 数据初始化 this.data = data } }) // 声明父组件,并将父组件挂载到 #app 上 let mSelf = new Vue({ el: '#app', template: `<div> <m-son-a></m-son-a> <m-son-b></m-son-b> </div>` }) </script> </html>
该应用场景用于父子祖孙组件之间的通讯。
这对选项须要一块儿使用,以容许一个祖先组件向其全部子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。浏览器
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>场景三:父子孙子等更高级别组件通讯</title> <!-- 开发环境版本,包含了有帮助的命令行警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <!-- 父组件挂载到 #app 元素下 --> <m-self></m-self> </div> </body> <script> // 声明曾孙组件 Vue.component('m-great-grandson', { // inject 引入 provide 注入的属性 inject: ['a', 'onChangeB'], // 孙组件经过 inject 获取祖组件传递的数据 template: `<div> <input type="text" :value="a" @input="onInput" /> </div>`, methods: { onInput(e) { let data = e.target.value // 孙组件中更改了数据,经过 $emit 触发当前实例上的事件。附加参数都会传给监听器回调。 // this.$emit('onChangeB', data) // 或者 this.onChangeB(data) } } }) // 声明孙组件 Vue.component('m-grandson', { template: `<div> <m-great-grandson></m-great-grandson> </div>` }) // 声明子组件 Vue.component('m-son', { template: `<div> <m-grandson></m-grandson> </div>` }) // 声明父组件,并将父组件挂载到 #app 上 let mSelf = new Vue({ el: '#app', data() { return { b: '将要传递给曾孙组件的数据' } }, // provide 能够是对象,也能够是返回一个对象函数,该对象包含可注入的属性 provide() { return { a: this.b, onChangeB: this.onChange } }, // 父组件内,经过 provide 声明注入属性 // 父组件内,经过 v-on(/@) 绑定事件 (@onChangeB="onChange") template: `<div> <p>{{b}}</p> <m-son @onChangeB="onChange" ></m-son> </div>`, methods: { onChange(data) { this.b = data } } }) </script> </html>