摘要:源碼對(duì)于計(jì)算屬性的理解這是我最近學(xué)習(xí)源碼的一個(gè)個(gè)人總結(jié)和理解,所以可能并不適合每一位讀者本文的整體脈絡(luò)如下,首先盡可能去掉細(xì)節(jié),對(duì)計(jì)算屬性源碼的大致實(shí)現(xiàn)有一個(gè)了解,然后舉一例子,分別談?wù)動(dòng)?jì)算屬性依賴收集和派發(fā)更新的流程。
vue源碼-對(duì)于「計(jì)算屬性」的理解
這是我最近學(xué)習(xí)vue源碼的一個(gè)個(gè)人總結(jié)和理解,所以可能并不適合每一位讀者
本文的整體脈絡(luò)如下,首先盡可能去掉細(xì)節(jié),對(duì)計(jì)算屬性源碼的大致實(shí)現(xiàn)有一個(gè)了解,然后舉一例子,分別談?wù)動(dòng)?jì)算屬性依賴收集和派發(fā)更新的流程。
計(jì)算屬性的源碼實(shí)現(xiàn)
舉例來說,談?wù)勴撁娉醮武秩緯r(shí),整個(gè)依賴收集的過程
舉例來說,計(jì)算屬性的依賴被修改時(shí),派發(fā)更新的過程
另外推薦2個(gè)開源的vue源碼分析集合
https://ustbhuangyi.github.io...
http://hcysun.me/vue-design/a...
計(jì)算屬性的源碼實(shí)現(xiàn)_init() --> initState() --> initComputed()
1.遍歷computed選項(xiàng),2.實(shí)例化computed watcher 3.defineComputed()
defineComputed()核心就是把計(jì)算屬性用Object.defineProperty包裝成響應(yīng)式對(duì)象,而getter就是把用戶傳入的函數(shù)作為getter
但是準(zhǔn)確的說,是用戶傳遞的fn的返回值是作為計(jì)算屬性getter的return值,但是除此之外計(jì)算屬性在getter時(shí)還做了一些其他的操作
1是watch.depend() 2.return watch.evaluate()。 也就是1.收集依賴2.把值返回
this._init() : 重點(diǎn)關(guān)注重點(diǎn)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() 重點(diǎn)關(guān)注這一句 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,每次循環(huán)都實(shí)例化一個(gè)computed watch,并且用defineComputed把計(jì)算屬性包裝成響應(yīng)式對(duì)象
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 ,大段代碼都是在給它設(shè)置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 : 計(jì)算屬性的getter就是這個(gè)computedGetter,做了2件事情,1.收集render watcher作為自己的依賴,2.調(diào)用用戶的那個(gè)函數(shù)作為返回值
function createComputedGetter (key) { return function computedGetter () { const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { watcher.depend() return watcher.evaluate() } } }
到目前為止就結(jié)束了,也就是說初始化computed watcher都沒有求值
直到render時(shí),才會(huì)觸發(fā)computed watcher的getter
舉例來說,談?wù)勴撁娉醮武秩緯r(shí),整個(gè)依賴收集的過程比如我們有一個(gè)計(jì)算屬性,并且fullName是渲染在模板中的。
computed: { fullName(){ return this.firstName + this.lastName } }
那么頁面初次渲染時(shí),整個(gè)依賴收集的過程如下
render函數(shù)執(zhí)行時(shí),會(huì)讀取計(jì)算屬性fullName,那么會(huì)觸發(fā)fullName的getter,那么就會(huì)執(zhí)行到watch.depend()和return watch.evaluate()
這個(gè)computed watcher的depend()會(huì)把render watcher作為依賴收集到它的subs里。
這個(gè)computed watcher的evaluate()主要是把調(diào)用了用戶給的那個(gè)函數(shù),求值并返回
最后值得注意的是,調(diào)用用戶的函數(shù),也就是執(zhí)行了this.firstName + this.lastName ,那么也會(huì)觸發(fā)他們的getter,所以他們也會(huì)把computed watcher作為依賴,收集到subs里,將來如果被修改的話,用通知subs里的watch調(diào)用update,也就是去派發(fā)更新
舉例來說,計(jì)算屬性的依賴被修改時(shí),派發(fā)更新的過程當(dāng)this.firstName或者this.lastName被修改時(shí),會(huì)觸發(fā)他們的setter,setter就干兩個(gè)事情。1是賦值 2是派發(fā)更新
所以會(huì)通知他們的subs里的watch去調(diào)用自己的update方法。其中也包括computed watcher,
那么computed watcher在update方法跟普通的user watcher的update存在區(qū)別,computed watcher并不是直接推入異步更新隊(duì)列,而是 this.dep.notify()發(fā)出之前計(jì)算屬性所收集的依賴去派發(fā)更新,其中就包括render watcher,調(diào)用render watcher的update就會(huì)實(shí)現(xiàn)視圖更新了
注意this.getAndInvoke的作用,就是如果this.lastName和this.firstName變化了,但是經(jīng)過計(jì)算之后,計(jì)算屬性的值不變,那么也不會(huì)觸發(fā)notify的,也就不會(huì)更新視圖
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) } }
這也是為什么,我們說計(jì)算屬性的依賴屬性不被修改的話,計(jì)算屬性就不會(huì)變化。因?yàn)間etter就是你那個(gè)函數(shù)嘛,而且其實(shí)就算依賴變化了,只要計(jì)算之后的計(jì)算屬性變,也不會(huì)觸發(fā)視圖更新。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/99264.html
摘要:前言最近在學(xué)習(xí)計(jì)算屬性的源碼,發(fā)現(xiàn)和普通的響應(yīng)式變量?jī)?nèi)部的實(shí)現(xiàn)還有一些不同,特地寫了這篇博客,記錄下自己學(xué)習(xí)的成果文中的源碼截圖只保留核心邏輯完整源碼地址可能需要了解一些響應(yīng)式的原理版本計(jì)算屬性的概念一般的計(jì)算屬性值是一個(gè)函數(shù),這個(gè)函數(shù)showImg(https://user-gold-cdn.xitu.io/2019/5/6/16a8b98f1361f6f6); 前言 最近在學(xué)習(xí)Vue計(jì)...
摘要:接下來,我們就一起深入了解的數(shù)據(jù)響應(yīng)式原理,搞清楚響應(yīng)式的實(shí)現(xiàn)機(jī)制。回調(diào)函數(shù)只是打印出新的得到的新的值,由執(zhí)行后生成。及異步更新相信讀過前文,你應(yīng)該對(duì)響應(yīng)式原理有基本的認(rèn)識(shí)。 前言 Vue.js 的核心包括一套響應(yīng)式系統(tǒng)。 響應(yīng)式,是指當(dāng)數(shù)據(jù)改變后,Vue 會(huì)通知到使用該數(shù)據(jù)的代碼。例如,視圖渲染中使用了數(shù)據(jù),數(shù)據(jù)改變后,視圖也會(huì)自動(dòng)更新。 舉個(gè)簡(jiǎn)單的例子,對(duì)于模板: {{ name ...
摘要:先說遍歷,很簡(jiǎn)單,如下行左右代碼就足夠遍歷一個(gè)對(duì)象了遇到普通數(shù)據(jù)屬性,直接處理,遇到對(duì)象,遍歷屬性之后遞歸進(jìn)去處理屬性,遇到數(shù)組,遞歸進(jìn)去處理數(shù)組元素。這樣改進(jìn)之后我就不需要對(duì)數(shù)組元素進(jìn)行響應(yīng)式處理,只是遇到數(shù)組的時(shí)候把數(shù)組的方法變異即可。 用了Vue很久了,最近決定系統(tǒng)性的看看Vue的源碼,相信看源碼的同學(xué)不在少數(shù),但是看的時(shí)候卻發(fā)現(xiàn)挺有難度,Vue雖然足夠精簡(jiǎn),但是怎么說現(xiàn)在也有1...
摘要:并在內(nèi)執(zhí)行了函數(shù),在函數(shù)內(nèi)部,訪問了。至此知道了它依賴于。需要根據(jù)最新的計(jì)算。本例中收集到了依賴并且也被告知觀察了他們。文章鏈接源碼分析系列源碼分析系列之環(huán)境搭建源碼分析系列之入口文件分析源碼分析系列之響應(yīng)式數(shù)據(jù)一源碼分析系列之響應(yīng)式數(shù)據(jù)二 前言 上一節(jié)著重講述了initData中的代碼,以及數(shù)據(jù)是如何從data中到視圖層的,以及data修改后如何作用于視圖。這一節(jié)主要記錄initCo...
摘要:無論是雙向綁定還是單向綁定,都是符合思想的。看了的源碼后不難發(fā)現(xiàn)的雙向綁定的實(shí)現(xiàn)也就是在表單元素上添加了事件,可以說雙向綁定是單向綁定的一個(gè)語法糖。 前言 本文會(huì)帶大家手動(dòng)實(shí)現(xiàn)一個(gè)雙向綁定過程(僅僅涵蓋一些簡(jiǎn)單的指令解析,如:v-text,v-model,插值),當(dāng)然借鑒的是Vue1的源碼,相信大家在閱讀完本文后對(duì)Vue1會(huì)有一個(gè)更好的理解,源代碼放到了github,由于本人水平有限,...
閱讀 756·2023-04-26 01:30
閱讀 3307·2021-11-24 10:32
閱讀 2193·2021-11-22 14:56
閱讀 1988·2021-11-18 10:07
閱讀 561·2019-08-29 17:14
閱讀 632·2019-08-26 12:21
閱讀 3111·2019-08-26 10:55
閱讀 2947·2019-08-23 18:09