摘要:中小秘密的發(fā)現(xiàn)之旅首先我們看一段代碼請問會間隔的打印出來嗎中去掉,再問會間隔的打印出來嗎如果第二問沒有打印出來,那么在第二問的基礎上怎么修改才能再次打印出來呢我先來揭曉答案會打印出來不會打印出來可以用過添加監(jiān)聽,來打印請問為什么呢以下是我的
vue中computed小秘密的發(fā)現(xiàn)之旅 首先我們看一段代碼
請問{{ count }}
console.log(1)會間隔的打印出來嗎?
html中去掉{{ count }},再問console.log(1)會間隔的打印出來嗎?
如果第二問沒有打印出來,那么在第二問的基礎上怎么修改才能再次打印出來呢?
我先來揭曉答案會打印出來
不會打印出來
可以用過添加watch監(jiān)聽count,來打印`console.log(1)
watch: { count: function (oldValue, newValue) { } }請問為什么呢?
以下是我的理解,有誤還請指出,共同進步
一句話總結就是computed是惰性求值,在new watcher時是計算屬性時,this.value=undefined所以一開始不會觸發(fā)get進行依賴收集即僅僅定義computed的話是沒有進行計算屬性count的依賴收集(可以類似看成data中的數(shù)值,僅僅進行了響應式get,set的定義,并沒有觸發(fā)dep.depend,所以當值發(fā)生變化的時候,他并不知道要通知誰,也就不會執(zhí)行相應的回調(diào)函數(shù)了)
源碼中有這么一段:
depend () { if (this.dep && Dep.target) { //因為惰性求值,所以Dep.target為false this.dep.depend() } }
所以如果僅僅是computed的初始化的話并Dep.target就是undefined,所以實例化的watch并不會加入dep的中
看看Computed的實現(xiàn)computed初始化
function initComputed (vm: Component, computed: Object) { const watchers = vm._computedWatchers = Object.create(null) //(標記1)新建一個沒有原型鏈的對象,用來存`computed`對象每個值的watch實例對象 const isSSR = isServerRendering() //與服務端渲染有關,暫時忽略 for (const key in computed) { const userDef = computed[key] //取key的值,該值大部分是function類型 //下面主要作用就是在非生產(chǎn)環(huán)境中沒有getter,保警告 const getter = typeof userDef === "function" ? userDef : userDef.get if (process.env.NODE_ENV !== "production" && getter == null) { warn( `Getter is missing for computed property "${key}".`, vm ) } } if (!isSSR) { //computed中不同的key,也就是計算屬性生成watch實例, //watch作用:簡單看就是當值發(fā)生變化時會觸通知到watch,觸發(fā)更新,執(zhí)行回調(diào)函數(shù) watchers[key] = new Watcher( vm, getter || noop, noop, computedWatcherOptions ) } if (!(key in vm)) { //作用是將{key: userDef}變成響應式,重寫其get和set defineComputed(vm, key, userDef) } else if (process.env.NODE_ENV !== "production") { if (key in vm.$data) { warn(`The computed property "${key}" is already defined in data.`, vm) } else if (vm.$options.props && key in vm.$options.props) { warn(`The computed property "${key}" is already defined as a prop.`, vm) } } }
defineComputed 先看這個函數(shù)做了什么
const sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop } export function defineComputed ( target: any, key: string, userDef: Object | Function ) { const shouldCache = !isServerRendering() if (typeof userDef === "function") { sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : userDef sharedPropertyDefinition.set = noop } else { sharedPropertyDefinition.get = userDef.get ? shouldCache && userDef.cache !== false ? createComputedGetter(key) : userDef.get : noop sharedPropertyDefinition.set = userDef.set ? userDef.set : noop } Object.defineProperty(target, key, sharedPropertyDefinition) }
上面函數(shù)的作用就是改寫get與set,關鍵就是這個createComputedGetter在做什么?
早版本createComputedGetter的實現(xiàn)是:
function createComputedGetter(){ return function computedGetter () { //這個就是之前用來收集watch實例的一個對象,可看注釋:標記1 const watcher = this._computedWatchers && this._computedWatchers[key] if(watcher) { if(watcher.dirty) { watcher.evaluate() } if(Dep.target){ //這里也可以看出Dep.target為false時是不會觸發(fā)depend,即添加依賴 watcher.depend() } return watcher.value } } }重點看看watch
export default class Watcher { constructor ( vm: Component, expOrFn: string | Function, cb: Function, options?: ?Object, isRenderWatcher?: boolean ) { //進行初始化的定義,忽略無關代碼 if(options) { this.lazy = !!options.lazy }else { this.lazy = false } this.getter = parsePath(expOrFn) //返回一個取data值得函數(shù) this.dirty = this.lazy //true this.value = this.lazy ? undefined : this.get() //undefined,當不會執(zhí)行get時也就不會觸發(fā)get實例方法中的depend的了 } get () { // 偽代碼 Dep.target = this //取值也就是訪問觸發(fā)屬性的get,get中又觸發(fā)dep.depend(),而dep.depend內(nèi)部觸發(fā)的是Dep.target.addDep(this),這里的this其實是Dep實例 let value = this.getter.call(vm, vm) Dep.target = undefined } addDep (dep: Dep) { //偽代碼 const id = dep.id if(!this.depIds.has(id)) { this.depIds.add(id) this.deps.push(dep) dep.addSub(this) //this是watch實例對象 } } update () { // 省略... } getAndInvoke (cb: Function) { // 省略... } evaluate () { this.value = this.get() this.dirty = false } depend () { let i = this.deps.length while(i --) { this.deps[i].depend() } } ... }
總結: 1.watcher.dirty默認為true,執(zhí)行watcher.evaluate()所以computed第一次默認會渲染,與watch不同;2.當默認渲染,觸發(fā)了get,Dep.target就不是false,就會執(zhí)行watcher.depend()
watcher.depend() 早版的實現(xiàn),它有什么問題this.dep這個數(shù)組中元素都是Dep的實例對象,watcher所依賴的所有Dep實例化列表;
舉個例子:當計算屬性中return this.num + this.num1,當讀取計算屬性時會分別觸發(fā)num與num1的get,get中又觸發(fā)dep.depend(),而dep.depend內(nèi)部觸發(fā)的是Dep.target.addDep(this),這里的this其實是Dep實例,這樣就會分別將不同編號的num與num1的dep,加入到deps中,最后將計算屬性的依賴加入到num,num1的Dep中,this.deps[i].depend()也會加,但之前已加入改id所以猜測會直接return掉
這樣當num發(fā)生改變,觸發(fā)set,觸發(fā)其notify 方法即遍歷dep.subDeps數(shù)組(subDeps中放的是各種依賴),觸發(fā)依賴的update方法。但之前的update方法看了一下
update () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true } else if (this.sync) { this.run() } else { queueWatcher(this) } } }
可以看出直接走queueWatcher(this)所以就算內(nèi)容沒有變化,也會走渲染流程,這就造成了浪費
新版本,發(fā)生了變化第一個createComputedGetter
function createComputedGetter (key) { return function computedGetter () { const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { watcher.depend() return watcher.evaluate() } } }
第二個watcher.depend()
if (this.dep && Dep.target) { this.dep.depend() } }
上面這里的dep又是哪里來的呢?在watch類中加了下面代碼
if (this.computed) { this.value = undefined this.dep = new Dep() //類似一個Object對象,進行observer設置get,set響應式時會進let dep = new Dep, 來收集改值得依賴 } else { this.value = this.get() }
所以從上面的實現(xiàn)可以看出,對當前計算屬性自身也生成一個dep列表進行收集;完全可以把一個computed的初始化看出data中數(shù)據(jù)的初始化,只不過該值又依賴多個依賴
第三個evaluate
evaluate () { if (this.dirty) { this.value = this.get() this.dirty = false } return this.value }
關鍵的update也做了修改,
update () { /* istanbul ignore else */ if (this.computed) { if (this.dep.subs.length === 0) { this.dirty = true } else { this.getAndInvoke(() => { this.dep.notify() }) } } else if (this.sync) { this.run() } else { queueWatcher(this) } }, //當計算屬性的值發(fā)生變化時,改觸發(fā)回調(diào)函數(shù)或者進行渲染,而不是通過之前值(例如num改變)變化就觸發(fā)回調(diào) getAndInvoke (cb: Function) { const value = this.get() if ( value !== this.value || isObject(value) || this.deep ) { const oldValue = this.value this.value = value this.dirty = false if (this.user) { try { cb.call(this.vm, value, oldValue) } catch (e) { handleError(e, this.vm, `callback for watcher "${this.expression}"`) } } else { cb.call(this.vm, value, oldValue) } } }
當觸發(fā)update時首先通過getAndInvoke函數(shù)進行值得比較,看是否發(fā)生變化,即只有在變化時才會執(zhí)行,執(zhí)行的是this.dep.notify(),而這邊打的this是當前watch實例對象;因為之前就添加了依賴this.dep.depend()所以接著觸發(fā)cb.call(this.vm, value, oldValue)cb是:this.dep.notify()但this指向了vm用來觸發(fā)渲染更新
總結計算屬性的觀察者是惰性求值,需要手動通過get
怎么手動get,所以有了問題的第二問,和第三問
觸發(fā)了get,也就是觸發(fā)了createComputedGetter函數(shù),就會去取值this.value = this.get(),進行第一次渲染或取值;同時watcher.depend(),將計算屬性的依賴添加至dep中,
值發(fā)送變化時,輸出watch.update,首先判斷是否存在依賴,存在則只需watcher.getAndInvoke(cb),
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/109508.html
摘要:不多廢話,先說結論小程序組件寫法這里就不再介紹。在官方文檔中,我們可以看到使用構造器構造頁面事實上,小程序的頁面也可以視為自定義組件。經(jīng)過一番測試,得出結果為為了簡便。畢竟官方標準,不用擔心其他一系列后續(xù)問題。 在開發(fā)小程序的時候,我們總是期望用以往的技術規(guī)范和語法特點來書寫當前的小程序,所以才會有各色的小程序框架,例如 mpvue、taro 等這些編譯型框架。當然這些框架本身對于新開...
摘要:每一個計算屬性都包含一個和一個。使用計算屬性的原因在于它的依賴緩存。及與綁定的主要用法是動態(tài)更新元素上的屬性。測試文字當?shù)谋磉_式過長或邏輯復雜時,還可以綁定一個計算屬性。 學習筆記:前端開發(fā)文檔 計算屬性 所有的計算屬性都以函數(shù)的形式寫在Vue實例中的computed選項內(nèi),最終返回計算后的結果。 計算屬性的用法 在一個計算屬性中可以完成各種復雜的邏輯,包括運算、函數(shù)調(diào)用等,只要最終...
摘要:對于最終的結果,兩種方式確實是相同的。然而,不同的是計算屬性是基于它們的依賴進行緩存的。這就意味著只要還沒有發(fā)生改變,多次訪問計算屬性會立即返回之前的計算結果,而不必再次執(zhí)行函數(shù)。 vue.js vue.js 的構造 new Vue({}) new new MyComponent() 屬性與方法 vue會代理其data對象里所有的屬性 例如 data.a=vn.a vm.$i...
摘要:這個是我們約定的額外的配置這個字段下的數(shù)據(jù)會被填充到頂部欄的統(tǒng)一配置美團汽車票同時,這個時候,我們會根據(jù)的頁面數(shù)據(jù),自動填充到中的字段。 美團小程序框架mpvue(花名:沒朋友)蹲坑指南 第一次接觸小程序大概是17年初,當時小程序剛剛內(nèi)側,當時就被各種限制折騰的死去活來的,單向綁定,沒有promise,請求數(shù)限制,包大小限制,各種反人類,...反正我是感受到了滿滿的惡意.最近接到一個工...
摘要:雖然計算屬性在大多數(shù)情況下更合適,但有時也需要一個自定義的偵聽器。當某個屬性發(fā)生變化,觸發(fā)攔截函數(shù),然后調(diào)用自身消息訂閱器的方法,遍歷當前中保存著所有訂閱者的數(shù)組,并逐個調(diào)用的方法,完成響應更新。 雖然目前的技術棧已由Vue轉到了React,但從之前使用Vue開發(fā)的多個項目實際經(jīng)歷來看還是非常愉悅的,Vue文檔清晰規(guī)范,api設計簡潔高效,對前端開發(fā)人員友好,上手快,甚至個人認為在很多...
閱讀 3525·2023-04-26 00:16
閱讀 1361·2021-11-25 09:43
閱讀 3824·2021-11-23 09:51
閱讀 2964·2021-09-24 09:55
閱讀 713·2021-09-22 15:45
閱讀 1387·2021-07-30 15:30
閱讀 3064·2019-08-30 14:04
閱讀 2237·2019-08-26 13:46