摘要:響應(yīng)式原理為了探究這一切的原因,我再次點開了的官網(wǎng)。在官網(wǎng)很下面的位置,找到了關(guān)于響應(yīng)式原理的說明。因此,新添加到數(shù)組中的對象中的屬性,就成了非響應(yīng)式的屬性了,改變它自然不會讓組件重新渲染。響應(yīng)式屬性的對象,有這個對象就代表是響應(yīng)式的。
??最近在用Vue開發(fā)一個后臺管理的demo,有一個非常常規(guī)的需求。然而這個常規(guī)的需求中,包含了大量的知識點。有一個產(chǎn)品表格,用來顯示不同產(chǎn)品的信息。然后表格要有一個內(nèi)嵌編輯的功能,點擊操作欄的編輯按鈕,對應(yīng)行的信息列就變成輸入框。第一版的代碼大致上像這樣。
{{scope.row.description}} 編輯
??邏輯很簡單,我在表格數(shù)據(jù)數(shù)組中,給每一個對象都加入一個初始值為false的屬性"edit",然后根據(jù)這個屬性的值,使用v-show來決定渲染的是文本還是輸入框,是“編輯”還是“保存”。
??然而運行起來之后的表現(xiàn)并不是像我想的一樣,事實上,點擊編輯按鈕后,對應(yīng)產(chǎn)品的“產(chǎn)品描述”并沒有變成輸入框,編輯按鈕也沒有變成保存按鈕。而我通過vue-devtool查看數(shù)據(jù)發(fā)現(xiàn),事實上對應(yīng)的edit屬性確實已經(jīng)變了,只是頁面上的組件沒有正確渲染。這讓我很困惑,說好的雙向綁定呢,為什么model層上的變化沒有響應(yīng)到view層上呢。
??首先,由于頁面初始顯示是正確的,把edit的初始值改成true后,也會有輸入框出現(xiàn),所以肯定不是代碼邏輯的問題。當我試著把v-show的判斷條件改成數(shù)組中的對象原本就有的屬性時,發(fā)現(xiàn)編輯狀態(tài)的切換突然變得正常了。而一旦我把判斷條件改回后來插入的edit時,一切又變得不正常了。因此我推測,一定是數(shù)據(jù)綁定出了什么問題。
??我在網(wǎng)上查了一下,有些類似的問題,大多數(shù)的解決方案是,給el-table加上一個隨機數(shù)key值:key="Math.random()"。試了一下,發(fā)現(xiàn)真的有用。之所以有用是因為,每次對這個表格有操作,key值都會變,這就相當于產(chǎn)生了一個新的table,瀏覽器就會根據(jù)model層的數(shù)據(jù)重新渲染,這時候顯示當然就正確了。但可想而知,這樣也會造成極大的性能浪費,而且這也沒有解決數(shù)據(jù)綁定的問題。
??我又試著對代碼做了一些修改。我把map和賦值操作放到了同一句里面去,代碼變成了這樣
this.$store.dispatch(GET_PRODUCTS).then(() => { this.products = this.$store.getters.products.map((item: any) => { item.edit = false; return item; }); });
神奇的事發(fā)生了,居然一切都恢復(fù)正常了。那么我就知道了,問題出在了數(shù)組和map函數(shù)上。
響應(yīng)式原理??為了探究這一切的原因,我再次點開了Vue的官網(wǎng)。在官網(wǎng)很下面的位置,找到了關(guān)于響應(yīng)式原理的說明。這張圖很好地說明了Vue實現(xiàn)雙向綁定的原理。
??當一個javscript對象傳入Vue實例的data中時,Vue會遍歷該對象的所有屬性,同時使用?Object.defineProperty方法將這些屬性全都轉(zhuǎn)成?getter/setter每個組件實例都對應(yīng)一個watcher實例,它會在組件渲染的過程中把“接觸”過的數(shù)據(jù)屬性記錄為依賴。之后當依賴項的數(shù)據(jù)發(fā)生變化,也就是setter觸發(fā)時,會通知watcher,從而使它關(guān)聯(lián)的組件重新渲染。
??而由于javascript的限制,Vue不能檢測到對象的添加或者刪除。并且Vue在初始化實例時就對屬性執(zhí)行了setter/getter轉(zhuǎn)化過程,所以屬性必須開始就在對象上,這樣才能讓Vue轉(zhuǎn)化它。而動態(tài)添加的根級別的屬性,則不會轉(zhuǎn)化成響應(yīng)式的屬性。也就是說,往已經(jīng)創(chuàng)建的實例上添加的根級別的屬性,都會是非響應(yīng)式的。但是,可以使用 Vue.set(object, propertyName, value) 或者vm.$set(object, propertyName, value)方法向嵌套對象添加響應(yīng)式屬性。
??這里,數(shù)組相關(guān)的注意事項被額外提了出來。由于 JavaScript 的限制,Vue 不能檢測以下數(shù)組的變動:
當你利用索引直接設(shè)置一個數(shù)組項時,例如:vm.items[indexOfItem] = newValue
當你修改數(shù)組的長度時,例如:vm.items.length = newLength
??解決方法也很簡單,使用上面提到的set方法就可以解決這個問題。與此同時,官網(wǎng)上還有一段專門針對數(shù)組的變異方法的說明。
??所謂的變異方法,顧名思義,會改變調(diào)用了這些方法的原始數(shù)組。相比之下,也有非變異 (non-mutating method) 方法,例如 filter()、concat() 和 slice() 。它們不會改變原始數(shù)組,而總是返回一個新數(shù)組。當使用非變異方法時,可以用新數(shù)組替換舊數(shù)組。并且,Vue還非常智能的會對于沒有變化的dom進行重用,并不會整個進行更新。
??看到這兒,我終于找到問題的關(guān)鍵在哪兒了。其實網(wǎng)上的各種說法都不準確,真正出問題的點在于map函數(shù)的使用上。map是一個非變異方法,方法本身并不會改變原數(shù)組,而是會返回一個新數(shù)組。因此,Vue并沒有對map方法進行包裝,而是建議替換原數(shù)組。然而我在用的時候并沒有注意到這一點,在使用的時候利用指針特性,把map方法當做變異方法來用,直接改變原數(shù)組,這自然就不會被Vue檢測到了。因此,新添加到數(shù)組中的對象中的edit屬性,就成了非響應(yīng)式的屬性了,改變它自然不會讓組件重新渲染。
原理都已經(jīng)搞清楚了,接下來我總結(jié)了一下這類數(shù)組問題的幾種解決方法。
??在el-table標簽上添加:key="Math.random()",不管做了什么,都強制刷新整個表格,非常不推薦,極大的性能消耗。
??在使用數(shù)組方法的時候,分清變異方法和非變異方法,用非變異方法的時候,要用新數(shù)組替代舊數(shù)組,而不是直接變換原數(shù)組。
??我在"vue/src/core/observer/index.js"中找到了set方法的源碼。我們發(fā)現(xiàn)set函數(shù)接收三個參數(shù)分別為 target、key、val,其中target的值為數(shù)組或者對象,這正好和官網(wǎng)給出的調(diào)用Vue.set()方法時傳入的參數(shù)參數(shù)對應(yīng)上。然后往下看實現(xiàn),我基本上給每一行都加上了注釋。
export function set (target: Array| Object, key: any, val: any): any { if (process.env.NODE_ENV !== "production" && (isUndef(target) || isPrimitive(target)) ) {//判斷target的類型是否符合要求,若不符合要求,且不在生產(chǎn)環(huán)境下,就拋出警告。 warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`) } if (Array.isArray(target) && isValidArrayIndex(key)) {//如果target是數(shù)組,且key值合法 target.length = Math.max(target.length, key) target.splice(key, 1, val)//用包裝好的變異方法splice進行賦值。 return val } if (key in target && !(key in Object.prototype)) {//如果key是target中原有的屬性,就直接賦值。 target[key] = val return val } const ob = (target: any).__ob__//響應(yīng)式屬性的observer對象,有這個對象就代表是響應(yīng)式的。 if (target._isVue || (ob && ob.vmCount)) {//如果當前的target對象是vue實例對象或者是根數(shù)據(jù)對象,就拋出警告。 process.env.NODE_ENV !== "production" && warn( "Avoid adding reactive properties to a Vue instance or its root $data " + "at runtime - declare it upfront in the data option." ) return val } if (!ob) {//如果不存在observer,那就不是響應(yīng)式對象,直接賦值。 target[key] = val return val } defineReactive(ob.value, key, val)//給新屬性添加依賴,以后直接修改屬性就能重新渲染。 ob.dep.notify()//直接觸發(fā)依賴。 return val }
可以看到,set方法對于數(shù)組的處理其實非常簡單,就是調(diào)用了包裝好的splice方法。那么再來看一下包裝Array變異方法的代碼實現(xiàn),我同樣給每一行加上了注釋。其實做的事情也不多,主要就是給每個新添加的元素都加上觀察者。
... methodsToPatch.forEach(function (method) { // cache original method const original = arrayProto[method]//保存原方法。 def(arrayMethods, method, function mutator (...args) {//修改方法映射,調(diào)用數(shù)組方法的時候?qū)嶋H上調(diào)用的是對應(yīng)的mutator方法。 const result = original.apply(this, args)//調(diào)用原方法,先把結(jié)果求出來 const ob = this.__ob__//獲取observer let inserted switch (method) { case "push": case "unshift": inserted = args break case "splice": inserted = args.slice(2) break }//對于往數(shù)組中加元素的方法,獲得添加的元素。 if (inserted) ob.observeArray(inserted)//給添加的元素添加觀察者。 // notify change ob.dep.notify()//觸發(fā)依賴。 return result }) })
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/105707.html
摘要:響應(yīng)式原理之不論如何,最終響應(yīng)式數(shù)據(jù)都要通過來實現(xiàn),實際要借助新增的。在函數(shù)內(nèi),首先實例化一個實例,會在稍后添加為響應(yīng)式數(shù)據(jù)自定義的中發(fā)揮作用。只有數(shù)組和對象才可能是響應(yīng)式,才能返回實例。參考鏈接技術(shù)內(nèi)幕揭開數(shù)據(jù)響應(yīng)系統(tǒng)的面紗源碼 Vue響應(yīng)式原理之defineReactive defineReactive 不論如何,最終響應(yīng)式數(shù)據(jù)都要通過defineReactive來實現(xiàn),實際要借助...
摘要:響應(yīng)式原理之之前簡單介紹了和類的代碼和作用,現(xiàn)在來介紹一下類和。對于數(shù)組,響應(yīng)式的實現(xiàn)稍有不同。不存在時,說明不是響應(yīng)式數(shù)據(jù),直接更新。如果對象是響應(yīng)式的,確保刪除能觸發(fā)更新視圖。 Vue響應(yīng)式原理之Observer 之前簡單介紹了Dep和Watcher類的代碼和作用,現(xiàn)在來介紹一下Observer類和set/get。在Vue實例后再添加響應(yīng)式數(shù)據(jù)時需要借助Vue.set/vm.$se...
摘要:淺析響應(yīng)式原理一的特點之一是響應(yīng)式,視圖隨著數(shù)據(jù)的更新而更新,在視圖中修改數(shù)據(jù)后實例中的數(shù)據(jù)也會同步更新。對于每個響應(yīng)式數(shù)據(jù),會有兩個實例,第一個是在中的閉包遍歷,用途顯而易見。接收一個回調(diào)函數(shù),會在重新求值且值更新后執(zhí)行。 淺析Vue響應(yīng)式原理(一) Vue的特點之一是響應(yīng)式,視圖隨著數(shù)據(jù)的更新而更新,在視圖中修改數(shù)據(jù)后Vue實例中的數(shù)據(jù)也會同步更新。內(nèi)部借助依賴(下文中的Dep類)...
摘要:前言最近在學(xué)習(xí)計算屬性的源碼,發(fā)現(xiàn)和普通的響應(yīng)式變量內(nèi)部的實現(xiàn)還有一些不同,特地寫了這篇博客,記錄下自己學(xué)習(xí)的成果文中的源碼截圖只保留核心邏輯完整源碼地址可能需要了解一些響應(yīng)式的原理版本計算屬性的概念一般的計算屬性值是一個函數(shù),這個函數(shù)showImg(https://user-gold-cdn.xitu.io/2019/5/6/16a8b98f1361f6f6); 前言 最近在學(xué)習(xí)Vue計...
摘要:淺析的特點之一就是響應(yīng)式,但數(shù)據(jù)更新時,并不會立即更新。盡管已經(jīng)更新,但新增的元素并不立即插入到中。實際在中,執(zhí)行了,這也是自動綁定到執(zhí)行上下文的原因。在內(nèi),使用數(shù)組保存回調(diào)函數(shù),表示當前狀態(tài),使用函數(shù)來執(zhí)行回調(diào)隊列。 Vue.nextTick 淺析 Vue 的特點之一就是響應(yīng)式,但數(shù)據(jù)更新時,DOM 并不會立即更新。當我們有一個業(yè)務(wù)場景,需要在 DOM 更新之后再執(zhí)行一段代碼時,可以...
閱讀 917·2021-10-18 13:32
閱讀 3513·2021-09-30 09:47
閱讀 2155·2021-09-23 11:21
閱讀 1878·2021-09-09 09:34
閱讀 3479·2019-08-30 15:43
閱讀 1522·2019-08-30 11:07
閱讀 1061·2019-08-29 16:14
閱讀 724·2019-08-29 11:06