vue コンポーネント間で通信するにはどうすればよいですか?この記事では、Vue コンポーネント間で通信する 12 の方法を詳しく紹介します。
vue は、データ駆動型のビュー更新のためのフレームワークです。私たちの日常の開発では、ページのさまざまなモジュールを vue コンポーネントに分割します。そのため、vue の場合、間のデータはコンポーネント 通信は非常に重要ですが、コンポーネント間でデータはどのように通信するのでしょうか?
まず、通信方法を理解しやすくするために、vue のコンポーネント間にどのような関係があるかを知る必要があります。 [関連する推奨事項: "vue.js チュートリアル "]
通常、次の関係に分かれます:
親コンポーネントは、以下を介して子コンポーネントと通信します。 props
データを渡すと、子コンポーネントは $emit
を通じて親コンポーネントと通信できます。
<!-- 父组件 --> <template> <div class="section"> <child :msg="articleList"></child> </div> </template> <script> import child from './child.vue' export default { name: 'HelloWorld', components: { comArticle }, data() { return { msg: '阿离王' } } } </script>
<!-- 子组件 child.vue --> <template> <div> {{ msg }} </div> </template> <script> export default { props: { msg: String } } </script>
注:
prop は上位レベルのコンポーネントから次のコンポーネントにのみ渡すことができます。レベル コンポーネント (親子コンポーネント)、いわゆる一方向のデータ フロー。また、prop は読み取り専用のため変更できず、すべての変更は無効となり、警告が発行されます。
computed
属性を使用できます。 props
に 参照型 (オブジェクトまたは配列)
が渡された場合、オブジェクトまたは配列が子コンポーネントで変更された場合、親の状態コンポーネントも変更されます 対応する更新が行われ、これを使用して親子コンポーネント データの 「双方向バインディング」
を実現できます。この実装ではコードを節約できますが、データ フローの単純さ
. それは理解できないので、やらないのが最善です。 v-model
または .sync
を使用できます。 $emit
を使用して親コンポーネントにデータを渡します。親コンポーネントは次のとおりです サブコンポーネントは関数をリッスンし、v-on
を通じてパラメーターを受け取ります vue フレームワークは v-on="fn"
fn イベントをリッスンしますサブコンポーネント内の関数。サブコンポーネント内で # を使用します。##$emit をトリガーできます。例を次に示します。
<!-- 父组件 --> <template> <div class="section"> <child :msg="articleList" @changMsg="changMsg"></child> </div> </template> <script> import child from './child.vue' export default { name: 'HelloWorld', components: { comArticle }, data() { return { msg: '阿离王' } }, methods:{ changMsg(msg) { this.msg = msg } } } </script>
<!-- 子组件 child.vue --> <template> <div> {{ msg }} <button @click="change">改变字符串</button> </div> </template> <script> export default { props: { msg: String }, methods: { change(){ this.$emit('changMsg', '阿离王带你学习前端') } } } </script>
v-model は、
フォーム コントロール または
コンポーネント # に ## を作成するために使用されます。双方向バインディング
、その本質は、コンポーネント v- で使用される
v-bind および
v-on の
糖衣構文 です。 model
は、デフォルトで、value という名前の prop
と input
という名前のイベントをコンポーネントにバインドします。 コンポーネント内の特定の
が上記の「双方向バインディング」を実装する必要がある場合、v-model
が活躍します。これを使用すると、コンポーネント上の現在のインスタンスでカスタム イベントを手動でバインドしてリッスンする必要がなくなり、 コードがより簡潔になります
。 以下では、入力コンポーネントによって実装されたコア コードを使用して、
のアプリケーションを紹介します。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:html;toolbar:false;"><!--父组件-->
<template>
<base-input v-model="inputValue"></base-input>
</template>
<script>
export default {
data() {
return {
input: &#39;&#39;
}
},
}
</script></pre><div class="contentsignin">ログイン後にコピー</div></div><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:html;toolbar:false;"><!--子组件-->
<template>
<input type="text" :value="currentValue" @input="handleInput">
</template>
<script>
export default {
data() {
return {
currentValue: this.value === undefined || this.value === null ? &#39;&#39;
}
},
props: {
value: [String, Number], // 关键1
},
methods: {
handleInput(event) {
const value = event.target.value;
this.$emit(&#39;input&#39;, value); // 关键2
},
},
}
</script></pre><div class="contentsignin">ログイン後にコピー</div></div>
上の例でわかるように、
は本質的に v-bind および v-on
の糖衣構文であり、デフォルトは親コンポーネント :value="inputValue"
という名前のプロパティと、子コンポーネントの @input="(v) => { this.inputValue = v }"
イベントをバインドします。 passthis.$emit('input', value)
親コンポーネントに通知するしたがって、彼の原理ではパラメータ
を渡す親子コンポーネントも使用します。上で説明した方法 双方向バインディングを実現するには一部の特定のコントロールの value という名前のプロパティが特別な意味を持っている場合があります。この場合、
.sync 修飾子
を持つ
prop が変更されると、値は親コンポーネントの値と同期されます。これは非常に便利ですが、問題も非常に明らかです。これにより、一方向のデータ フローが破壊され、アプリケーションが複雑な場合、デバッグのコストが非常に高くなります。
つまり、
.sync には独自のアプリケーション シナリオがあるため、
vue 2.3 バージョンでは、新しい
.sync が導入されています。
新しい
.sync に似ています。
通常のカプセル化されたコンポーネントの例: <text-document v-bind:title="doc.title" v-on:update:title="doc.title = $event" />
.sync
を使用して<text-document v-bind:title.sync="doc.title" />
として記述できます。サブコンポーネントでは、次のコードを通じてこのプロパティを再割り当てできます。
this.$emit('update:title', newTitle)
看到这里,是不是发现 .sync
修饰符 和 v-model
很相似,也是语法糖, v-bind:title.sync
也就是 等效于 v-bind:title="doc.title" v-on:update:title="doc.title = $event"
.sync
从功能上看和 v-model
十分相似,都是为了实现数据的“双向绑定”
,本质上,也都不是真正的双向绑定,而是语法糖
。
相比较之下,.sync
更加灵活,它可以给多个 prop
使用,而 v-model
在一个组件中只能有一个。
从语义上来看,v-model
绑定的值是指这个组件的绑定值,比如 input 组件
,select 组件
,日期时间选择组件
,颜色选择器组件
,这些组件所绑定的值使用 v-model
比较合适。其他情况,没有这种语义,个人认为使用 .sync
更好。
通过$parent
和$children
就可以访问组件的实例,拿到实例代表什么?代表可以访问此组件的所有方法
和data
。列子如下:
<!-- 父组件 --> <template> <div class="hello_world"> <div>{{msg}}</div> <com-a></com-a> <button @click="changeA">点击改变子组件值</button> </div> </template> <script> import ComA from './test/comA.vue' export default { name: 'HelloWorld', components: { ComA }, data() { return { msg: 'Welcome' } }, methods: { changeA() { // 获取到子组件A this.$children[0].messageA = 'this is new value' } } } </script>
<!-- 子组件 --> <template> <div class="com_a"> <span>{{messageA}}</span> <p>获取父组件的值为: {{parentVal}}</p> </div> </template> <script> export default { data() { return { messageA: 'this is old' } }, computed:{ parentVal(){ return this.$parent.msg; } } } </script>
要注意边界情况,如在#app
上拿$parent
得到的是new Vue()
的实例,在这实例上再拿$parent
得到的是undefined
,而在最底层的子组件拿$children
是个空数组
。也要注意得到children的值不一样,$children 的值是数组
,而$parent是个对象
props $emit
、 $parent $children
两种方式用于父子组件之间的通信, 而使用props进行父子组件通信更加普遍,二者皆不能用于非父子组件之间的通信。
provide / inject
是vue2.2.0新增的api, 简单来说就是父组件中通过provide来提供变量
, 然后再子组件中通过inject来注入变量
。
官方描述: 这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效
provide
选项应该是
inject
选项应该是:
// 祖先组件 提供foo //第一种 export default { name: "father", provide() { return { foo: 'hello' } }, } //第二种 export default { name: "father", provide: { foo:'hello~~~~' }, }
//后代组件 注入foo, 直接当做this.foo来用 export default { inject:['foo'], }
上面的两种用法有什么区别吗?
hello
,那么是没有区别的,后代都能读到。this
对象属性的值(如下所示代码),那么第二种是传不了的,后代组件拿不到数据。所以建议只写第一种//当你传递对象给后代时 provide() { return { test: this.msg } },
注意:一旦注入了某个数据,比如上面示例中的 foo,那这个组件中就不能再声明 foo 这个数据了,因为它已经被父级占有。
这是刻意为之的。然而,如果你传入了一个可监听的对象
,那么其对象的属性还是可响应的。因为对象是引用类型。
先来个值类型的数据(也就是字符串)例子,不会响应
provide(){ return{ test:this.msg } }, data() { return { msg: "Welcome to Your Vue.js App", } } mounted(){ setTimeout(()=>{ this.msg = "halo world"; console.log(this._provided.msg) //log:Welcome to Your Vue.js App },3000) },
如上所示,这样做是不行的,打印出来的 _provided
中的数据并没有改,子组件取得值也没变。
你甚至可以直接给 this._provided.msg
赋值,但是即使是_provided.msg 里面的值改变了,子组件的取值,依然没有变。
当你的参数是对象的时候,就可以响应了,如下:
provide(){ return{ test:this.activeData } }, data() { return { activeData:{name:'halo'}, } } mounted(){ setTimeout(()=>{ this.activeData.name = 'world'; },3000) }
这就是vue官方中写道的对象的属性是可以响应的
provide/inject不是只能从祖先传递给后代吗?是的,但是,如果我们绑定到最顶层的组件app.vue,是不是所有后代都接收到了,就是当做全局变量来用了。
//app.vue export default { name: 'App', provide(){ return{ app:this } }, data(){ return{ text:"it's hard to tell the night time from the day" } }, methods:{ say(){ console.log("Desperado, why don't you come to your senses?") } } }
//其他所有子组件,需要全局变量的,只需要按需注入app即可 export default { inject:['foo','app'], mounted(){ console.log(this.app.text); // 获取app中的变量 this.app.say(); // 可以执行app中的方法,变身为全局方法! } }
vue-router
重新路由到当前页面,页面是不进行刷新的window.reload()
,或者router.go(0)
刷新时,整个浏览器进行了重新加载,闪烁,体验不好那我们怎么做呢?
跟上面的原理差不多,我们只在控制路由的组件中写一个函数(使用v-if控制router-view的显示隐藏,这里的原理不作赘述),然后把这个函数传递给后代,然后在后代组件中调用这个方法即可刷新路由啦。
//app.vue <router-view v-if="isShowRouter"/> export default { name: 'App', provide() { return { reload: this.reload } }, data() { return { isShowRouter: true, } }, methods:{ reload() { this.isShowRouter = false; this.$nextTick(() => { this.isShowRouter = true; }) } } }
//后代组件 export default { inject: ['reload'], }
这里 provide 使用了函数传递给后代,然后后代调用这个函数,这种思路,也是可以做子后代向父组件传参通讯的思路了。这里的原理,和 event 事件订阅发布就很像了
ref
:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素
;如果用在子组件
上,引用就指向组件实例
,可以通过实例直接调用组件的方法或访问数据, 我们看一个ref 来访问组件的例子:
// 子组件 A.vue export default { data () { return { name: 'Vue.js' } }, methods: { sayHello () { console.log('hello') } } }
// 父组件 app.vue <template> <component-a ref="comA"></component-a> </template> <script> export default { mounted () { const comA = this.$refs.comA; console.log(comA.name); // Vue.js comA.sayHello(); // hello } } </script>
ref 这种方式,就是获取子组件的实例,然后可以直接子组件的方法和访问操作data的数据,就是父组件控制子组件的一种方式,子组件想向父组件传参或操作,只能通过其他的方式了
eventBus
呢,其实原理就是 事件订阅发布,eventBus
又称为事件总线,在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件, 所以组件都可以通知其他组件。
这里我们可以直接使用 vue 自带的事件监听,也就是 on,我们来简单封装下:
新建一个 event-bus.js
文件
// event-bus.js import Vue from 'vue' export const EventBus = new Vue()
假设你有两个组件: additionNum 和 showNum, 这两个组件可以是兄弟组件也可以是父子组件;这里我们以兄弟组件为例:
<template> <div> <show-num-com></show-num-com> <addition-num-com></addition-num-com> </div> </template> <script> import showNumCom from './showNum.vue' import additionNumCom from './additionNum.vue' export default { components: { showNumCom, additionNumCom } } </script>
// addtionNum.vue 中发送事件 <template> <div> <button @click="additionHandle">+加法器</button> </div> </template> <script> import { EventBus } from './event-bus.js' console.log(EventBus) export default { data() { return { num: 1 } }, methods: { additionHandle() { EventBus.$emit('addition', { num: this.num++ }) } } } </script>
// showNum.vue 中接收事件 <template> <div>计算和: {{count}}</div> </template> <script> import { EventBus } from './event-bus.js' export default { data() { return { count: 0 } }, mounted() { EventBus.$on('addition', param => { this.count = this.count + param.num; }) } } </script>
这样就实现了在组件addtionNum.vue
中点击相加按钮, 在showNum.vue中利用传递来的 num 展示求和的结果.
如果想移除事件的监听, 可以像下面这样操作:
import { eventBus } from 'event-bus.js' EventBus.$off('addition')
这里使用自己封装一套eventBus也行,方便自己想干啥就干啥, 下面贴封装好的一套给大家
/* eslint-disable no-console */ // 事件映射表 let eventMap = {} /** * 监听事件 * @param {string} eventName 事件名 * @param {function} listener 回调函数 * @param {object} instance 注册事件的实例 */ function on(eventName, listener, instance) { eventMap[eventName] = eventMap[eventName] || [] eventMap[eventName].push({ listener, instance, }) } // 监听事件,只执行一次 function once(eventName, listener, instance) { eventMap[eventName] = eventMap[eventName] || [] eventMap[eventName].push({ listener, instance, once: true, }) } // 解除事件监听 function off(eventName, listener) { // 解除所有事件监听 if (!eventName) { eventMap = {} return } // 没有对应事件 if (!eventMap[eventName]) { return } // 解除某事件监听 eventMap[eventName].forEach((currentEvent, index) => { if (currentEvent.listener === listener) { eventMap[eventName].splice(index, 1) } }) } // 发送事件,执行对应响应函数 function emit(eventName, ...args) { if (!eventMap[eventName]) { return } eventMap[eventName].forEach((currentEvent, index) => { currentEvent.listener(...args) if (currentEvent.once) { eventMap[eventName].splice(index, 1) } }) } // 显示当前注册的事件,代码优化时使用 function showEventMap(targetEventName) { if (targetEventName) { // 查看具体某个事件的监听情况 eventMap[targetEventName].forEach(eventItem => { console.log(targetEventName, eventItem.instance, eventItem.listener) }) } else { // 查看所以事件的监听情况 Object.keys(eventMap).forEach(eventName => { eventMap[eventName].forEach(eventItem => { console.log(eventName, eventItem.instance, eventItem.listener) }) }) } } // 提供 vue mixin 方法,在 beforeDestroy 自动注销事件监听 export const mixin = { created() { // 重载 on 函数,收集本组件监听的事件,待消除时,销毁事件监听 this.$eventListenerList = [] this.$event = { off, once, emit, showEventMap } this.$event.on = (eventName, listener) => { this.$eventListenerList.push({ eventName, listener }) on(eventName, listener) } }, // 消除组件时,自动销毁事件监听 beforeDestroy() { this.$eventListenerList.forEach(currentEvent => { off(currentEvent.eventName, currentEvent.listener) }) }, } export default { on, off, once, emit, showEventMap }
如何使用呢,只需在 项目的 main.js
, 引入 ,然后 Vue.mixin 即可,如下:
// main.js import Vue from 'vue' import { mixin as eventMixin } from '@/event/index' Vue.mixin(eventMixin)
在vue项目其他文件,就可以直接 this.$event.on
this.$event.$emit
如下:
this.$event.on('test', (v) => { console.log(v) }) this.$event.$emit('test', 1)
还顺便封装了个mixin, 好处呢,就是在vue页面监听事件后,页面销毁后,也自动销毁了事件监听
Vuex
是一个专为 Vue.js 应用程序开发的状态管理模式
。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.
Vuex
解决了多个视图依赖于同一状态和来自不同视图的行为需要变更同一状态的问题,将开发者的精力聚焦于数据的更新而不是数据在组件之间的传递上
state
:用于数据的存储,是store中的唯一数据源
getters
:如vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据的相关性计算mutations
:类似函数,改变state数据的唯一途径,且不能用于处理异步事件actions
:类似于mutation,用于提交mutation来改变状态,而不直接变更状态,可以包含任意异步操作modules
:类似于命名空间,用于项目中将各个模块的状态分开定义和操作,便于维护这里我们先新建 store文件夹
, 对Vuex进行一些封装处理
在 store 文件夹下添加 index.js
文件
// index.js // 自动挂载指定目录下的store import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) let modules = {} // @/store/module 目录下的文件自动挂载为 store 模块 const subModuleList = require.context('@/store/modules', false, /.js$/) subModuleList.keys().forEach(subRouter => { const moduleName = subRouter.substring(2, subRouter.length - 3) modules[moduleName] = subModuleList(subRouter).default }) export default new Vuex.Store({ state: {}, mutations: {}, actions: {}, modules })
在 store 文件夹下添加 module
文件夹,在module文件夹再新建 user.js
文件
// user.js import user from '@/utils/user.js' import userApi from '@/apis/user' import { OPEN_ACCOUNT_STAGE, STAGE_STATUS } from '@/constant' let getUserPromise = null export default { namespaced: true, state() { return { userInfo: null, // 用户信息 isLogined: !!user.getToken(), // 是否已经登录 } }, mutations: { // 更新用户信息 updateUser(state, payload) { state.isLogined = !!payload state.userInfo = payload }, }, actions: { // 获取当前用户信息 async getUserInfo(context, payload) { // forceUpdate 表示是否强制更新 if (context.state.userInfo && !payload?.forceUpdate) { return context.state.userInfo } if (!getUserPromise || payload?.forceUpdate) { getUserPromise = userApi.getUserInfo() } // 获取用户信息 try { const userInfo = await getUserPromise context.commit('updateUser', userInfo) } finally { getUserPromise = null } return context.state.userInfo }, // 登出 async logout(context, payload = {}) { // 是否手动退出 const { manual } = payload if (manual) { await userApi.postLogout() } user.clearToken() context.commit('updateUser', null) }, } }
然后在项目的 main.js
文件中引入
import Vue from 'vue' import App from '@/app.vue' import { router } from '@/router' import store from '@/store/index' const vue = new Vue({ el: '#app', name: 'root', router, store, render: h => h(App), })
封装的很愉快了,然后就正常操作即可。
this.$store.state.user.isLogined this.$store.state.user.userInfo this.$store.commit('user/updateUser', {}) await this.$store.dispatch('user/logout', { manual: true })
这种通信比较简单,缺点是数据和状态比较混乱,不太容易维护。
window.localStorage.getItem(key)
获取数据window.localStorage.setItem(key,value)
存储数据注意用JSON.parse()
/ JSON.stringify()
做数据格式转换, localStorage / sessionStorage可以结合vuex, 实现数据的持久保存
,同时使用vuex解决数据和状态混乱问题.
对于小型的项目,通信十分简单,这时使用 Vuex 反而会显得冗余和繁琐,这种情况最好不要使用 Vuex,可以自己在项目中实现简单的 Store。
// store.js const store = { debug: true, state: { author: 'yushihu!' }, setAuthorAction (newValue) { if (this.debug) console.log('setAuthorAction triggered with', newValue) this.state.author = newValue }, deleteAuthorAction () { if (this.debug) console.log('deleteAuthorAction triggered') this.state.author = '' } } export default store
上面代码原理就是,store.js文件
暴露出一个对象 store
,通过引入 store.js 文件
各个页面来共同维护这个store对象
和 Vuex 一样,store 中 state 的改变都由 store 内部的 action 来触发,并且能够通过 console.log()
打印触发的痕迹。这种方式十分适合在不需要使用 Vuex 的小项目中应用。
与 $root
访问根实例的方法相比,这种集中式状态管理的方式
能够在调试过程中,通过 console.log()
记录来确定当前变化是如何触发的,更容易定位问题。
通过 $root
,任何组件都可以获取当前组件树的根 Vue 实例
,通过维护根实例上的 data,就可以实现组件间的数据共享
。
//main.js 根实例 new Vue({ el: '#app', store, router, // 根实例的 data 属性,维护通用的数据 data: function () { return { author: '' } }, components: { App }, template: '<App/>', }); <!--组件A--> <script> export default { created() { this.$root.author = '于是乎' } } </script> <!--组件B--> <template> <div><span>本文作者</span>{{ $root.author }}</div> </template>
注意:通过这种方式,虽然可以实现通信,但在应用的任何部分,任何时间发生的任何数据变化,都不会留下变更的记录,这对于稍复杂的应用来说,调试是致命的,不建议在实际应用中使用。
现在我们来讨论一种情况, 我们一开始给出的组件关系图中A组件与D组件是隔代关系, 那它们之前进行通信有哪些方式呢?
props
绑定来进行一级一级的信息传递, 如果D组件中状态改变需要传递数据给A, 使用事件系统一级级往上传递eventBus
,这种情况下还是比较适合使用, 但是碰到多人合作开发时, 代码维护性较低, 可读性也低
Vuex
来进行数据管理, 但是如果仅仅是传递数据
, 而不做中间处理,使用Vuex处理感觉有点大材小用
了.所以就有了 $attrs
/ $listeners
,通常配合 inheritAttrs
一起使用。
默认情况下父作用域的不被认作 props
的 attribute
绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。
通过设置 inheritAttrs
到 false
,这些默认行为将会被去掉。而通过 (同样是 2.4 新增的) 实例 property $attrs 可以让这些 attribute 生效,且可以通过 v-bind 显性的绑定到非根元素上。
注意:这个选项不影响 class
和 style
绑定。
上面是官方描述:还是很难懂。
简单的说就是
inheritAttrs:true
时继承除props之外的所有属性inheritAttrs:false
只继承class 和 style属性$attrs
:包含了父作用域中不被认为 (且不预期为) props 的特性绑定 (class 和 style 除外
),并且可以通过 v-bind="$attrs"
传入内部组件。当一个组件没有声明任何 props
时,它包含所有父作用域的绑定 (class 和 style 除外)。
$listeners
:包含了父作用域中的 (不含 .native 修饰符
) v-on 事件监听器。它可以通过 v-on="$listeners"
传入内部组件。它是一个对象,里面包含了作用在这个组件上的所有事件监听器,相当于子组件继承了父组件的事件
。
讲了这么多文字概念,我们还是来看代码例子吧:
新建一个 father.vue 组件
<template> <child :name="name" :age="age" :infoObj="infoObj" @updateInfo="updateInfo" @delInfo="delInfo" /> </template> <script> import Child from '../components/child.vue' export default { name: 'father', components: { Child }, data () { return { name: '阿离王', age: 22, infoObj: { from: '广东', job: 'policeman', hobby: ['reading', 'writing', 'skating'] } } }, methods: { updateInfo() { console.log('update info'); }, delInfo() { console.log('delete info'); } } } </script>
child.vue 组件:
<template> <!-- 通过 $listeners 将父作用域中的事件,传入 grandSon 组件,使其可以获取到 father 中的事件 --> <grand-son :height="height" :weight="weight" @addInfo="addInfo" v-bind="$attrs" v-on="$listeners" /> </template> <script> import GrandSon from '../components/grandSon.vue' export default { name: 'child', components: { GrandSon }, props: ['name'], data() { return { height: '180cm', weight: '70kg' }; }, created() { console.log(this.$attrs); // 结果:age, infoObj, 因为父组件共传来name, age, infoObj三个值,由于name被 props接收了,所以只有age, infoObj属性 console.log(this.$listeners); // updateInfo: f, delInfo: f }, methods: { addInfo () { console.log('add info') } } } </script>
grandSon.vue 组件
<template> <div> {{ $attrs }} --- {{ $listeners }} <div> </template> <script> export default { props: ['weight'], created() { console.log(this.$attrs); // age, infoObj, height console.log(this.$listeners) // updateInfo: f, delInfo: f, addInfo: f this.$emit('updateInfo') // 可以触发 father 组件中的updateInfo函数 } } </script>
这种方式的传值虽然说不常用,感觉可读性不是很好。但其对于组件层级嵌套比较深,使用props会很繁琐,或者项目比较小,不太适合使用 Vuex 的时候,可以考虑用它
常见使用场景可以分为三类:
props
、$parent / $children
、 provide / inject
、 ref \ $refs
、 $attrs / $listeners
eventBus
、 vuex
、 自己实现简单的 Store 模式
eventBus
、 Vuex
、 自己实现简单的 Store 模式
、 provide / inject
、 $attrs / $listeners
更多编程相关知识,请访问:编程入门!!
以上がVue コンポーネント間で通信するにはどうすればよいですか?共有する 12 のコミュニケーション方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。