摘要:前言最近在學(xué)習(xí)計(jì)算屬性的源碼,發(fā)現(xiàn)和普通的響應(yīng)式變量?jī)?nèi)部的實(shí)現(xiàn)還有一些不同,特地寫了這篇博客,記錄下自己學(xué)習(xí)的成果文中的源碼截圖只保留核心邏輯完整源碼地址可能需要了解一些響應(yīng)式的原理版本計(jì)算屬性的概念一般的計(jì)算屬性值是一個(gè)函數(shù),這個(gè)函數(shù)
前言
最近在學(xué)習(xí)Vue計(jì)算屬性的源碼,發(fā)現(xiàn)和普通的響應(yīng)式變量?jī)?nèi)部的實(shí)現(xiàn)還有一些不同,特地寫了這篇博客,記錄下自己學(xué)習(xí)的成果
文中的源碼截圖只保留核心邏輯 完整源碼地址
可能需要了解一些Vue響應(yīng)式的原理
Vue 版本:2.5.21
計(jì)算屬性的概念一般的計(jì)算屬性值是一個(gè)函數(shù),這個(gè)函數(shù)會(huì)返回一個(gè)值,并且其函數(shù)內(nèi)部還可能會(huì)依賴別的變量
一般的計(jì)算屬性看起來(lái)和 method 很像,值都是一個(gè)函數(shù),那他們有什么區(qū)別呢
計(jì)算屬性和method的區(qū)別將一個(gè)計(jì)算屬性的函數(shù)放在 methods 中同樣也能達(dá)到相同的效果
但是如果視圖中依賴了這個(gè) method 的返回值,并且當(dāng)另外一個(gè)其他的響應(yīng)式變量的修改導(dǎo)致視圖被更新時(shí), method 會(huì)重新執(zhí)行一遍,即使這次的更新和 method 中依賴的變量沒(méi)有任何關(guān)系!
而對(duì)于計(jì)算屬性,只有當(dāng)計(jì)算屬性依賴的變量改變后,才會(huì)重新執(zhí)行一遍函數(shù),并重新返回一個(gè)新的值
點(diǎn)我看示例
當(dāng) otherProp 變量被修改導(dǎo)致更新視圖的時(shí)候,methodFullName 每次都會(huì)執(zhí)行,而 computedFullName 只會(huì)在頁(yè)面初始化的時(shí)候執(zhí)行一次,Vue 推薦開發(fā)者將 method 和 compute 屬性區(qū)分開來(lái),能夠有效的提升性能,避免執(zhí)行一些不必要的代碼
回顧過(guò)計(jì)算屬性的概念,接下來(lái)我們深入源碼,來(lái)了解一下計(jì)算屬性到底是怎么實(shí)現(xiàn)的,為什么只有計(jì)算屬性的依賴項(xiàng)被改變了才會(huì)重新求值
從例子入手這里我寫了一個(gè)簡(jiǎn)單的例子,幫助各位理解計(jì)算屬性的運(yùn)行原理,下面的解析會(huì)圍繞這個(gè)例子進(jìn)行解析
const App = {
template: `
{{fullName}}
`,
data() {
return {
firstName: "尤",
lastName: "雨溪",
}
},
methods: {
handleChangeName() {
this.lastName = "大大"
}
},
computed: {
fullName() {
return this.firstName + this.lastName
}
}
}
new Vue({
el: "#app",
components: {
App
},
template: `
`
}).$mount()
fullName 依賴了 firstName 和 lastName,點(diǎn)擊 button 會(huì)修改 lastName, 同時(shí) fullName 會(huì)重新計(jì)算,視圖變成"尤大大"
深入計(jì)算屬性的源碼
在日常開發(fā)中書寫的計(jì)算屬性,實(shí)際上內(nèi)部都會(huì)保存一個(gè) watcher, watcher 的作用是觀察某個(gè)響應(yīng)式變量的改變?nèi)缓髨?zhí)行相應(yīng)的回調(diào),由 Watcher 類實(shí)例化而成, Vue 中定義了3個(gè) watcher
render watcher: 模板依賴并且需要顯示在視圖上變量,其內(nèi)部保存了一個(gè) render watcher
computed watcher: 計(jì)算屬性內(nèi)部保存了一個(gè) computed watcher
user watcher: 使用 watch 屬性觀察的變量?jī)?nèi)部保存了一個(gè) user watcher
理解這3個(gè) watcher 各自的作用非常重要,文本會(huì)著重圍繞 computed watcher 展開
一個(gè)計(jì)算屬性的初始化分為2部分
實(shí)例化一個(gè) computed watcher
定義計(jì)算屬性的 getter 函數(shù)
在初始化當(dāng)前組件時(shí),會(huì)執(zhí)行 initComputed 方法初始化計(jì)算屬性,會(huì)給每個(gè)計(jì)算屬性實(shí)例化一個(gè) computed watcher
在實(shí)例化 watcher 時(shí)傳入不同的配置項(xiàng)就可以生成不同的 watcher 實(shí)例 ,當(dāng)傳入{ lazy: true } 時(shí),實(shí)例化的 watcher 即為 computed watcher
定義計(jì)算屬性的 getter 函數(shù)在創(chuàng)建完 computed watcher 后,接著會(huì)定義計(jì)算屬性的 getter 函數(shù),我們?cè)趫?zhí)行計(jì)算屬性的函數(shù)時(shí),實(shí)際上執(zhí)行的是 computedGetter 這個(gè)函數(shù)
computedGetter代碼很少,但是卻是計(jì)算屬性的核心,我們一步步來(lái)分析
dirty屬性通過(guò) key 獲取到第一步中定義的 computed watcher,隨后會(huì)判斷這個(gè) computed watcher 的 dirty 屬性是否為 true,當(dāng) dirty 為 true 時(shí), 會(huì)執(zhí)行 evaluate 方法, evaluate 內(nèi)部會(huì)執(zhí)行計(jì)算屬性的函數(shù),并且將 watcher 的 value 屬性等于函數(shù)執(zhí)行后的結(jié)果也就是最終計(jì)算出來(lái)的值,具體我們放到后面講
dirty 屬性是一個(gè)用來(lái)檢測(cè)當(dāng)前的 computed watcher是否需要重新執(zhí)行的一個(gè)標(biāo)志,這也是計(jì)算屬性和普通method的區(qū)別,結(jié)合上圖可以發(fā)現(xiàn),當(dāng) dirty 為 false 時(shí),就不會(huì)去執(zhí)行 evaluate 也就不會(huì)執(zhí)行計(jì)算屬性的函數(shù),可以看到最后直接就返回了 watcher.value 表示這次不會(huì)進(jìn)行計(jì)算,會(huì)直接使用以前的 value 的值
當(dāng)?shù)谝淮斡|發(fā)computedGetter 時(shí),dirty 屬性的默認(rèn)值是 true ,那是因?yàn)樵诔跏蓟?computed watcher時(shí)候 Vue 將 dirty 屬性等于了 lazy 屬性,即為 true
知道 dirty 的默認(rèn)值為 true,什么時(shí)候?yàn)?false 呢?我們接著來(lái)看 evalutate 具體的實(shí)現(xiàn)
evaluate方法evaluate 方法是 computed watcher 獨(dú)有的方法,代碼也只有短短2行
get方法
第一行執(zhí)行了 get 方法, get 方法是所有 watcher 用來(lái)求值的通用方法
get 主要就做了這三步
將當(dāng)前這個(gè) watcher 作為棧頂?shù)?watcher 推入棧
執(zhí)行g(shù)etter方法
將這個(gè) watcher 彈出棧
我們知道 Vue.js 會(huì)維護(hù)一個(gè)全局的棧用來(lái)存放 watcher ,每當(dāng)觸發(fā)響應(yīng)式變量?jī)?nèi)部的 getter 時(shí),就會(huì)收集這個(gè)全局的棧的頂部的 watcher(即Dep.target),將這個(gè) watcher 存入響應(yīng)式變量?jī)?nèi)部保存的dep中
第一步通過(guò) pushTarget 將當(dāng)前的 computed watcher 推入全局的棧中,此時(shí)Dep.target就指向這個(gè)棧頂?shù)?computed watcher
第二步執(zhí)行 getter 方法, 對(duì)于 computed watcher,getter 方法就是計(jì)算屬性的函數(shù),執(zhí)行函數(shù)將返回的值賦值給 value 屬性,而當(dāng)計(jì)算屬性的函數(shù)執(zhí)行時(shí),如果內(nèi)部含有其他的響應(yīng)式變量,會(huì)觸發(fā)它們內(nèi)部的 getter ,將第一步放入作為當(dāng)前棧頂?shù)?computed watcher 存入響應(yīng)式變量?jī)?nèi)部的dep對(duì)象中
注意響應(yīng)式變量?jī)?nèi)部的 getter 和 getter 方法不是一個(gè)函數(shù)
第三步將這個(gè) computed watcher 彈出全局的棧
之所以將這個(gè) computed watcher 推入又彈出,是為了讓第二步執(zhí)行內(nèi)部的 getter 時(shí),能讓計(jì)算屬性函數(shù)內(nèi)部依賴的響應(yīng)式變量收集到這個(gè) computed watcher
對(duì)于計(jì)算屬性來(lái)說(shuō),get 方法的作用就是進(jìn)行求值
將dirty設(shè)為false執(zhí)行完 get 方法,即一旦計(jì)算屬性執(zhí)行過(guò)一次求值,就會(huì)將 dirty 屬性設(shè)為 false,如果下次又觸發(fā)了這個(gè)計(jì)算屬性的 getter 會(huì)直接跳過(guò)求值階段
結(jié)合文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/7324.html
摘要:五六月份推薦集合查看最新的請(qǐng)點(diǎn)擊集前端最近很火的框架資源定時(shí)更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語(yǔ)。葉上初陽(yáng)乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門,久作長(zhǎng)安旅。五月漁郎相憶否。小楫輕舟,夢(mèng)入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請(qǐng)::點(diǎn)擊::集web前端最近很火的vue2框架資源;定時(shí)更新,歡迎 Star 一下。 蘇...
摘要:五六月份推薦集合查看最新的請(qǐng)點(diǎn)擊集前端最近很火的框架資源定時(shí)更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語(yǔ)。葉上初陽(yáng)乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門,久作長(zhǎng)安旅。五月漁郎相憶否。小楫輕舟,夢(mèng)入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請(qǐng)::點(diǎn)擊::集web前端最近很火的vue2框架資源;定時(shí)更新,歡迎 Star 一下。 蘇...
摘要:插件開發(fā)前端掘金作者原文地址譯者插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。提供了與使用掌控異步前端掘金教你使用在行代碼內(nèi)優(yōu)雅的實(shí)現(xiàn)文件分片斷點(diǎn)續(xù)傳。 Vue.js 插件開發(fā) - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins譯者:jeneser Vue.js插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。插....
摘要:模塊化是隨著前端技術(shù)的發(fā)展,前端代碼爆炸式增長(zhǎng)后,工程化所采取的必然措施。目前模塊化的思想分為和。特別指出,事件不等同于異步,回調(diào)也不等同于異步。將會(huì)討論安全的類型檢測(cè)惰性載入函數(shù)凍結(jié)對(duì)象定時(shí)器等話題。 Vue.js 前后端同構(gòu)方案之準(zhǔn)備篇——代碼優(yōu)化 目前 Vue.js 的火爆不亞于當(dāng)初的 React,本人對(duì)寫代碼有潔癖,代碼也是藝術(shù)。此篇是準(zhǔn)備篇,工欲善其事,必先利其器。我們先在代...
閱讀 3656·2021-10-12 10:11
閱讀 1020·2021-09-22 15:42
閱讀 3471·2019-08-30 13:06
閱讀 911·2019-08-29 17:05
閱讀 1657·2019-08-29 12:21
閱讀 2386·2019-08-29 11:31
閱讀 1141·2019-08-23 18:37
閱讀 1262·2019-08-23 14:58