詳解Vue計算屬性原理
初始化
組件初始化時會調用掛載在Vue原型鏈上的_init方法,即Vue.prototype._init:
function Vue(options) {
this._init(options)
}同時_init方法會調用initState方法,這個方法主要用來初始化props、methods、data、watch以及本文所介紹的computed:
Vue.prototype._init = function (options?: Record<string, any>) {
// ...省略一些代碼
initState(vm)
// ...省略一些代碼
}為了看上去一目了然,這里省略了一些代碼(包括判斷條件)
export function initState(vm: Component) {
// ..省略了一些代碼
initProps(vm, opts.props)
initMethods(vm, opts.methods)
initData(vm)
initComputed(vm, opts.computed)
initWatch(vm, opts.watch)
}走到這里之后,程序就進入了初始化計算屬性的過程中
initComputed
在這個方法里,主要做了兩件事:
- 遍歷用戶傳入的計算屬性,生成相應的
Wacher,每個Watcher都被標記為lazy - 在組件實例
vm上掛載同名屬性,值為一個按條件生成的getter函數(shù)
以下代碼有省略
function initComputed(vm: Component, computed: Object) {
const watchers = (vm._computedWatchers = Object.create(null))
for (const key in computed) {
const userDef = computed[key];
// 獲取用戶自定義的計算屬性getter
const getter = isFunction(userDef) ? userDef : userDef.get;
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
{ lazy: true }
);
defineComputed(vm, key, userDef);
}
}其中new Watcher會生成Watcher實例:
export default class Watcher implements DepTarget {
constructor(
vm: Component | null,
expOrFn: string | (() => any),
cb: Function,
options?: WatcherOptions | null,
isRenderWatcher?: boolean
) {
this.getter = expOrFn
this.value = this.lazy ? undefined : this.get();
}因為我們傳入的lazy配置,Wacher不會執(zhí)行Watcher.prototype.get()方法,這個方法主要用于依賴收集
defineComputed
這個方法主要是在組件實例上生成相應的計算屬性,便于我們在組件內部通過this[key]的方式進行獲取,主要代碼如下:
export function defineComputed(
target: any,
key: string,
userDef: Record<string, any> | (() => any)
) {
sharedPropertyDefinition.get = createComputedGetter(key);
Object.defineProperty(target, key, sharedPropertyDefinition)
}這里的target就是組件實例,key即為我們定義的相應的響應式屬性名,它的值是一個 生成的getter;
createComputedGetter
有了訪問屬性的方式,那么就需要返回相應的計算屬性值,這個方法主要用來實現(xiàn)計算屬性的惰性求值的:
function createComputedGetter(key) {
return function computedGetter() {
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
if (watcher.dirty) {
watcher.evaluate()
}
// ...
return watcher.value
}
}
}這里返回的函數(shù)就是用于設置Object.defineProperty的getter的存取描述符get,它會根據(jù)watcher.dirty,判斷是否需要調用方法watcher.evaluate()進行求值。 在watcher.evaluate方法里主要做兩件事:
- 調用
get方法進行依賴收集(會調用我們在計算屬性上定義的函數(shù),從而觸發(fā)相應的響應式數(shù)據(jù)的getter進行依賴收集) - 將
watcher的dirty標志位置為false
this.value = this.get() this.dirty = false
完成了上述步驟后,整個響應式屬性就建立完畢,當計算屬性依賴的數(shù)據(jù)發(fā)生變化時,會調用watcher.update()方法,這個方法會再次將dirty標志位寫為false。當我們在模版里或其他地方訪問組件實例上的響應式屬性后,就會觸發(fā)上述定義的createComputedGetter返回的函數(shù)。從而進行wacher.evaluate()求值進行重新計算,計算完成后又將dirty置為false,等待下一次重新計算。如果我們不妨問這個屬性,那么是不會觸發(fā)getter,從而進行計算的(惰性求值)。
總結
方法調用流程:
組件初始化 -> _init() -> initState -> initComputed -> definedComputed -> createComputedGetter
數(shù)據(jù)流:
watcher.evaluate -> 依賴收集 -> dirty = false -> update -> dirty = true -> 用戶訪問計算屬性 -> watcher.evaluate -> 循環(huán)此過程...
紙上得來終覺淺 絕知此事要躬行
以上就是詳解Vue計算屬性原理的詳細內容,更多關于Vue計算屬性原理的資料請關注腳本之家其它相關文章!
相關文章
Vue項目通過vue-i18n實現(xiàn)國際化方案(推薦)
這篇文章主要介紹了Vue項目通過vue-i18n實現(xiàn)國際化方案,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-12-12
vue實現(xiàn)tab切換的3種方式及切換保持數(shù)據(jù)狀態(tài)
這篇文章主要給大家介紹了關于vue實現(xiàn)tab切換的3種方式及切換保持數(shù)據(jù)狀態(tài)的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-05-05
vue+elementUI實現(xiàn)表格關鍵字篩選高亮
這篇文章主要為大家詳細介紹了vue+elementUI實現(xiàn)表格關鍵字篩選高亮,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-05-05
vue?perfect-scrollbar(特定框架里使用非該框架定制庫/插件)
這篇文章主要為大家介紹了vue?perfect-scrollbar在特定框架里使用一款并非為該框架定制的庫/插件如何實現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪<BR>2023-05-05

