Vue2的通信方式

Vue2.0 傳值方式:

在Vue的框架開發(fā)的項目過程中,經(jīng)常會用到組件來管理不同的功能侮腹,有一些公共的組件會被提取出來问顷。這時必然會產(chǎn)生一些疑問和需求?比如一個組件調(diào)用另一個組件作為自己的子組件冗荸,那么我們?nèi)绾芜M行給子組件進行傳值呢承璃?如果是電商網(wǎng)站系統(tǒng)的開發(fā),還會涉及到購物車的選項蚌本,這時候就會涉及到非父子組件傳值的情況盔粹。當(dāng)然你也可以用Vuex狀態(tài)管理工具來實現(xiàn),這部分我們后續(xù)會單獨介紹程癌。

引用官網(wǎng)的一句話:父子組件的關(guān)系可以總結(jié)為 prop 向下傳遞舷嗡,事件向上傳遞。父組件通過 prop 給子組件下發(fā)數(shù)據(jù)嵌莉,子組件通過事件給父組件發(fā)送消息进萄,如下圖所示:

接下來,我們通過實例來看可能會更明白一些:

1. 父組件向子組件進行傳值props

父組件:

? ? 父組件:



<!-- 引入子組件 -->

importchildfrom'./child'

exportdefault{

components: {

? ? ? child

? ? },

? ? data () {

return{

name:''

? ? ? }

? ? }

? }

子組件:

? ? 子組件:

{{inputName}}

exportdefault{

// 接受父組件的值

props: {

inputName:String,

required:true

? ? }

? }

2. 子組件向父組件傳值$emit,父組件通過子組件的ref屬性獲取值

子組件:

? ? 子組件:

{{childValue}}

<!-- 定義一個子組件傳值的方法 -->

exportdefault{

? ? data () {

return{

childValue:'我是子組件的數(shù)據(jù)'

? ? ? }

? ? },

methods: {

? ? ? childClick () {

// childByValue是在父組件on監(jiān)聽的方法

// 第二個參數(shù)this.childValue是需要傳的值

this.$emit('childByValue',this.childValue)

? ? ? }

? ? }

? }

父組件:

? ? 父組件:

{{name}}



<!-- 引入子組件 定義一個on的方法監(jiān)聽子組件的狀態(tài)-->

importchildfrom'./child'

exportdefault{

components: {

? ? ? child

? ? },

? ? data () {

return{

name:''

? ? ? }

? ? },

methods: {

childByValue:function(childValue){

// childValue就是子組件傳過來的值

this.name = childValue

? ? ? }

? ? }

? }


盡管有 prop 和事件锐峭,但是有時仍然需要在 JavaScript 中直接訪問子組件中鼠。

為此可以使用 ref 為子組件指定一個引用 ID。例如:

</div>

varparent=newVue({ el:'#parent'})

// 訪問子組件實例

varchild =parent.$refs.profile

當(dāng) ref 和 v-for 一起使用時沿癞,獲取到的引用會是一個數(shù)組援雇,包含和循環(huán)數(shù)據(jù)源對

應(yīng)的子組件。

$refs 只在組件渲染完成后才填充抛寝,并且它是非響應(yīng)式的熊杨。它僅僅是一個直接

操作子組件的

應(yīng)急方案——應(yīng)當(dāng)避免在模板或計算屬性中使用 $refs。

具體使用方法:

父組件

{{a}}

通過ref得到子組件的值

<!-- 使用組件 -->

importthreefrom'./components/three'

exportdefault{

components:{

? ? three,

? },

? data(){

return{

a:'我是父組件',

? ? }

? },

methods:{

? ? getchild(){

// 通過this.$refs調(diào)用

console.log(this.$refs.mesage.mes)

? ? }

? }

}

子組件:?

{{mes}}

exportdefault{

? data() {

return{

mes:'666'

? ? };

? }

};

3. 事件總線?$emit /$on


非父子組件之間傳值盗舰,需要定義個公共的公共實例文件bus.js晶府,作為中間倉庫來傳值,不然路由組件之間達不到傳值的效果钻趋。這種方法通過一個空的Vue實例作為中央事件總線(事件中心)川陆,用它來觸發(fā)事件和監(jiān)聽事件,巧妙而輕量地實現(xiàn)了任何組件間的通信,包括父子蛮位、兄弟较沪、跨級鳞绕。當(dāng)我們的項目比較大時,可以選擇更好的狀態(tài)管理解決方案vuex尸曼。

公共bus.js

//bus.js

importVuefrom'vue'

exportdefaultnewVue()

組件A:

? ? A組件:

{{elementValue}}

// 引入公共的bug们何,來做為中間傳達的工具

importBusfrom'./bus.js'

exportdefault{

? ? data () {

return{

elementValue:4

? ? ? }

? ? },

methods: {

elementByValue:function(){

Bus.$emit('val',this.elementValue)

? ? ? }

? ? }

? }

組件B:

? ? B組件:

{{name}}

importBusfrom'./bus.js'

exportdefault{

? ? data () {

return{

name:0

? ? ? }

? ? },

mounted:function(){

varvm =this

// 用$on事件來接收參數(shù)

Bus.$on('val',(data) =>{

console.log(data)

? ? ? ? vm.name = data

? ? ? })

? ? },

methods: {

getData:function(){

this.name++

? ? ? }

? ? }

? }

4.?利用vuex

(1)簡要介紹Vuex原理

Vuex實現(xiàn)了一個單向數(shù)據(jù)流,在全局擁有一個State存放數(shù)據(jù)控轿,當(dāng)組件要更改State中的數(shù)據(jù)時冤竹,必須通過Mutation進行,Mutation同時提供了訂閱者模式供外部插件調(diào)用獲取State數(shù)據(jù)的更新茬射。而當(dāng)所有異步操作(常見于調(diào)用后端接口異步獲取更新數(shù)據(jù))或批量的同步操作需要走Action鹦蠕,但Action也是無法直接修改State的,還是需要通過Mutation來修改State的數(shù)據(jù)在抛。最后钟病,根據(jù)State的變化,渲染到視圖上刚梭。

(2)簡要介紹各模塊在流程中的功能:

Vue Components:Vue組件肠阱。HTML頁面上,負責(zé)接收用戶操作等交互行為朴读,執(zhí)行dispatch方法觸發(fā)對應(yīng)action進行回應(yīng)辖所。

dispatch:操作行為觸發(fā)方法,是唯一能執(zhí)行action的方法磨德。

actions:操作行為處理模塊,由組件中的?$store.dispatch('action 名稱',data1)來觸發(fā)。然后由commit()來觸發(fā)mutation的調(diào)用 , 間接更新 state吆视。負責(zé)處理Vue Components接收到的所有交互行為典挑。包含同步/異步操作,支持多個同名方法啦吧,按照注冊的順序依次觸發(fā)您觉。向后臺API請求的操作就在這個模塊中進行,包括觸發(fā)其他action以及提交mutation的操作授滓。該模塊提供了Promise的封裝琳水,以支持action的鏈式觸發(fā)。

commit:狀態(tài)改變提交操作方法般堆。對mutation進行提交在孝,是唯一能執(zhí)行mutation的方法。

mutations:狀態(tài)改變操作方法淮摔,由actions中的?commit('mutation 名稱')來觸發(fā)私沮。是Vuex修改state的唯一推薦方法。該方法只能進行同步操作和橙,且方法名只能全局唯一仔燕。操作之中會有一些hook暴露出來造垛,以進行state的監(jiān)控等。

state:頁面狀態(tài)管理容器對象晰搀。集中存儲Vue components中data對象的零散數(shù)據(jù)五辽,全局唯一,以進行統(tǒng)一的狀態(tài)管理外恕。頁面顯示所需的數(shù)據(jù)從該對象中進行讀取杆逗,利用Vue的細粒度數(shù)據(jù)響應(yīng)機制來進行高效的狀態(tài)更新。

getters:state對象讀取方法吁讨。圖中沒有單獨列出該模塊髓迎,應(yīng)該被包含在了render中,Vue Components通過該方法讀取全局state對象建丧。

5.Vuex與localStorage

vuex 是 vue 的狀態(tài)管理器排龄,存儲的數(shù)據(jù)是響應(yīng)式的。但是并不會保存起來翎朱,刷新之后就回到了初始狀態(tài)橄维,具體做法應(yīng)該在vuex里數(shù)據(jù)改變的時候把數(shù)據(jù)拷貝一份保存到localStorage里面,刷新之后拴曲,如果localStorage里有保存的數(shù)據(jù)争舞,取出來再替換store里的state。


letdefaultCity ="北京"

try{

// 用戶關(guān)閉了本地存儲功能澈灼,此時在外層加個try...catch

if(!defaultCity){

defaultCity =JSON.parse(window.localStorage.getItem('defaultCity'))

? }

}catch(e){}

exportdefault

newVuex.Store({

state: {

city: defaultCity

? },

mutations: {

? ? changeCity(state, city) {

? ? ? state.city = city

try{

window.localStorage.setItem('defaultCity',JSON.stringify(state.city));

// 數(shù)據(jù)改變的時候把數(shù)據(jù)拷貝一份保存到localStorage里面

}catch(e) {}

? }

}

})

這里需要注意的是:由于vuex里竞川,我們保存的狀態(tài),都是數(shù)組叁熔,而localStorage只支持字符串委乌,所以需要用JSON轉(zhuǎn)換:

JSON.stringify(state.subscribeList);

// array -> string

JSON.parse(window.localStorage.getItem("subscribeList"));

// string -> array

6.$attrs /$listeners

1.簡介

多級組件嵌套需要傳遞數(shù)據(jù)時,通常使用的方法是通過vuex荣回。但如果僅僅是傳遞數(shù)據(jù)遭贸,而不做中間處理,使用 vuex 處理心软,未免有點大材小用壕吹。為此Vue2.4 版本提供了另一種方法----?$attrs/?$listeners

$attrs:包含了父作用域中不被 prop 所識別 (且獲取) 的特性綁定 (class 和 style 除外)。當(dāng)一個組件沒有聲明任何 prop 時删铃,這里會包含所有父作用域的綁定 (class 和 style 除外)耳贬,并且可以通過 v-bind="$attrs" 傳入內(nèi)部組件。通常配合 interitAttrs 選項一起使用泳姐。

$listeners:包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監(jiān)聽器效拭。它可以通過 v-on="$listeners" 傳入內(nèi)部組件接下來我們看個跨級通信的例子:

//index.vue

浪里行舟

importchildCom1from"./childCom1.vue"

exportdefault{

components: {childCom1},

? ? data (){

return{

foo:"javascript",

boo:"Html",

coo:"Css",

doo:"Vue"

? ? ? ? }

? ? }

}

//childCom1.vue

foo:{{foo}}

childCom1的$attrs:{{$attrs}}

importchildCom2from"./childCom2.vue";

exportdefault{

components:{ childCom2 },

inheritAttrs:false,//可以關(guān)閉自動掛載到組件跟元素上的沒有在props聲明的屬性

props: {foo:String},// foo作為props屬性綁定

created() {console.log(this.$attrs);// { "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "前端工匠" }

? }

}


//childCom2.vue

boo:{{boo}}

childCom2的$attrs:{{$attrs}}

importchildCom3from"./childCom3.vue";

exportdefault{

components:{ childCom3 },

inheritAttrs:false,//可以關(guān)閉自動掛載到組件跟元素上的沒有在props聲明的屬性

props: {foo:String},// foo作為props屬性綁定

created() {console.log(this.$attrs);// { "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "前端工匠" }

? }

}


// childCom3.vue

childCom3:{{$attrs}}

exportdefault{

props: {

coo:String,

title:String

? }

};


?$attrs表示沒有繼承數(shù)據(jù)的對象,格式為{屬性名:屬性值}。Vue2.4提供了?$attrs?,$listeners?來傳遞數(shù)據(jù)與事件缎患,跨級組件之間的通訊變得更簡單慕的。

簡單來說:?$attrs與?$listeners?是兩個對象,?$attrs?里存放的是父組件中綁定的非 Props 屬性挤渔,?$listeners里存放的是父組件中綁定的非原生事件肮街。

7.provide/inject

1.簡介

Vue2.2.0新增API,這對選項需要一起使用,以允許一個祖先組件向其所有子孫后代注入一個依賴判导,不論組件層次有多深嫉父,并在起上下游關(guān)系成立的時間里始終生效。一言而蔽之:祖先組件中通過provider來提供變量眼刃,然后在子孫組件中通過inject來注入變量绕辖。provide / inject API 主要解決了跨級組件間的通信問題,不過它的使用場景擂红,主要是子組件獲取上級組件的狀態(tài)仪际,跨級組件間建立了一種主動提供與依賴注入的關(guān)系

2.舉個例子

假設(shè)有兩個組件: A.vue 和 B.vue昵骤,B 是 A 的子組件

// A.vue

exportdefault{

provide: {name:'浪里行舟'}

}

// B.vue

exportdefault{

inject: ['name'],

? mounted () {

console.log(this.name);// 浪里行舟

? }

}

可以看到树碱,在 A.vue 里,我們設(shè)置了一個provide: name变秦,值為 浪里行舟成榜,它的作用就是將name這個變量提供給它的所有子組件。而在 B.vue 中蹦玫,通過?inject注入了從 A 組件中提供的name變量赎婚,那么在組件 B 中,就可以直接通過this.name訪問這個變量了樱溉,它的值也是 浪里行舟惑淳。這就是 provide / inject API 最核心的用法。

需要注意的是:provide 和 inject 綁定并不是可響應(yīng)的饺窿。這是刻意為之的。然而移斩,如果你傳入了一個可監(jiān)聽的對象肚医,那么其對象的屬性還是可響應(yīng)的----vue官方文檔 所以,上面 A.vue 的 name 如果改變了向瓷,B.vue 的?this.name?是不會改變的肠套,仍然是 浪里行舟。

3.provide與inject 怎么實現(xiàn)數(shù)據(jù)響應(yīng)式

一般來說猖任,有兩種辦法:

provide祖先組件的實例你稚,然后在子孫組件中注入依賴,這樣就可以在子孫組件中直接修改祖先組件的實例的屬性,不過這種方法有個缺點就是這個實例上掛載很多沒有必要的東西比如props刁赖,methods

使用2.6最新API Vue.observable 優(yōu)化響應(yīng)式 provide(推薦)

我們來看個例子:孫組件D搁痛、E和F獲取A組件傳遞過來的color值,并能實現(xiàn)數(shù)據(jù)響應(yīng)式變化宇弛,即A組件的color變化后鸡典,組件D、E枪芒、F不會跟著變(核心代碼如下:)


// A 組件

<div>

? ? ? <h1>A 組件</h1>

改變color

? ? ? <ChildrenB/><ChildrenC/>

</div>

......

data() {

return

? ? {

color:"blue"

? ? };

? },


// provide() {

//? return {

//? ? theme: {

//? ? ? color: this.color //這種方式綁定的數(shù)據(jù)并不是可響應(yīng)的

//? ? } // 即A組件的color變化后彻况,組件D、E舅踪、F不會跟著變

//? };

// },

? provide() {

return{

theme:this//方法一:提供祖先組件的實例

? ? };

? },

? methods: {

? ? changeColor(color) {?

if(color) {

this.color = color;

}else{

this.color =this.color ==="blue"?"red":"blue";

? ? }

? }

? }

// 方法二:使用vue2.6最新API Vue.observable 優(yōu)化響應(yīng)式 provide

// provide() {

//? this.theme = Vue.observable({

//? ? color: "blue"

//? });

//? return {

//? ? theme: this.theme

//? };

// },

// methods: {

//? changeColor(color) {

//? ? if (color) {

//? ? ? this.theme.color = color;

//? ? } else {

//? ? ? this.theme.color = this.theme.color === "blue" ? "red" : "blue";

//? ? }

//? }?

// }

// F 組件

F 組件

exportdefault

{

inject: {

theme: {

//函數(shù)式組件取值不一樣? ?

default:() =>({})

? }

? }

};

雖說provide 和 inject 主要為高階插件/組件庫提供用例纽甘,但如果你能在業(yè)務(wù)中熟練運用,可以達到事半功倍的效果抽碌!

8.$parent/$children&ref

ref:如果在普通的 DOM 元素上使用悍赢,引用指向的就是 DOM 元素;如果用在子組件上咬展,引用就指向組件實例

$parent/$children:訪問父 / 子實例

需要注意的是:這兩種都是直接得到組件實例泽裳,使用后可以直接調(diào)用組件的方法或訪問數(shù)據(jù)。我們先來看個用?ref來訪問組件的例子:

// component-a 子組件

exportdefault

{

? data () {?

return

? ? {

title:'Vue.js'

? ? }

? },

methods: {

? ? sayHello () {

window.alert('Hello');

? ? }

? }

}

// 父組件

<template>

</template>


exportdefault

{

? ? mounted () {? ?

constcomA =this.$refs.comA;

console.log(comA.title);// Vue.js

comA.sayHello();// 彈窗

? ? }

? }


不過破婆,這兩種方法的弊端是涮总,無法在跨級或兄弟間通信

// parent.vue

我們想在 component-a 中祷舀,訪問到引用它的頁面中(這里就是 parent.vue)的兩個 component-b 組件瀑梗,那這種情況下,就得配置額外的插件或工具了裳扯,比如 Vuex 和 Bus 的解決方案抛丽。

總結(jié)

常見使用場景可以分為三類:

父子通信: 父向子傳遞數(shù)據(jù)是通過 props,子向父是通過 events($emit)饰豺;通過父鏈 / 子鏈也可以通信($parent/$children)亿鲜;ref 也可以訪問組件實例;provide / inject API冤吨;$attrs/$listeners

兄弟通信: Bus蒿柳;Vuex

跨級通信: Bus;Vuex漩蟆;provide / inject API垒探、$attrs/$listeners

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市怠李,隨后出現(xiàn)的幾起案子圾叼,更是在濱河造成了極大的恐慌蛤克,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夷蚊,死亡現(xiàn)場離奇詭異构挤,居然都是意外死亡,警方通過查閱死者的電腦和手機撬码,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門儿倒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人呜笑,你說我怎么就攤上這事夫否。” “怎么了叫胁?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵凰慈,是天一觀的道長。 經(jīng)常有香客問我驼鹅,道長微谓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任输钩,我火速辦了婚禮豺型,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘买乃。我一直安慰自己姻氨,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布剪验。 她就那樣靜靜地躺著肴焊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪功戚。 梳的紋絲不亂的頭發(fā)上娶眷,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音啸臀,去河邊找鬼届宠。 笑死,一個胖子當(dāng)著我的面吹牛乘粒,可吹牛的內(nèi)容都是我干的席揽。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼谓厘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了寸谜?” 一聲冷哼從身側(cè)響起竟稳,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后他爸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體聂宾,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年诊笤,在試婚紗的時候發(fā)現(xiàn)自己被綠了系谐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡讨跟,死狀恐怖纪他,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情晾匠,我是刑警寧澤茶袒,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站凉馆,受9級特大地震影響薪寓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望隔躲。 院中可真熱鬧恕沫,春花似錦、人聲如沸埂蕊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽销睁。三九已至,卻和暖如春存崖,著一層夾襖步出監(jiān)牢的瞬間冻记,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工来惧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留冗栗,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓供搀,卻偏偏與公主長得像隅居,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子葛虐,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容