前言
vue的核心就是組件的使用驾诈,組件是可復(fù)用的vue實(shí)例。如果項(xiàng)目中某一個(gè)部分需要在多個(gè)頁面中使用到溶浴,我們就可以將這部分代碼抽成一個(gè)可復(fù)用的組件乍迄。
然而,組件實(shí)例的作用域之間是相互獨(dú)立的戳葵,如果需要把組件之間的數(shù)據(jù)關(guān)聯(lián)起來就乓,這就需要懂組件之間的通信。
一拱烁、父組件向子組件傳值(通過props)
父組件通過v-bind
綁定變量生蚁,子組件通過props
方式接收。
例子:在子組件Child.vue
中如何獲取到父組件App.vue
中的數(shù)據(jù)title: '我是父組件的數(shù)據(jù)'
戏自。
父組件
// App.vue 父組件
<template>
<div id="app">
<h2>父組件:</h2>
<!-- :title 是傳到子組件的變量名邦投,便于子組件調(diào)用 -->
<!-- title 是父組件中的data的屬性值 -->
<Child :title="title"/>
</div>
</template>
<script>
import Child from './components/Child'
export default {
name: 'App',
data () {
return {
title: '我是父組件的數(shù)據(jù)'
}
},
components: {
Child
}
}
</script>
<style>
</style>
子組件
// Child.vue 子組件
<template>
<div class="child">
<h3>子組件:{{title}}</h3>
</div>
</template>
<script>
export default {
name: 'Child',
// 接收父組件的值
props: {
title: String
}
}
</script>
<style scoped>
</style>
實(shí)現(xiàn)效果
總結(jié):這種方式只能由父組件向子組件傳遞,子組件不能更新父組件內(nèi)的data擅笔。也就是說志衣,當(dāng)父組件的屬性發(fā)生變化時(shí),將傳遞給子組件猛们,但不會(huì)反過來念脯,因?yàn)閜rops是單向綁定的。
二弯淘、子組件向父組件傳值(通過$emit)
相當(dāng)于子組件調(diào)用父組件的方法绿店。
子組件通過$emit
發(fā)射一個(gè)方法,父組件通過v-on
實(shí)現(xiàn)庐橙。
例子:當(dāng)我們點(diǎn)擊子組件的調(diào)用父組件中的方法
按鈕時(shí)假勿,父組件中的內(nèi)容我是父組件的內(nèi)容
修改成父組件的內(nèi)容被修改了
,從而實(shí)現(xiàn)子組件向父組件傳值态鳖。
子組件
// Child.vue 子組件
<template>
<div class="child">
<h3>子組件:</h3>
// 定義一個(gè)子組件傳值的方法 handleClick
<input type="button" value="調(diào)用父組件中的方法" @click="handleClick">
</div>
</template>
<script>
export default {
name: 'Child',
data () {
return {
title: '父組件的內(nèi)容被修改了'
}
},
methods: {
handleClick () {
// titleChanged 自定義事件名
// this.title 需要傳給父組件的值
this.$emit("titleChanged", this.title)
}
}
}
</script>
<style scoped>
</style>
父組件
// App.vue 父組件
<template>
<div id="app">
<h3>父組件 -- {{title}}</h3>
<!-- titleChanged 與子組件中自定義事件名保持一致 -->
<!-- updateTitle 方法名转培,需要接收子組件傳遞過來的值 -->
<Child @titleChanged="updateTitle"/>
</div>
</template>
<script>
import Child from './components/Child'
export default {
name: 'App',
data () {
return {
title: '我是父組件的內(nèi)容'
}
},
components: {
Child
},
methods: {
updateTitle (e) {
// e 就是子組件傳遞過來的值
this.title = e;
}
}
}
</script>
<style>
</style>
實(shí)現(xiàn)效果
三、父組件調(diào)用子組件的方法或訪問數(shù)據(jù)(通過$ref調(diào)用)
例子:在父組件App.vue
中訪問子組件Child.vue
中的title
數(shù)據(jù)和調(diào)用childAlert
方法浆竭。
子組件
// Child.vue
<template>
<div class="child">
</div>
</template>
<script>
export default {
name: 'Child',
data () {
return {
title: '我是子組件的內(nèi)容'
}
},
methods: {
childAlert () {
window.alert('我是子組件里面的彈窗浸须!')
}
}
}
</script>
<style scoped>
</style>
父組件
// App.vue
<template>
<div id="app">
<Child ref="childRef"/>
</div>
</template>
<script>
import Child from './components/Child'
export default {
name: 'App',
data () {
return {
}
},
components: {
Child
},
mounted () {
// 訪問子組件的 title
console.log(this.$refs.childRef.title) // 輸出‘我是子組件的內(nèi)容’
// 調(diào)用子組件的 childAlert 方法
this.$refs.childRef.childAlert()
}
}
</script>
<style>
</style>
總結(jié):ref
如果直接在普通的DOM
元素上使用惨寿,引用所指向的就是DOM
元素,如果在子組件
上使用羽戒,引用所指向的就是組件實(shí)例
缤沦。
實(shí)現(xiàn)效果
四、非父子組件之間的通信(中央事件總線)
該方法通過一個(gè)空的Vue
實(shí)例作為中央事件總線易稠,才能使用$emit
獲取$on
的數(shù)據(jù)參數(shù)缸废,實(shí)現(xiàn)組件通信。
創(chuàng)建一個(gè)空的Vue實(shí)例文件eventBus.js
驶社,也就是一個(gè)中央事件總線企量。
// eventBus.js
import Vue from 'vue'
export default new Vue()
創(chuàng)建A.vue
組件,引入eventBus.js
文件亡电。
// A.vue 組件
<template>
<div class="a_com">
<h3>A組件:</h3>
<input type="button" value="點(diǎn)擊按鈕給B組件傳遞數(shù)據(jù)" @click="emitBCom">
</div>
</template>
<script>
// 引入空的 vue 實(shí)例
import eventBus from '../js/eventBus'
export default {
name: 'A',
data () {
return {
msg: '我是A組件的內(nèi)容'
}
},
methods: {
emitBCom () {
// bComHandle 自定義事件名届巩,觸發(fā)一個(gè)可以讓B組件監(jiān)聽的方法
// this.msg 要傳給B組件的值
eventBus.$emit('bComHandle', this.msg)
}
}
}
</script>
<style scoped>
</style>
創(chuàng)建B.vue
組件,引入eventBus.js
文件份乒。
// B.vue 組件
<template>
<div class="b_com">
<h3>B組件:</h3>
<p>接收A組件傳遞過來的值:{{msg}}</p>
</div>
</template>
<script>
// 引入空的 vue 實(shí)例
import eventBus from '../js/eventBus'
export default {
name: 'B',
data () {
return {
msg: '',
}
},
mounted () {
// 監(jiān)聽A組件的自定義事件
// data 這個(gè)data就是A組件傳遞過來的值
eventBus.$on('bComHandle', data => this.msg = data)
}
}
</script>
<style scoped>
</style>
在App.vue
中引入A.vue
和B.vue
兩個(gè)組件恕汇,并掛載到頁面上。
// App.vue
<template>
<div id="app">
<a-com />
<b-com />
</div>
</template>
<script>
const ACom = () => import('./components/A')
const BCom = () => import('./components/B')
export default {
name: 'App',
components: {
ACom,
BCom
}
}
</script>
<style>
</style>
整個(gè)過程的步驟:
新建一個(gè)空的Vue實(shí)例文件eventBus.js
或辖;
在組件中引入定義的實(shí)例瘾英;
通過$emit
觸發(fā)一個(gè)自定義事件,并傳遞數(shù)據(jù)颂暇,eventBus.$emit(自定義事件名稱缺谴,要傳遞的值)
;
把傳遞過來的自定義事件通過$on
監(jiān)聽回調(diào)函數(shù)耳鸯,eventBus.$on(自定義事件名稱, () => {})
湿蛔。
總結(jié):這種只用一個(gè)Vue
實(shí)例來作為中央事件總線來管理非父子組件通信的方法只適用于通信需求簡單一點(diǎn)的項(xiàng)目,對于更加復(fù)雜的情況县爬,需要使用Vue
提供的狀態(tài)管理模式Vuex
來進(jìn)行處理阳啥。
實(shí)現(xiàn)效果
五、子組件調(diào)用父組件的方法或訪問數(shù)據(jù)(另外一種方法:通過$parent)
例子:在子組件Child.vue
中訪問父組件App.vue
中的msg
數(shù)據(jù)和調(diào)用show
方法财喳。
父組件
// App.vue
<template>
<div id="app">
<child />
</div>
</template>
<script>
const Child = () => import('./components/Child')
export default {
name: 'App',
data () {
return {
msg: '我是父組件中的內(nèi)容'
}
},
methods: {
show () {
console.log('我是父組件的方法察迟!')
}
},
components: {
Child
}
}
</script>
<style>
</style>
子組件
// Child.vue
<template>
<div class="child">
<h3>我是子組件:</h3>
<p>訪問父組件中的msg數(shù)據(jù):{{msg}}</p>
</div>
</template>
<script>
export default {
name: 'Child',
data () {
return {
msg: ''
}
},
mounted () {
// 訪問父組件中的 msg 數(shù)據(jù)
this.msg = this.$parent.msg
// 調(diào)用父組件中的 show 方法
this.$parent.show()
}
}
</script>
<style scoped>
</style>
總結(jié):用此方法前提得知道父組件是誰,如果項(xiàng)目中組件嵌套非常多的話纲缓,不推薦使用這個(gè)方法!
實(shí)現(xiàn)效果
六喊废、跨級組件間的通信(通過provide/inject)
這對選項(xiàng)需要一起使用祝高,以允許一個(gè)祖先組件向其所有子孫后代注入一個(gè)依賴,不論組件層次有多深污筷,并在起上下游關(guān)系成立的時(shí)間里始終生效工闺。
provide
選項(xiàng)應(yīng)該是:一個(gè)對象或返回一個(gè)對象的函數(shù)乍赫。該對象包含可注入其子孫的屬性。
inject
選項(xiàng)應(yīng)該是:一個(gè)字符串?dāng)?shù)組陆蟆,或一個(gè)對象雷厂,對象的key
是本地的綁定名,value
是:在可用的注入內(nèi)容中搜索用的key
(字符串或Symbol)叠殷,或一個(gè)對象改鲫,該對象的:from
屬性是在可用的注入內(nèi)容中搜索用的 key(字符串或 Symbol),default
屬性是降級情況下使用的value
林束。
假設(shè)我們有兩個(gè)組件Child.vue
和Grandparent.vue
像棘,來看一下比較簡單的用法:
祖先級組件
// Grandparent.vue組件
<template>
<div class="grandparent">
<child />
</div>
</template>
<script>
const Child = () => import('./Child')
export default {
name: 'Grandparent',
provide: {
name: 'allen'
},
components: {
Child
}
}
</script>
<style scoped>
</style>
子孫級組件
// Child.vue
<template>
<div class="child">
<!-- 獲取 Grandparent 組件中的 name 值 -->
<h2>{{gp_name}}</h2>
</div>
</template>
<script>
export default {
name: 'Child',
data () {
return {
gp_name: ''
}
},
inject: ['name'],
mounted () {
this.gp_name = this.name
console.log(this.name) // 輸出 allen
}
}
</script>
<style scoped>
</style>
我們在祖先級組件中設(shè)置了一個(gè)provide:name
,值為allen
壶冒,它的作用就是將name
這個(gè)變量提供給它的所有子孫級組件缕题。而在子孫級組件中通過inject
注入了從上級組件中提供的name
變量,那么在子孫級組件中胖腾,就可以直接通過this.name
來訪問了烟零。
提示:
provide
和inject
綁定并不是可響應(yīng)的。這是刻意為之的咸作。然而锨阿,如果你傳入了一個(gè)可監(jiān)聽的對象,那么其對象的屬性還是可響應(yīng)的性宏。
關(guān)于這對選項(xiàng)的深入用法群井,大家可以去看看官方文檔哦!
七毫胜、兄弟組件之間的通信(通過Vuex)
Vuex
是一個(gè)專為Vue.js
應(yīng)用程序開發(fā)的狀態(tài)管理模式书斜。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化酵使。
父組件App.vue
// App.vue
<template>
<div id="app">
<child-a />
<child-b />
</div>
</template>
<script>
const ChildA = () => import('./components/ChildA') // 導(dǎo)入 ChildA 組件
const ChildB = () => import('./components/ChildB') // 導(dǎo)入 ChildB 組件
export default {
name: 'App',
components: {
ChildA,
ChildB
}
}
</script>
<style>
</style>
子組件ChildA.vue
// ChildA.vue
<template>
<div class="child-a">
<h3>A組件:</h3>
<input type="button" value="點(diǎn)擊按鈕讓B組件接收數(shù)據(jù)" @click="AComHandler">
<p>B組件的數(shù)據(jù):{{bmsg}}</p>
</div>
</template>
<script>
export default {
name: 'ChildA',
data () {
return {
amsg: '我是A組件的內(nèi)容'
}
},
computed: {
bmsg () {
return this.$store.state.BComMsg
}
},
methods: {
AComHandler () {
this.$store.commit('transferAComMsg', {
AComMsg: this.amsg
})
}
}
}
</script>
<style scoped>
</style>
子組件ChildB.vue
// ChildB.vue
<template>
<div class="child-b">
<h3>B組件:</h3>
<input type="button" value="點(diǎn)擊按鈕讓A組件接收到數(shù)據(jù)" @click="AComHandler">
<p>A組件的數(shù)據(jù):{{amsg}}</p>
</div>
</template>
<script>
export default {
name: 'ChildB',
data () {
return {
bmsg: '我是B組件的內(nèi)容'
}
},
computed: {
amsg () {
return this.$store.state.AComMsg
}
},
methods: {
AComHandler () {
this.$store.commit('transferBComMsg', {
BComMsg: this.bmsg
})
}
}
}
</script>
<style scoped>
</style>
引入vuex
模塊
> npm install vuex --save
在src
文件夾下創(chuàng)建store
文件夾荐吉,并創(chuàng)建index.js
文件
// index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
// 初始化A組件和B組件的數(shù)據(jù)
AComMsg: '',
BComMsg: ''
},
mutations: {
// 將A組件數(shù)據(jù)存放到state中
transferAComMsg (state, payload) {
state.AComMsg = payload.AComMsg
},
// 將B組件數(shù)據(jù)存放到state中
transferBComMsg (state, payload) {
state.BComMsg = payload.BComMsg
}
}
})
export default store
在main.js
導(dǎo)入
// main.js
import Vue from 'vue'
import App from './App'
import store from './store/index'
Vue.config.productionTip = false
new Vue({
el: '#app',
store,
render: h => h(App)
})
參考資料
Vue.js 官方文檔
Vue.js API官方文檔
Vue.js 組件通信方式
Vuex 官方文檔使用
Vue進(jìn)行兄弟組件通信