1、props / $emit (這是最常用的一種父子組件之間傳遞數(shù)據(jù)的方式)
父組件通過綁定屬性來向子組件傳遞數(shù)據(jù),子組件通過 props 屬性來獲取對應(yīng)的數(shù)據(jù)切诀;子組件通過 $emit 事件向父組件發(fā)送消息,將自己的數(shù)據(jù)傳遞給父組件搔弄。
使用方式:
// 父組件compA
<template>
? ? <div>
? ? ? ? <compB
? ? ? ? :title="value"
? ? ? ? :moreBtn="moreBtn"
? ? ? ? @more="onMore"/>
? ? </div>
</template>
<script>
import compB from './compB'
export default{
? ? name: 'compA',
? ? components:{
? ? ? ? compB
? ? },
? ? data(){
? ? ? ? return {
? ? ? ? ? ? value: '',
? ? ? ? ? ? moreBtn: true
? ? ? ? }
? ? },
? ? method:{
? ? ? ? onMore(value){
? ? ? ? ? ? console.log('value', value)
? ? ? ? ? ? //點(diǎn)擊查看更多按鈕
? ? ? ? }
? ? }
}
</script>
//子組件compB
<template>
? ? <div>
? ? ? ? <div class="title">{{value}}</div>
? ? ? ? <div v-if="moreBtn" @click="handleMore">查看更多</div>
? ? </div>
</template>
<script>
export default{
? ? name: 'compB',
? ? data(){
? ? ? ? return {
? ? ? ? }
? ? },
? ? props: {
? ? ? ? title: {
? ? ? ? ? ? type: String,
? ? ? ? ? ? default: '標(biāo)題'
? ? ? ? },
? ? ? ? moreBtn: {
? ? ? ? ? ? type: Boolean,
? ? ? ? ? ? default: false
? ? ? ? }
? ? }
? ? method:{
? ? ? ? handleMore(){
? ? ? ? ? ? this.$emit('more', '點(diǎn)擊查看更多按鈕')
? ? ? ? }
? ? }
}
</script>
props使得父子之間形成一種單向數(shù)據(jù)流幅虑,父元素更新的時候,子元素的狀態(tài)也會隨之改變顾犹。但反之會導(dǎo)致你的應(yīng)用的數(shù)據(jù)流向難以理解倒庵。
注意在 JavaScript 中對象和數(shù)組是通過引用傳入的,所以對于一個數(shù)組或?qū)ο箢愋偷?prop 來說炫刷,在子組件中改變這個對象或數(shù)組本身將會影響到父組件的狀態(tài)擎宝。
如果通過prop傳遞的數(shù)據(jù)是數(shù)組或者是對象,則是可以改變父對象的值浑玛,但是不推薦绍申。如果傳遞的是個字符串,還要改變父組件的值顾彰,那么Vue 會在瀏覽器的控制臺中發(fā)出警告极阅。
在某些情況下,需要子組件改變父組件的值涨享,推薦使用(2.3.0+)版本新增的.sync語法糖筋搏。
使用方法:
// A.vue
<template>
? ? <add-modal
? ? ? v-if="modalVisiable"
? ? ? :visiable.sync='modalVisiable'
? ? ? @submit="saveForm"
? ? />
</template>
<script>
export default {
? ? name: 'A',
? ? data(){
? ? ? ? return {
? ? ? ? ? ? modalVisiable: false
? ? ? ? }
? ? }
}
</script>
// B.vue
<template>
? ? <Modal
? ? ? v-model="show"
? ? ? width="600"
? ? ? title="彈框"
? ? ? :loading='true'
? ? ? @on-cancel="$emit('update:visiable', false)"
? ? >
? ? </Modal>
</template>
<script>
export default {
? ? name: 'A',
? ? data(){
? ? ? ? return {
? ? ? ? ? ? show: false,
? ? ? ? }
? ? },
? ? created(){
? ? ? ? this.show = this.visiable
? ? },
? ? props: {
? ? ? ? visiable: {
? ? ? ? ? type: Boolean,
? ? ? ? ? default: false
? ? ? ? }
? ? },
}
</script>
通過this.$emit('update:visiable', false)來改變父元素的狀態(tài)
2、$emit/$on
eventBus事件總線
描述:
這個方法是通過創(chuàng)建一個空的 vue 實(shí)例厕隧,當(dāng)做 $emit 事件的處理中心(事件總線)奔脐,通過他來觸發(fā)以及監(jiān)聽事件俄周,方便的實(shí)現(xiàn)了任意組件間的通信,包含父子髓迎,兄弟峦朗,隔代組件。
以下圖為例竖般,我們要實(shí)現(xiàn)的是A組件和C組件向B組件傳輸數(shù)據(jù)甚垦。
使用方法:
?
<template>
? <div>
? ? <div>我是父元素涣雕,我的num的值是:{{num}}</div>
? ? <a-comp :event="bus" :num.sync='num'></a-comp>
? ? <c-comp :event="bus" :num='num'></c-comp>
? ? <b-comp :event="bus"></b-comp>
? </div>
</template>
<script>
import Vue from 'vue';
import aComp from './a.vue';
import bComp from './b.vue';
import cComp from './c.vue';
// 創(chuàng)建一個空的vue實(shí)例
const bus = new Vue();
export default {
? 'name': 'example',
? data() {
? ? return {
? ? ? bus,
? ? ? 'num': 1
? ? };
? },
? 'components': {
? ? aComp,
? ? bComp,
? ? cComp
? }
};
</script>
?
<template>
? <div class="a-content">
? ? 我是組件A,
? ? <el-button type="primary" @click="send">點(diǎn)擊我向B發(fā)送東西</el-button>
? </div>
</template>
<script>
export default {
? 'name': 'aComp',
? 'props': [
? ? 'event',
? ? 'num'
? ],
? data() {
? ? return {'nb': 0};
? },
? created() {
? ? this.nb = this.num;
? },
? 'methods': {
? ? send() {
? ? ? this.nb = this.nb + 1;
? ? ? this.$emit('update:num', this.nb);
? ? ? // 通過$emit 來觸發(fā)phone-a事件
? ? ? this.event.$emit('phone-a', '我是組件A啊', this.nb);
? ? }
? }
};
</script>
?
<template>
? <div>
? ? 我是組件C迄埃,
? ? <el-button type="primary" @click="send">點(diǎn)擊我向B發(fā)送東西</el-button>
? </div>
</template>
<script>
export default {
? 'name': 'cComp',
? 'props': [
? ? 'event',
? ? 'num'
? ],
? data() {
? ? return {};
? },
? 'methods': {
? ? send() {
? ? ? console.log(this.num);
? ? ? this.event.$emit('phone-c', `我是組件C啊${this.num}`);
? ? }
? }
};
</script>
?
<template>
? <div class="b-content">
? ? <div>我是組件B侄非,我會接收組件A及組件C</div>
? ? <div>
? ? ? A: {{a}}
? ? </div>
? ? <div>
? ? ? B: {{c}}
? ? </div>
? </div>
</template>
<script>
export default {
? 'name': 'bComp',
? data() {
? ? return {
? ? ? 'a': '',
? ? ? 'c': ''
? ? };
? },
? 'props': ['event'],
? mounted() {
? ? // 通過$on來監(jiān)聽 phone-a 、phone-c 事件
? ? this.event.$on('phone-a', (a, num) => {
? ? ? this.a = `${a},我在改變父元素傳過來的值num: ${num}`;
? ? });
? ? this.event.$on('phone-c', c => {
? ? ? this.c = c;
? ? });
? }
};
</script>
其中最重要的是他們必須要在公共的實(shí)例中才能實(shí)現(xiàn)數(shù)據(jù)的互相傳遞流译。
3逞怨、provide / inject
描述:
這對選項(xiàng)需要一起使用,父組件使用 provide 向下提供數(shù)據(jù)福澡,其下所有子組件都可以通過inject注入叠赦。不管中間隔了多少代,都可以注入多個來自不同父級提供的數(shù)據(jù)
provide 選項(xiàng)是一個對象或返回一個對象的函數(shù)革砸。該對象包含可注入其子孫的屬性
inject 選項(xiàng)是一個字符串?dāng)?shù)組除秀,或一個對象
使用方法:
// 父組件
<template>
? <div>
? ? <com-a></com-a>
? </div>
</template>
<script>
import ComA from './a';
export default {
? 'name': 'home',
? 'components': {ComA},
? provide() {
? ? return {
? ? ? 'a': 'Hello',
? ? ? 'show': val => !val
? ? };
? }
};
</script>
// 子組件
<template>
? <div>
? ? <el-button @click="showFn(textShow)">點(diǎn)擊我切換下面文字展示及隱藏</el-button>
? ? <div v-if="textShow">我是一段隨意被操控的文字{{a}}</div>
? </div>
</template>
<script>
export default {
? 'name': 'ComA',
? data() {
? ? return {'textShow': true};
? },
? 'inject': [
? ? 'a',
? ? 'show'
? ],
? 'methods': {
? ? showFn(val) {
? ? ? this.textShow = this.show(val);
? ? }
? }
};
</script>
如果是在app.vue 文件(根文件)里面全局注冊信息,就可以在整個路由里面去引用
(類似于全局的數(shù)據(jù)管理vuex)
? ?
? ? <div>
? ? ? ? <router-view></router-view>
? ? </div>
</template>
<script>
export default {
? ? name: 'app',
? ? provide(){
? ? ? ? return {
? ? ? ? ? ? app: this
? ? ? ? }
? ? }
}
</script>
接下來任何組件只要通過inject注入app的話算利,都可以直接通過this.app.xx 來訪問app.vue 的所有實(shí)例册踩。
4、$parent / $children
描述:
$parent 可以用來從一個子組件訪問父組件的實(shí)例效拭。它提供了一種機(jī)會暂吉,可以在后期隨時觸達(dá)父級組件,來替代將數(shù)據(jù)以 prop 的方式傳入子組件的方式
?
? <div class="b-content">
? ? <div>我是子組件</div>
? ? <span>{{msgText}}</span>
? </div>
</template>
<script>
export default {
? 'name': 'childComp',
? data() {
? ? return {
? ? ? 'msgText': '',
? ? ? 'childMsg': '來自子元素的吶喊'
? ? };
? },
? created() {
? ? this.msgText = this.$parent.parentMsg;
? ? // MsgText: 來自父組件的呵呵
? }
};
</script>
$children可以遍歷全部子組件允耿,需要注意 $children 并不保證順序借笙,也不是響應(yīng)式的。
?
? <div class="b-content">
? ? <div>我是父組件</div>
? ? <child-comp></child-comp>
? </div>
</template>
<script>
import childComp from './child';
export default {
? 'name': 'parentComp',
? data() {
? ? return {'parentMsg': '來自父組件的呵呵'};
? },
? 'components': {childComp},
? mounted() {
? ? // 讀取子組件數(shù)據(jù),注意$children子組件的排序是不安全的
? ? console.log(this.$children[0].childMsg);
? ? // 來自子元素的吶喊
? }
};
</script>
5盗痒、$root & refs
描述:
$root 屬性是在每個 new Vue 實(shí)例的子組件中蚂蕴,其根實(shí)例可以通過 $root 屬性進(jìn)行訪問低散。例如,在這個根實(shí)例中:
// Vue 根實(shí)例
new Vue({
? data: {
? ? foo: 1
? },
? computed: {
? ? bar: function () { /* ... */ }
? },
? methods: {
? ? baz: function () { /* ... */ }
? }
})
所有的子組件都可以將這個實(shí)例作為一個全局 store 來訪問或使用骡楼。
// 獲取根組件的數(shù)據(jù)
this.$root.foo
// 寫入根組件的數(shù)據(jù)
this.$root.foo = 2
// 訪問根組件的計(jì)算屬性
this.$root.bar
// 調(diào)用根組件的方法
this.$root.baz()
$refs屬性當(dāng)你需要在 JavaScript 里直接訪問一個子組件熔号。你可以通過 ref 這個 attribute 為子組件賦予一個 ID 引用。 一個對象鸟整,持有注冊過 ref 特性 的所有 DOM 元素和組件實(shí)例引镊。例如:
<my-component ref="childrenCompA"></my-component>
訪問子組件:this.$refs.childrenCompA
6、Vuex
在做中大型的單頁應(yīng)用的時候篮条,例如需要多人協(xié)作開發(fā)弟头,全局維護(hù)登錄狀態(tài)等,我們可以選擇vuex來進(jìn)行狀態(tài)管理涉茧。
state里面是存儲在vuex里的數(shù)據(jù)赴恨,通過在根實(shí)例中注冊 store 選項(xiàng),該 store 實(shí)例會注入到根組件下的所有子組件中伴栓,且子組件能通過 this.$store 訪問到伦连。
當(dāng)需要獲取多個數(shù)據(jù)時,可以通過下面這種方式:
import {mapState} from'vuex'
computed:{? ?
...mapState('commissionSetting', ['listData'])
},
不是所有的狀態(tài)都適合放在vuex里面钳垮,有些狀態(tài)只屬于單個組件惑淳,所以還是要視情況來定。
mutations 更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation
const mutations = {?
????? updateListData(state, payload){? ?
????????? state.listData = payload?
?? ?? }
}
不能直接調(diào)用updateListData饺窿,需要以相應(yīng)的 type 調(diào)用 store.commit 方法:
store.commit('updateListData', data)復(fù)制代碼
actions 提交的是 mutation歧焦,而不是直接變更狀態(tài)。
Action 可以包含任意異步操作短荐。
寫一個簡單的action:
async getListAction({commit}, params){
? ? const result = await getList(params)
? ? const {code, message, data} = result
? ? if(code === SUCCESS && data){
? ? ? // 提交更改數(shù)據(jù)
? ? ? commit('updateListData', data.rows)
? ? }else{
? ? ? vueInit.$Message.error({
? ? ? ? content: message || '請您稍后再試~'
? ? ? });
? ? }
? ? return result
? },
Vuex 并不限制你的代碼結(jié)構(gòu)倚舀。但是,它規(guī)定了一些需要遵守的規(guī)則:
應(yīng)用層級的狀態(tài)應(yīng)該集中到單個 store 對象中忍宋。
提交 mutation 是更改狀態(tài)的唯一方法痕貌,并且這個過程是同步的。
異步邏輯都應(yīng)該封裝到 action 里面糠排。
只要你遵守以上規(guī)則舵稠,如何組織代碼隨你便。如果你的 store 文件太大入宦,只需將 action哺徊、mutation 和 getter 分割到單獨(dú)的文件。
作者:快狗打車前端團(tuán)隊(duì)
鏈接:https://juejin.im/post/5e5f7705f265da576f5310c7
來源:掘金
著作權(quán)歸作者所有乾闰。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)落追,非商業(yè)轉(zhuǎn)載請注明出處。