摘要:源碼對于計算屬性的理解這是我最近學習源碼的一個個人總結和理解,所以可能并不適合每一位讀者本文的整體脈絡如下,首先盡可能去掉細節,對計算屬性源碼的大致實現有一個了解,然后舉一例子,分別談談計算屬性依賴收集和派發更新的流程。
vue源碼-對于「計算屬性」的理解
這是我最近學習vue源碼的一個個人總結和理解,所以可能并不適合每一位讀者
本文的整體脈絡如下,首先盡可能去掉細節,對計算屬性源碼的大致實現有一個了解,然后舉一例子,分別談談計算屬性依賴收集和派發更新的流程。
計算屬性的源碼實現
舉例來說,談談頁面初次渲染時,整個依賴收集的過程
舉例來說,計算屬性的依賴被修改時,派發更新的過程
另外推薦2個開源的vue源碼分析集合
https://ustbhuangyi.github.io...
http://hcysun.me/vue-design/a...
計算屬性的源碼實現_init() --> initState() --> initComputed()
1.遍歷computed選項,2.實例化computed watcher 3.defineComputed()
defineComputed()核心就是把計算屬性用Object.defineProperty包裝成響應式對象,而getter就是把用戶傳入的函數作為getter
但是準確的說,是用戶傳遞的fn的返回值是作為計算屬性getter的return值,但是除此之外計算屬性在getter時還做了一些其他的操作
1是watch.depend() 2.return watch.evaluate()。 也就是1.收集依賴2.把值返回
this._init() : 重點關注重點init方法中initState
initLifecycle(vm) initEvents(vm) initRender(vm) callHook(vm, "beforeCreate") initInjections(vm) // resolve injections before data/props initState(vm) initProvide(vm) // resolve provide after data/props callHook(vm, "created")
initState() 重點關注這一句 if (opts.computed) initComputed(vm, opts.computed)
export function initState (vm: Component) { vm._watchers = [] const opts = vm.$options if (opts.props) initProps(vm, opts.props) if (opts.methods) initMethods(vm, opts.methods) if (opts.data) { initData(vm) } else { observe(vm._data = {}, true /* asRootData */) } if (opts.computed) initComputed(vm, opts.computed) if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch) } }
initComputed() 核心就是遍歷computed,每次循環都實例化一個computed watch,并且用defineComputed把計算屬性包裝成響應式對象
function initComputed (vm: Component, computed: Object) { // $flow-disable-line const watchers = vm._computedWatchers = Object.create(null) // computed properties are just getters during SSR const isSSR = isServerRendering() for (const key in computed) { const userDef = computed[key] 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) { // create internal watcher for the computed property. watchers[key] = new Watcher( vm, getter || noop, noop, computedWatcherOptions ) } // component-defined computed properties are already defined on the // component prototype. We only need to define computed properties defined // at instantiation here. if (!(key in vm)) { 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() 核心就是Object.defineProperty ,大段代碼都是在給它設置get和set
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 } if (process.env.NODE_ENV !== "production" && sharedPropertyDefinition.set === noop) { sharedPropertyDefinition.set = function () { warn( `Computed property "${key}" was assigned to but it has no setter.`, this ) } } Object.defineProperty(target, key, sharedPropertyDefinition) }
createComputedGetter : 計算屬性的getter就是這個computedGetter,做了2件事情,1.收集render watcher作為自己的依賴,2.調用用戶的那個函數作為返回值
function createComputedGetter (key) { return function computedGetter () { const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { watcher.depend() return watcher.evaluate() } } }
到目前為止就結束了,也就是說初始化computed watcher都沒有求值
直到render時,才會觸發computed watcher的getter
舉例來說,談談頁面初次渲染時,整個依賴收集的過程比如我們有一個計算屬性,并且fullName是渲染在模板中的。
computed: { fullName(){ return this.firstName + this.lastName } }
那么頁面初次渲染時,整個依賴收集的過程如下
render函數執行時,會讀取計算屬性fullName,那么會觸發fullName的getter,那么就會執行到watch.depend()和return watch.evaluate()
這個computed watcher的depend()會把render watcher作為依賴收集到它的subs里。
這個computed watcher的evaluate()主要是把調用了用戶給的那個函數,求值并返回
最后值得注意的是,調用用戶的函數,也就是執行了this.firstName + this.lastName ,那么也會觸發他們的getter,所以他們也會把computed watcher作為依賴,收集到subs里,將來如果被修改的話,用通知subs里的watch調用update,也就是去派發更新
舉例來說,計算屬性的依賴被修改時,派發更新的過程當this.firstName或者this.lastName被修改時,會觸發他們的setter,setter就干兩個事情。1是賦值 2是派發更新
所以會通知他們的subs里的watch去調用自己的update方法。其中也包括computed watcher,
那么computed watcher在update方法跟普通的user watcher的update存在區別,computed watcher并不是直接推入異步更新隊列,而是 this.dep.notify()發出之前計算屬性所收集的依賴去派發更新,其中就包括render watcher,調用render watcher的update就會實現視圖更新了
注意this.getAndInvoke的作用,就是如果this.lastName和this.firstName變化了,但是經過計算之后,計算屬性的值不變,那么也不會觸發notify的,也就不會更新視圖
update () { 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) } }
這也是為什么,我們說計算屬性的依賴屬性不被修改的話,計算屬性就不會變化。因為getter就是你那個函數嘛,而且其實就算依賴變化了,只要計算之后的計算屬性變,也不會觸發視圖更新。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/99264.html
摘要:前言最近在學習計算屬性的源碼,發現和普通的響應式變量內部的實現還有一些不同,特地寫了這篇博客,記錄下自己學習的成果文中的源碼截圖只保留核心邏輯完整源碼地址可能需要了解一些響應式的原理版本計算屬性的概念一般的計算屬性值是一個函數,這個函數showImg(https://user-gold-cdn.xitu.io/2019/5/6/16a8b98f1361f6f6); 前言 最近在學習Vue計...
摘要:接下來,我們就一起深入了解的數據響應式原理,搞清楚響應式的實現機制。回調函數只是打印出新的得到的新的值,由執行后生成。及異步更新相信讀過前文,你應該對響應式原理有基本的認識。 前言 Vue.js 的核心包括一套響應式系統。 響應式,是指當數據改變后,Vue 會通知到使用該數據的代碼。例如,視圖渲染中使用了數據,數據改變后,視圖也會自動更新。 舉個簡單的例子,對于模板: {{ name ...
摘要:先說遍歷,很簡單,如下行左右代碼就足夠遍歷一個對象了遇到普通數據屬性,直接處理,遇到對象,遍歷屬性之后遞歸進去處理屬性,遇到數組,遞歸進去處理數組元素。這樣改進之后我就不需要對數組元素進行響應式處理,只是遇到數組的時候把數組的方法變異即可。 用了Vue很久了,最近決定系統性的看看Vue的源碼,相信看源碼的同學不在少數,但是看的時候卻發現挺有難度,Vue雖然足夠精簡,但是怎么說現在也有1...
摘要:并在內執行了函數,在函數內部,訪問了。至此知道了它依賴于。需要根據最新的計算。本例中收集到了依賴并且也被告知觀察了他們。文章鏈接源碼分析系列源碼分析系列之環境搭建源碼分析系列之入口文件分析源碼分析系列之響應式數據一源碼分析系列之響應式數據二 前言 上一節著重講述了initData中的代碼,以及數據是如何從data中到視圖層的,以及data修改后如何作用于視圖。這一節主要記錄initCo...
摘要:無論是雙向綁定還是單向綁定,都是符合思想的。看了的源碼后不難發現的雙向綁定的實現也就是在表單元素上添加了事件,可以說雙向綁定是單向綁定的一個語法糖。 前言 本文會帶大家手動實現一個雙向綁定過程(僅僅涵蓋一些簡單的指令解析,如:v-text,v-model,插值),當然借鑒的是Vue1的源碼,相信大家在閱讀完本文后對Vue1會有一個更好的理解,源代碼放到了github,由于本人水平有限,...
閱讀 746·2023-04-26 01:30
閱讀 3301·2021-11-24 10:32
閱讀 2179·2021-11-22 14:56
閱讀 1979·2021-11-18 10:07
閱讀 553·2019-08-29 17:14
閱讀 624·2019-08-26 12:21
閱讀 3103·2019-08-26 10:55
閱讀 2940·2019-08-23 18:09