摘要:回顧上回提到,計(jì)算屬性的緩存與這個(gè)類的屬性有關(guān),那么這次我們接著來看下,屬性到底取決于什么情況來變化,從而對(duì)進(jìn)行緩存。
回顧
上回提到,computed————計(jì)算屬性的緩存與Watcher這個(gè)類的dirty屬性有關(guān),那么這次我們接著來看下,dirty屬性到底取決于什么情況來變化,從而對(duì)computed進(jìn)行緩存。依賴收集
切入正題之前,我們先來看一個(gè)問題:如果一個(gè)computed的結(jié)果是受data屬性下的值影響的,那么如何去捕獲因某個(gè)值變化而引起的computed的變化?答案是:依賴收集
根據(jù)上面的斷點(diǎn),在update函數(shù)執(zhí)行之前,我們注意到,有個(gè)reactiveSetter函數(shù)在它之前。我們點(diǎn)擊右側(cè)調(diào)用棧中的reactiveSetter,此時(shí)有一個(gè)函數(shù)特別醒目:defineReactive$$1,經(jīng)過又一次的斷點(diǎn),我們發(fā)現(xiàn)它在幾處都有調(diào)用:
在initRender函數(shù)中調(diào)用
在walk函數(shù)中調(diào)用
在實(shí)際斷點(diǎn)調(diào)試的時(shí)候,我們很容易可以知道存在這樣的,同時(shí)也是與本文有關(guān)的調(diào)用順序(從下往上):
defineReactive$$1
walk
Observer
observe
initData
initState
...
Observer類根據(jù)上邊提供的調(diào)用順序,我們重點(diǎn)看一下幾個(gè)關(guān)鍵的函數(shù):
/** * Attempt to create an observer instance for a value, * returns the new observer if successfully observed, * or the existing observer if the value already has one. */ function observe (value, asRootData) { if (!isObject(value) || value instanceof VNode) { return } var ob; if (hasOwn(value, "__ob__") && value.__ob__ instanceof Observer) { ob = value.__ob__; } else if ( shouldObserve && !isServerRendering() && (Array.isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue ) { ob = new Observer(value); } if (asRootData && ob) { ob.vmCount++; } return ob }
光看注釋我們都能知道,observe函數(shù)的作用是:為某個(gè)值創(chuàng)建一個(gè)observer實(shí)例,隨后將這個(gè)observer實(shí)例返回,在這里起到一個(gè)對(duì)值進(jìn)行篩選的作用
/** * Observer class that is attached to each observed * object. Once attached, the observer converts the target * object"s property keys into getter/setters that * collect dependencies and dispatch updates. */ var Observer = function Observer (value) { this.value = value; this.dep = new Dep(); this.vmCount = 0; def(value, "__ob__", this); if (Array.isArray(value)) { if (hasProto) { protoAugment(value, arrayMethods); } else { copyAugment(value, arrayMethods, arrayKeys); } this.observeArray(value); } else { this.walk(value); } };
注釋大意:
每個(gè)被觀察的對(duì)象都附屬于Observer類。每次對(duì)對(duì)象的觀察都會(huì)將它的 getter和setter屬性覆蓋,用以收集依賴以及觸發(fā)更新
Observer.prototype.walk = function walk (obj) { var keys = Object.keys(obj); for (var i = 0; i < keys.length; i++) { defineReactive$$1(obj, keys[i]); } }; /** * Define a reactive property on an Object. */ function defineReactive$$1 ( obj, key, val, customSetter, shallow ) { var dep = new Dep(); var property = Object.getOwnPropertyDescriptor(obj, key); if (property && property.configurable === false) { return } // cater for pre-defined getter/setters var getter = property && property.get; var setter = property && property.set; if ((!getter || setter) && arguments.length === 2) { val = obj[key]; } var childOb = !shallow && observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { var value = getter ? getter.call(obj) : val; if (Dep.target) { dep.depend(); if (childOb) { childOb.dep.depend(); if (Array.isArray(value)) { dependArray(value); } } } return value }, set: function reactiveSetter (newVal) { var value = getter ? getter.call(obj) : val; /* eslint-disable no-self-compare */ if (newVal === value || (newVal !== newVal && value !== value)) { return } /* eslint-enable no-self-compare */ if (process.env.NODE_ENV !== "production" && customSetter) { customSetter(); } // #7981: for accessor properties without setter if (getter && !setter) { return } if (setter) { setter.call(obj, newVal); } else { val = newVal; } childOb = !shallow && observe(newVal); dep.notify(); } }); }
其中,這端代碼是關(guān)鍵:
get: function reactiveGetter () { var value = getter ? getter.call(obj) : val; if (Dep.target) { dep.depend(); if (childOb) { childOb.dep.depend(); if (Array.isArray(value)) { dependArray(value); } } } return value },
如果閱讀了整段defineReactive$$1函數(shù),那么很容易就發(fā)現(xiàn),dep不過是Dep類new出來的實(shí)例,那么即使不看Dep.prototype.depend的實(shí)現(xiàn),你也知道dep.depend()其實(shí)也就是在收集依賴。
另外,這段代碼意味著單單在data屬性下聲明一個(gè)變量是不會(huì)進(jìn)行依賴收集的,需要變量在程序中被調(diào)用,那么才會(huì)被收集到依賴中(其實(shí)這也是一種優(yōu)化)
/** * A dep is an observable that can have multiple * directives subscribing to it. */ var Dep = function Dep () { this.id = uid++; this.subs = []; }; Dep.prototype.addSub = function addSub (sub) { this.subs.push(sub); }; Dep.prototype.removeSub = function removeSub (sub) { remove(this.subs, sub); }; Dep.prototype.depend = function depend () { if (Dep.target) { Dep.target.addDep(this); } }; Dep.prototype.notify = function notify () { // stabilize the subscriber list first var subs = this.subs.slice(); if (process.env.NODE_ENV !== "production" && !config.async) { // subs aren"t sorted in scheduler if not running async // we need to sort them now to make sure they fire in correct // order subs.sort(function (a, b) { return a.id - b.id; }); } for (var i = 0, l = subs.length; i < l; i++) { subs[i].update(); } };總結(jié)
上面說了這么多未免有點(diǎn)亂,最后重新梳理下computed實(shí)現(xiàn)緩存的思路:
Vue在初始化data屬性時(shí),會(huì)將data屬性下相關(guān)的變量進(jìn)行觀察(observe),同時(shí)重新設(shè)置它的getter和setter屬性,以便在其被調(diào)用時(shí)收集到它的依賴。
初始化computed
調(diào)用computed時(shí)判斷this.dirty屬性,為true時(shí)調(diào)用evaluate重新計(jì)算它的值并將this.dirty置為false,將值存在this.value?上,再調(diào)用computed則直接返回this.value
當(dāng)computed中依賴的值發(fā)生變化時(shí)會(huì)自動(dòng)觸發(fā)該值的setter屬性,緊接著調(diào)用notify函數(shù),遍歷一個(gè)subs數(shù)組,觸發(fā)update函數(shù)將this.dirty重置為true
當(dāng)computed再次被調(diào)用時(shí),由于this.dirty已經(jīng)是true,則會(huì)重新計(jì)算
掃描下方的二維碼或搜索「tony老師的前端補(bǔ)習(xí)班」關(guān)注我的微信公眾號(hào),那么就可以第一時(shí)間收到我的最新文章。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/102605.html
摘要:官方定義類型詳細(xì)計(jì)算屬性將被混入到實(shí)例中。所有和的上下文自動(dòng)地綁定為實(shí)例計(jì)算屬性的結(jié)果會(huì)被緩存,除非依賴的響應(yīng)式屬性變化才會(huì)重新計(jì)算。注意,如果某個(gè)依賴比如非響應(yīng)式屬性在該實(shí)例范疇之外,則計(jì)算屬性是不會(huì)被更新的。 官方定義 類型:{ [key: string]: Function | { get: Function, set: Function } } 詳細(xì):計(jì)算屬性將被混入到 V...
摘要:前言上回我們提到,在子組件存在的情況下,父組件在執(zhí)行完鉤子函數(shù)之后生成子組件的實(shí)例,子組件執(zhí)行鉤子函數(shù),同時(shí)也檢查是否也有子組件,有則重復(fù)父組件的步驟,否則子組件的元素渲染深入了解在上一篇文章中其實(shí)我們提到一個(gè)函數(shù) 前言 上回我們提到,在子組件存在的情況下,父組件在執(zhí)行完created鉤子函數(shù)之后生成子組件的實(shí)例,子組件執(zhí)行created鉤子函數(shù),同時(shí)也檢查是否也有子組件,有則重復(fù)父組...
摘要:哪吒別人的看法都是狗屁,你是誰只有你自己說了才算,這是爹教我的道理。哪吒去他個(gè)鳥命我命由我,不由天是魔是仙,我自己決定哪吒白白搭上一條人命,你傻不傻敖丙不傻誰和你做朋友太乙真人人是否能夠改變命運(yùn),我不曉得。我只曉得,不認(rèn)命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出處 查看github最新的Vue...
摘要:由于工作中經(jīng)常使用調(diào)試工具來定位問題,覺著這東西真的挺好用。突然有一天受到啟發(fā),想著我學(xué)習(xí)源碼是否也可以通過調(diào)試工具呢因此,誕生了這篇文章來記錄我的一些學(xué)習(xí)成果,后續(xù)應(yīng)該會(huì)寫成一個(gè)系列。 由于工作中經(jīng)常使用chrome調(diào)試工具來定位問題,覺著這東西真的挺好用。突然有一天受到啟發(fā),想著:我學(xué)習(xí)源碼是否也可以通過調(diào)試工具呢? 因此,誕生了這篇文章來記錄我的一些學(xué)習(xí)成果,后續(xù)應(yīng)該會(huì)寫成一個(gè)...
摘要:根據(jù)調(diào)試工具看源碼之組件通信一根據(jù)調(diào)試工具看源碼之組件通信一在平時(shí)的業(yè)務(wù)開發(fā)中,相信在座的各位沒少用過組件通信。看完本文可以幫助你了解組件的通信方式及原理,從而進(jìn)一步加深對(duì)的理解,遠(yuǎn)離工程師的行列。 根據(jù)調(diào)試工具看Vue源碼之組件通信(一)## 根據(jù)調(diào)試工具看Vue源碼之組件通信(一) 在平時(shí)的業(yè)務(wù)開發(fā)中,相信在座的各位沒少用過組件通信。然而,對(duì)于一些新手/業(yè)務(wù)熟手來說,不懂技術(shù)原理往...
閱讀 3320·2021-11-12 10:36
閱讀 2477·2021-11-02 14:43
閱讀 2151·2019-08-30 14:23
閱讀 3467·2019-08-30 13:08
閱讀 924·2019-08-28 18:09
閱讀 3136·2019-08-26 12:22
閱讀 3147·2019-08-23 18:24
閱讀 2021·2019-08-23 18:17