生活随笔
收集整理的這篇文章主要介紹了
vue源码-对于「计算属性」的理解
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
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/propsinitState(vm)initProvide(vm) // resolve provide after data/propscallHook(vm, 'created')
initState() 重點關注這一句 if (opts.computed) initComputed(vm, opts.computed) export function initState (vm: Component) {vm._watchers = []const opts = vm.$optionsif (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-lineconst watchers = vm._computedWatchers = Object.create(null)// computed properties are just getters during SSRconst isSSR = isServerRendering()for (const key in computed) {const userDef = computed[key]const getter = typeof userDef === 'function' ? userDef : userDef.getif (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): userDefsharedPropertyDefinition.set = noop} else {sharedPropertyDefinition.get = userDef.get? shouldCache && userDef.cache !== false? createComputedGetter(key): userDef.get: noopsharedPropertyDefinition.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就是你那個函數嘛,而且其實就算依賴變化了,只要計算之后的計算屬性變,也不會觸發視圖更新。
總結
以上是生活随笔為你收集整理的vue源码-对于「计算属性」的理解的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。