Vue.js 父子组件之间通信的十种方式
这篇文章介绍了Vue.js 父子组件之间通信的十种方式,不管是初学者还是已经在用 Vue 的开发者都会有所收获。无可否认,现在无论大厂还是小厂都已经用上了 Vue.js 框架,简单易上手不说,教程详尽,社区活跃,第三方套件还多。真的是前端开发人员必备技能。而且在面试当中也往往会问到关于 Vue 方面的各种问题,其中大部分面试官会问到如上这种问题。
概述
几种通信方式无外乎以下几种:
Prop(常用)
$emit (组件封装用的较多)
.sync语法糖 (较少)
$attrs 和 $listeners (组件封装用的较多)
provide 和 inject (高阶组件/组件库用的较多)
其他方式通信
详述
下面逐个介绍,大神请绕行。
1. Prop
英式发音:[prɒp]。这个在我们日常开发当中用到的非常多。简单来说,我们可以通过 Prop 向子组件传递数据。用一个形象的比喻来说,父子组件之间的数据传递相当于自上而下的下水管子,只能从上往下流,不能逆流。这也正是 Vue 的设计理念之单向数据流。而 Prop 正是管道与管道之间的一个衔接口,这样水(数据)才能往下流。说这么多,看代码:
<``div id="app">
<``child :content="message">``child``>
``div``>
// Js
let Child = Vue.extend({
template: ``'
``,`
props: [``'name'``],
data () {
return {
text: ``''
}
},
watch: {
text (newVal) {
this``.$emit(``'update:name'``, newVal)
}
}
})
new Vue({
el: ``'#app'``,
data: {
userName: ``''
},
components: {
Login
}
})
|
你可以狠狠的戳这里查看Demo!下面划重点,代码里有这一句话:
this``.$emit(``'update:name'``, newVal)
官方语法是:update:myPropName 其中 myPropName 表示要更新的 prop 值。当然如果你不用 .sync 语法糖使用上面的 .$emit 也能达到同样的效果。仅此而已!
4. $attrs 和 $listeners
官网对 $attrs 的解释如下:
包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。
官网对 $listeners 的解释如下:
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。
我觉得 $attrs 和 $listeners 属性像两个收纳箱,一个负责收纳属性,一个负责收纳事件,都是以对象的形式来保存数据。看下面的代码解释:
<``div id="app">
<``child
:foo="foo"
:bar="bar"
@one.native="triggerOne"
@two="triggerTwo">
``child
|
从 Html 中可以看到,这里有俩属性和俩方法,区别是属性一个是 prop 声明,事件一个是 .native 修饰器。
|
let Child = Vue.extend({
template: ``'{{ content }}
'``, props: { content: { type: String, default``: () => { ``return 'from child' } } } }) new Vue({ el: ``'#app'``, data: { message: ``'from parent' }, components: { Child } }) | 你可以狠狠的戳这里查看Demo!浏览器输出:1 from parent2. $emit 英式发音:[iˈmɪt]。官方说法是触发当前实例上的事件。附加参数都会传给监听器回调。按照我的理解不知道能不能给大家说明白,先简单看下代码吧: <``div id="app"> <``my-button @greet="sayHi">``my-button``> ``div``> let MyButton = Vue.extend({ template: ``''``, data () { return { greeting: ``'vue.js!' } }, methods: { triggerClick () { this``.$emit(``'greet'``, ``this``.greeting) } } }) new Vue({ el: ``'#app'``, components: { MyButton }, methods: { sayHi (val) { alert(``'Hi, ' + val) ``// 'Hi, vue.js!' } } }) | 你可以狠狠的戳这里查看Demo! 大致逻辑是酱婶儿的:当我在页面上点击按钮时,触发了组件 MyButton 上的监听事件 greet,并且把参数传给了回调函数 sayHi 。说白了,当我们从子组件 Emit(派发) 一个事件之前,其内部都提前在事件队列中 On(监听)了这个事件及其监听回调。其实相当于下面这种写法: vm.$on(``'greet'``, ``function sayHi (val) { console.log(``'Hi, ' + val) } vm.$emit(``'greet'``, ``'vue.js'``) // => "Hi, vue.js" | 3. .sync 修饰符 这个家伙在 vue@1.x 的时候曾作为双向绑定功能存在,即子组件可以修改父组件中的值。因为它违反了单向数据流的设计理念,所以在 vue@2.0 的时候被干掉了。但是在 vue@2.3.0+ 以上版本又重新引入了这个 .sync 修饰符。但是这次它只是作为一个编译时的语法糖存在。它会被扩展为一个自动更新父组件属性的 v-on 监听器。说白了就是让我们手动进行更新父组件中的值了,从而使数据改动来源更加的明显。下面引入自官方的一段话: 在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以修改父组件,且在父组件和子组件都没有明显的改动来源。 既然作为一个语法糖,肯定是某种写法的简写形式,哪种写法呢,看代码: <``text-document v-bind:title="doc.title" v-on:update:title="doc.title = $event"> ``text-document``> | 于是我们可以用 .sync 语法糖简写成如下形式: <``text-document v-bind:title.sync="doc.title">``text-document``> | 废话这么多,如何做到“双向绑定” 呢?让我们进段广告,广告之后更加精彩! ... 好的,欢迎回来。假如我们想实现这样一个效果:改变子组件文本框中的值同时改变父组件中的值。怎么做?列位不妨先想想。先看段代码: let Login = Vue.extend({ `template: ``
发表评论 (审核通过后显示评论):