詳解Vue計(jì)算屬性原理
初始化
組件初始化時(shí)會(huì)調(diào)用掛載在Vue原型鏈上的_init方法,即Vue.prototype._init:
function Vue(options) {
this._init(options)
}同時(shí)_init方法會(huì)調(diào)用initState方法,這個(gè)方法主要用來(lái)初始化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)
}走到這里之后,程序就進(jìn)入了初始化計(jì)算屬性的過(guò)程中
initComputed
在這個(gè)方法里,主要做了兩件事:
- 遍歷用戶(hù)傳入的計(jì)算屬性,生成相應(yīng)的
Wacher,每個(gè)Watcher都被標(biāo)記為lazy - 在組件實(shí)例
vm上掛載同名屬性,值為一個(gè)按條件生成的getter函數(shù)
以下代碼有省略
function initComputed(vm: Component, computed: Object) {
const watchers = (vm._computedWatchers = Object.create(null))
for (const key in computed) {
const userDef = computed[key];
// 獲取用戶(hù)自定義的計(jì)算屬性getter
const getter = isFunction(userDef) ? userDef : userDef.get;
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
{ lazy: true }
);
defineComputed(vm, key, userDef);
}
}其中new Watcher會(huì)生成Watcher實(shí)例:
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();
}因?yàn)槲覀儌魅氲?code>lazy配置,Wacher不會(huì)執(zhí)行Watcher.prototype.get()方法,這個(gè)方法主要用于依賴(lài)收集
defineComputed
這個(gè)方法主要是在組件實(shí)例上生成相應(yīng)的計(jì)算屬性,便于我們?cè)诮M件內(nèi)部通過(guò)this[key]的方式進(jìn)行獲取,主要代碼如下:
export function defineComputed(
target: any,
key: string,
userDef: Record<string, any> | (() => any)
) {
sharedPropertyDefinition.get = createComputedGetter(key);
Object.defineProperty(target, key, sharedPropertyDefinition)
}這里的target就是組件實(shí)例,key即為我們定義的相應(yīng)的響應(yīng)式屬性名,它的值是一個(gè) 生成的getter;
createComputedGetter
有了訪(fǎng)問(wèn)屬性的方式,那么就需要返回相應(yīng)的計(jì)算屬性值,這個(gè)方法主要用來(lái)實(shí)現(xiàn)計(jì)算屬性的惰性求值的:
function createComputedGetter(key) {
return function computedGetter() {
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
if (watcher.dirty) {
watcher.evaluate()
}
// ...
return watcher.value
}
}
}這里返回的函數(shù)就是用于設(shè)置Object.defineProperty的getter的存取描述符get,它會(huì)根據(jù)watcher.dirty,判斷是否需要調(diào)用方法watcher.evaluate()進(jìn)行求值。 在watcher.evaluate方法里主要做兩件事:
- 調(diào)用
get方法進(jìn)行依賴(lài)收集(會(huì)調(diào)用我們?cè)谟?jì)算屬性上定義的函數(shù),從而觸發(fā)相應(yīng)的響應(yīng)式數(shù)據(jù)的getter進(jìn)行依賴(lài)收集) - 將
watcher的dirty標(biāo)志位置為false
this.value = this.get() this.dirty = false
完成了上述步驟后,整個(gè)響應(yīng)式屬性就建立完畢,當(dāng)計(jì)算屬性依賴(lài)的數(shù)據(jù)發(fā)生變化時(shí),會(huì)調(diào)用watcher.update()方法,這個(gè)方法會(huì)再次將dirty標(biāo)志位寫(xiě)為false。當(dāng)我們?cè)谀0胬锘蚱渌胤皆L(fǎng)問(wèn)組件實(shí)例上的響應(yīng)式屬性后,就會(huì)觸發(fā)上述定義的createComputedGetter返回的函數(shù)。從而進(jìn)行wacher.evaluate()求值進(jìn)行重新計(jì)算,計(jì)算完成后又將dirty置為false,等待下一次重新計(jì)算。如果我們不妨問(wèn)這個(gè)屬性,那么是不會(huì)觸發(fā)getter,從而進(jìn)行計(jì)算的(惰性求值)。
總結(jié)
方法調(diào)用流程:
組件初始化 -> _init() -> initState -> initComputed -> definedComputed -> createComputedGetter
數(shù)據(jù)流:
watcher.evaluate -> 依賴(lài)收集 -> dirty = false -> update -> dirty = true -> 用戶(hù)訪(fǎng)問(wèn)計(jì)算屬性 -> watcher.evaluate -> 循環(huán)此過(guò)程...
紙上得來(lái)終覺(jué)淺 絕知此事要躬行
以上就是詳解Vue計(jì)算屬性原理的詳細(xì)內(nèi)容,更多關(guān)于Vue計(jì)算屬性原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue?使用el-table循環(huán)生成表格的過(guò)程
這篇文章主要介紹了vue?使用el-table循環(huán)生成表格的過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
教你使用vue3寫(xiě)Json-Preview的示例代碼
這篇文章主要介紹了用vue3寫(xiě)了一個(gè)Json-Preview的相關(guān)知識(shí),引入后直接<json-preview?v-model="jsonData"></json-preview>就可以使用了,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06
Vue項(xiàng)目通過(guò)vue-i18n實(shí)現(xiàn)國(guó)際化方案(推薦)
這篇文章主要介紹了Vue項(xiàng)目通過(guò)vue-i18n實(shí)現(xiàn)國(guó)際化方案,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-12-12
vue實(shí)現(xiàn)tab切換的3種方式及切換保持?jǐn)?shù)據(jù)狀態(tài)
這篇文章主要給大家介紹了關(guān)于vue實(shí)現(xiàn)tab切換的3種方式及切換保持?jǐn)?shù)據(jù)狀態(tài)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
npm install sentry-cli失敗的問(wèn)題解決
本文主要介紹了npm install sentry-cli失敗的問(wèn)題解決,文章首先描述了問(wèn)題現(xiàn)象,然后分析了問(wèn)題的原因,具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08
vue+elementUI實(shí)現(xiàn)表格關(guān)鍵字篩選高亮
這篇文章主要為大家詳細(xì)介紹了vue+elementUI實(shí)現(xiàn)表格關(guān)鍵字篩選高亮,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05
vue?perfect-scrollbar(特定框架里使用非該框架定制庫(kù)/插件)
這篇文章主要為大家介紹了vue?perfect-scrollbar在特定框架里使用一款并非為該框架定制的庫(kù)/插件如何實(shí)現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2023-05-05

