深入理解Vue 單向數(shù)據(jù)流的原理
單向數(shù)據(jù)流是什么
單向數(shù)據(jù)流指只能從一個(gè)方向來(lái)修改狀態(tài)。下圖是單向數(shù)據(jù)流的極簡(jiǎn)示意:
單向數(shù)據(jù)流的極簡(jiǎn)示意
與單向數(shù)據(jù)流對(duì)對(duì)應(yīng)的是雙向數(shù)據(jù)流(也叫雙向綁定)。在雙向數(shù)據(jù)流中,Model(可以理解為狀態(tài)的集合) 中可以修改自己或其他Model的狀態(tài), 用戶(hù)的操作(如在輸入框中輸入內(nèi)容)也可以修改狀態(tài)。這使改變一個(gè)狀態(tài)有可能會(huì)觸發(fā)一連串的狀態(tài)的變化,最后很難預(yù)測(cè)最終的狀態(tài)是什么樣的。使得代碼變得很難調(diào)試。如下圖所示:
與雙向數(shù)據(jù)流比,在單向數(shù)據(jù)流中,當(dāng)你需要修改狀態(tài),完全重新開(kāi)始走一個(gè)修改的流程。這限制了狀態(tài)修改的方式,讓狀態(tài)變得可預(yù)測(cè),容易調(diào)試。
單向數(shù)據(jù)流的使用場(chǎng)景
多個(gè)組件會(huì)共享狀態(tài)時(shí),共享狀態(tài)和組件間(兄弟組件)通信變的不容易。我們把共享狀態(tài)抽取出來(lái),用單向數(shù)據(jù)流的方式會(huì)變得容易。
從v-model開(kāi)始講起
1、v-model 用在 input 元素上
v-model在使用的時(shí)候很像雙向綁定的(實(shí)際上也是。。。),但是 Vue 是單項(xiàng)數(shù)據(jù)流,v-model 只是語(yǔ)法糖而已:
<input v-model="something" /> <input v-bind:value="something" v-on:input="something = $event.target.value" />
第一行的代碼其實(shí)只是第二行的語(yǔ)法糖。然后第二行代碼還能簡(jiǎn)寫(xiě)成這樣:
<input :value="something" @input="something = $event.target.value" />
要理解這行代碼,首先你要知道 input 元素本身有個(gè) oninput 事件,這是 HTML5 新增加的,類(lèi)似 onchange ,每當(dāng)輸入框內(nèi)容發(fā)生變化,就會(huì)觸發(fā) oninput ,通過(guò) $event 把最新的 value 傳遞給 something。
我們仔細(xì)觀察語(yǔ)法糖和原始語(yǔ)法那兩行代碼,可以得出一個(gè)結(jié)論: 在給 input 元素添加 v-model 屬性時(shí),默認(rèn)會(huì)把 value 作為元素的屬性,然后把 'input' 事件作為實(shí)時(shí)傳遞 value 的觸發(fā)事件
2、v-model 用在組件上
v-model 不僅僅能在 input 上用,在組件上也能使用,拿官網(wǎng)上的demo看。
<currency-input v-model="price"></currency-input> Vue.component('currency-input', { template: '\ <span>\ $\ <input\ ref="input"\ v-bind:value="value"\ v-on:input="updateValue($event.target.value)"\ >\ </span>\ ', props: ['value'], // 為什么這里要用 value 屬性,value在哪里定義的? methods: { // 不是直接更新值,而是使用此方法來(lái)對(duì)輸入值進(jìn)行格式化和位數(shù)限制 updateValue: function (value) { var formattedValue = value // 刪除兩側(cè)的空格符 .trim() // 保留 2 位小數(shù) .slice( 0, value.indexOf('.') === -1 ? value.length : value.indexOf('.') + 3 ) // 如果值尚不合規(guī),則手動(dòng)覆蓋為合規(guī)的值 if (formattedValue !== value) { this.$refs.input.value = formattedValue } // 通過(guò) input 事件帶出數(shù)值 // <!--為什么這里把 'input' 作為觸發(fā)事件的事件名?`input` 在哪定義的?--> this.$emit('input', Number(formattedValue)) } } })
如果你知道這兩個(gè)問(wèn)題的答案,那么恭喜你真正掌握了 v-model,如果你沒(méi)明白,那么可以看下這段代碼:
<currency-input v-model="price"></currency-input> 所以在組件中使用時(shí),它相當(dāng)于下面的簡(jiǎn)寫(xiě): //上行代碼是下行的語(yǔ)法糖 <currency-input :value="price" @input="price = arguments[0]"></currency-input>
所以,給組件添加 v-model 屬性時(shí),默認(rèn)會(huì)把 value 作為組件的屬性,然后把 'input' 值作為給組件綁定事件時(shí)的事件名。這在寫(xiě)組件時(shí)特別有用。
3、v-model 的缺點(diǎn)和解決辦法
在創(chuàng)建類(lèi)似復(fù)選框或者單選框的常見(jiàn)組件時(shí),v-model就不好用了。
<input type="checkbox" v-model="something" />
v-model 給我們提供好了 value 屬性和 oninput 事件,但是,我們需要的不是 value 屬性,而是 checked 屬性,并且當(dāng)你點(diǎn)擊這個(gè)單選框的時(shí)候不會(huì)觸發(fā) oninput 事件,它只會(huì)觸發(fā) onchange 事件。
因?yàn)?v-model 只是用到了 input 元素上,所以這種情況好解決:
<input type="checkbox" :checked="value" @change="change(value, $event)"
當(dāng) v-model 用到組件上時(shí):
<checkbox v-model="value"></checkbox> Vue.component('checkbox', { tempalte: '<input type="checkbox" @change="change" :checked="currentValue"/>' props: ['value'], data: function () { return { //這里為什么要定義一個(gè)局部變量,并用 prop 的值初始化它。 currentValue: this.value }; }, methods: { change: function ($event) { this.currentValue = $event.target.checked; this.$emit('input', this.currentValue); } })
在 Vue 2.2 版本,你可以在定義組件時(shí)通過(guò) model 選項(xiàng)的方式來(lái)定制 prop/event 。
4、vue 組件數(shù)據(jù)流
從上面 v-model 的分析我們可以這么理解,雙向數(shù)據(jù)綁定就是在單向綁定的基礎(chǔ)上給可輸入元素(input、textare等)添加了 change(input) 事件,來(lái)動(dòng)態(tài)修改 model 和 view ,即通過(guò)觸發(fā)($emit)父組件的事件來(lái)修改mv來(lái)達(dá)到 mvvm 的效果。而 vue 組件間傳遞數(shù)據(jù)是單向的,即數(shù)據(jù)總是由父組件傳遞到子組件,子組件在其內(nèi)部可以有自己維護(hù)的數(shù)據(jù),但它無(wú)權(quán)修改父組件傳遞給它的數(shù)據(jù),當(dāng)開(kāi)發(fā)者嘗試這樣做的時(shí)候,vue 將會(huì)報(bào)錯(cuò)。這樣做是為了組件間更好的解耦,在開(kāi)發(fā)中可能有多個(gè)子組件依賴(lài)于父組件的某個(gè)數(shù)據(jù),假如子組件可以修改父組件數(shù)據(jù)的話(huà),一個(gè)子組件變化會(huì)引發(fā)所有依賴(lài)這個(gè)數(shù)據(jù)的子組件發(fā)生變化,所以 vue 不推薦子組件修改父組件的數(shù)據(jù),直接修改 props 會(huì)拋出警告。流程圖如下:
所以,當(dāng)你想要在子組件去修改 props 時(shí),把這個(gè)子組件當(dāng)成父組件那樣用,所以就有了
1、定義一個(gè)局部變量,并用 prop 的值初始化它。
2、定義一個(gè)計(jì)算屬性,處理 prop 的值并返回。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Vue實(shí)現(xiàn)彈出框點(diǎn)擊空白頁(yè)彈框消失效果
這篇文章主要介紹了VUE實(shí)現(xiàn)彈出框點(diǎn)擊空白頁(yè)彈框消失,實(shí)現(xiàn)方法可以在Vue中實(shí)現(xiàn)彈出框然后通過(guò)點(diǎn)擊空白頁(yè)面來(lái)讓彈窗隱藏,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12Vue.directive 自定義指令的問(wèn)題小結(jié)
這篇文章主要介紹了Vue.directive 自定義指令的問(wèn)題小結(jié),需要的朋友可以參考下2018-03-03Vue跳轉(zhuǎn)頁(yè)面的幾種常用方法總結(jié)
在Vue.js中,頁(yè)面跳轉(zhuǎn)是構(gòu)建單頁(yè)面應(yīng)用(SPA)的基本操作之一,本文將介紹Vue中實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)的幾種方法,并通過(guò)實(shí)例代碼幫助理解每種方法的用法,需要的朋友可以參考下2024-09-09vue3.0中的雙向數(shù)據(jù)綁定方法及優(yōu)缺點(diǎn)
這篇文章主要介紹了vue3.0中的雙向數(shù)據(jù)綁定方法 ,文中通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08iview實(shí)現(xiàn)select tree樹(shù)形下拉框的示例代碼
這篇文章主要介紹了iview實(shí)現(xiàn)select tree樹(shù)形下拉框的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-12-12基于el-table實(shí)現(xiàn)行內(nèi)增刪改功能
這篇文章主要介紹了基于el-table實(shí)現(xiàn)行內(nèi)增刪改功能,用過(guò)通過(guò)操作按鈕點(diǎn)擊刪除或者編輯功能即可實(shí)現(xiàn)相應(yīng)的效果,下面小編給大家分享實(shí)例代碼感興趣的朋友跟隨小編一起看看吧2024-04-04vue2中provide/inject的使用與響應(yīng)式傳值詳解
Vue中 Provide/Inject實(shí)現(xiàn)了跨組件的通信,下面這篇文章主要給大家介紹了關(guān)于vue2中provide/inject的使用與響應(yīng)式傳值的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09vue3.2?Composition?API項(xiàng)目依賴(lài)升級(jí)
這篇文章主要為大家介紹了vue3.2?Composition?API項(xiàng)目依賴(lài)升級(jí)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08