vue源碼(二)-vue組件通信方式
一、組件化
組件化開發能夠提高開發效率,方便重複使用
簡化調試步驟,提升項目的可維護性,便於多人系統開發
二、通信方式
1.父組件->子組件
1.1通過屬性props進行傳遞
子組件進行定義一個字段msg接受父組件傳遞的參數
<template>
<div class="hello">
<h3>{{ msg }}</h3>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<style scoped>
h3 {
margin: 40px 0 0;
}
</style>
上述代碼中,通過對props中msg的限定,進行接收有父組件傳遞下來的內容,然後在模版文件中通過插槽表達式進行使用。
<HelloWorld msg="用於prop傳遞參數給子組件"/> // 父組件通過msg進行傳遞參數
1.2vm.$attrs
-
只讀
包含了父作用域中不作爲 prop 被識別 (且獲取) 的特性綁定 (
class
和style
除外)。當一個組件沒有聲明任何 prop 時,這裏會包含所有父作用域的綁定 (class
和style
除外),並且可以通過v-bind="$attrs"
傳入內部組件——在創建高級別的組件時非常有用。
<template>
<div class="hello">
<p>{{$attrs.foo }}</p>
</div>
</template>
<script>
export default {
name: 'HelloWorld'
}
</script>
<style scoped>
h3 {
margin: 40px 0 0;
}
</style>
上述代碼,通過$attrs屬性進行獲取由父組件傳遞的參數foo,這種方式用於獲取在子組件中props中沒有聲明的屬性,在父組件中使用和1.1使用方式一致
<HelloWorld foo="測試$attrs.foo"/> // 父組件通過foo進行傳遞參數
1.3 vm.$refs
-
只讀
一個對象,持有註冊過
ref
特性 的所有 DOM 元素和組件實例。通過給子組件添加ref的形式進行傳遞數據如下所示:
<template> <div class="hello"> <p>{{ age }}</p> </div> </template> <script> export default { name: 'HelloWorld', data: () => { return { age: '30' } } } </script> <style scoped> h3 { margin: 40px 0 0; } </style>
子組件中有數據age,我們能夠在父組件中通過this.$refs.hw.age = '18'
的方式進行修改age屬性值
1.4 vm.$children-子元素
-
只讀
當前實例的直接子組件。**需要注意
$children
並不保證順序,也不是響應式的。**如果你發現自己正在嘗試使用$children
來進行數據綁定,考慮使用一個數組配合v-for
來生成子組件,並且使用 Array 作爲真正的來源。
與1.3同樣的子組件,在父組件通信時,只需要使用this.$children[0].age = '20';
方式進行處理age的值即可
2.子組件->父組件通信
通過註冊自定義事件監聽的方式進行傳遞,通過 @click="$emit(‘clickDiv’)"進行派發事件,然後父組件處理的方式
<template>
<div class="hello" @click="$emit('clickDiv')">
<h3>{{ msg }}</h3>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
data: () => {
return {
age: '30'
}
}
}
</script>
<style scoped>
h3 {
margin: 40px 0 0;
}
</style>
父組件中需要對事件進行監聽
<div id="app">
<HelloWorld
msg="用於prop傳遞參數給子組件"
foo="測試$attrs.foo"
ref="hw"
@clickDiv="onMyClick"
/>
其中onMyClick是父組件中方法,用於處理clickDiv傳遞的數據
3.兄弟之間通信:通過共同的祖輩組件通信
一般通過$parent
和 $root
進行處理,利用組件的生命週期進行監聽事件,然後進行處理兄弟節點傳遞的數據
在每個需要監聽的兄弟節點中添加下面監聽事件
created() {
// 監聽事件
this.$parent.$on('hiBrother', () => {
console.log('來自兄弟的問候', 'HelloWorld');
});
},
接着在需要通過兄弟節點的地方進行調用如下代碼:
this.$parent.$emit('hiBrother');
通過上述方式,就能夠實現兄弟之間的通信
4.祖先和後代之間
由於嵌套層級過多,傳遞props不切合實際,可使用provide/inject API完成該任務
provide / inject:
這對選項需要一起使用,以允許一個祖先組件向其所有子孫後代注入一個依賴,不論組件層次有多深,並在起上下游關係成立的時間裏始終生效。如果你熟悉 React,這與 React 的上下文特性context很相似。
provide
選項應該是一個對象或返回一個對象的函數。該對象包含可注入其子孫的屬性。在該對象中你可以使用 ES2015 Symbols 作爲 key,但是隻在原生支持 Symbol
和 Reflect.ownKeys
的環境下可工作。
inject
選項應該是:
- 一個字符串數組,或
- 一個對象,對象的 key 是本地的綁定名,value 是:
- 在可用的注入內容中搜索用的 key (字符串或 Symbol),或
- 一個對象,該對象的:
from
屬性是在可用的注入內容中搜索用的 key (字符串或 Symbol)default
屬性是降級情況下使用的 value
在父組件進行提供值:
// ancestor
provide(){
return {
provideFoo: '測試provide/inject傳遞信息'
}
},
在子組件中進行使用注入值:
// descendant
inject: {
provideFoo: { default: '這裏設置默認值' }
},
// 或者使用數組形式
inject: ['provideFoo']
5.任意兩個組件之間:事件總線或vuex
- 事件總線:創建一個公共類負責事件派發、監聽和回調管理
// EventEmit:事件派發、監聽和回調管理
export default class EventEmit {
constructor() {
this.callbacks = {};
}
$on(name, fn) {
this.callbacks[name] = this.callbacks[name] || [];
this.callbacks[name].push(fn);
}
$emit(name, args) {
if (this.callbacks[name]) {
// 存在 遍歷所有callback
this.callbacks[name].forEach(cb => cb(args));
}
}
}
上述代碼,使用$on
進行註冊全局監聽事件、使用$emit
進行通知全局處理事件.
當然我們需要將這個公共處理事件類掛在Vue原型上:
import Vue from "vue";
import App from "./App.vue";
import EventEmit from './utils/eventEmit';
// main.js
// 事件總線方式,掛載到Vue中
Vue.prototype.$eventEmit = new EventEmit();
new Vue({
data: {
bar: 'bar'
},
render: h => h(App),
}).$mount("#app");
在需要使用監聽的地方進行添加監聽
// 創建事件總線
this.$eventEmit.$on('fromHelloWorld', (value) => {
alert('監聽到由hello world組件傳遞來的數據:' + value);
});
在傳遞觸發的組件中進行觸發
// 派發事件,在HelloWorldEmit.vue組件中監聽
this.$eventEmit.$emit('fromHelloWorld', '傳遞給HelloWorldEmit.vue的數據');
觸發的同時,通過第二個參數進行傳值。
以上就是組件之間通信的各種方式。