自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

詳解Vue中的Computed和Watch

開(kāi)發(fā) 前端
作為一名Vue開(kāi)發(fā)者,雖然在項(xiàng)目中頻繁使用過(guò)computed和watch,但從來(lái)沒(méi)有系統(tǒng)的學(xué)習(xí)過(guò),總覺(jué)著對(duì)這兩個(gè)知識(shí)點(diǎn)有些一知半解。

[[376061]]

本文轉(zhuǎn)載自微信公眾號(hào)「不知名寶藏程序媛  」,作者小土豆 。轉(zhuǎn)載本文請(qǐng)聯(lián)系不知名寶藏程序媛  公眾號(hào)。  

 1. 前言

作為一名Vue開(kāi)發(fā)者,雖然在項(xiàng)目中頻繁使用過(guò)computed和watch,但從來(lái)沒(méi)有系統(tǒng)的學(xué)習(xí)過(guò),總覺(jué)著對(duì)這兩個(gè)知識(shí)點(diǎn)有些一知半解。

如果你也和我一樣,就一起來(lái)回顧和總結(jié)一下這兩個(gè)知識(shí)點(diǎn)吧。

本篇非源碼分析,只是從兩者各自的用法、特性等做一些梳理。

2. Vue中的computed

Vue中的computed又叫做計(jì)算屬性,Vue官網(wǎng)中給了下面這樣一個(gè)示例。

模板中有一個(gè)message數(shù)據(jù)需要展示:

  1. <template> 
  2.   <div id="app"
  3.     {{message}} 
  4.   </div> 
  5. </template> 
  6. <script> 
  7. export default { 
  8.   name'App'
  9.   data() { 
  10.     return { 
  11.       message: 'Hello' 
  12.     } 
  13.   } 
  14. </script> 

假如此時(shí)有一個(gè)需求:對(duì)message進(jìn)行反轉(zhuǎn)并展示到模板中。

那最簡(jiǎn)單的實(shí)現(xiàn)方式就是直接在模板中做這樣的轉(zhuǎn)化:

  1. <template> 
  2.   <div id="app"
  3.     <p>{{message}}</p> 
  4.     <p>{{message.split('').reverse().join('')}}</p> 
  5.   </div> 
  6. </template> 

那這個(gè)時(shí)候,Vue官方告訴我們:過(guò)多的邏輯運(yùn)算會(huì)讓模板變得重且難以維護(hù),而且這種轉(zhuǎn)化無(wú)法復(fù)用,并指導(dǎo)我們使用計(jì)算屬性-computed來(lái)實(shí)現(xiàn)這個(gè)需求。

  1. export default { 
  2.   name'App'
  3.   computed: { 
  4.     reverseMessage: function(){ 
  5.       return this.message.split('').reverse().join(''); 
  6.     } 
  7.   }, 
  8.   data() { 
  9.     return { 
  10.       message: 'Hello' 
  11.     } 
  12.   } 

在以上代碼中我們定義了一個(gè)計(jì)算屬性:reverseMessage,其值為一個(gè)函數(shù)并返回我們需要的結(jié)果。

之后在模板中就可以像使用message一樣使用reverseMessage。

  1. <template> 
  2.   <div id="app"
  3.     <p>{{message}}</p> 
  4.     <p>{{reverseMessage}}</p> 
  5.   </div> 
  6. </template> 

那么此時(shí)有人肯定要說(shuō)了,我用methods也能實(shí)現(xiàn)呀。確實(shí)使用methods也能實(shí)現(xiàn)此種需求,但是在這種情況下我們的計(jì)算屬性相較于methods是有很大優(yōu)勢(shì)的,這個(gè)優(yōu)勢(shì)就是計(jì)算屬性存在緩存。

如果我們使用methods實(shí)現(xiàn)前面的需求,當(dāng)message的反轉(zhuǎn)結(jié)果有多個(gè)地方在使用,對(duì)應(yīng)的methods函數(shù)會(huì)被調(diào)用多次,函數(shù)內(nèi)部的邏輯也需要執(zhí)行多次;而計(jì)算屬性因?yàn)榇嬖诰彺妫灰猰essage數(shù)據(jù)未發(fā)生變化,則多次訪(fǎng)問(wèn)計(jì)算屬性對(duì)應(yīng)的函數(shù)只會(huì)執(zhí)行一次。

  1. <template> 
  2.   <div id="app"
  3.     <p>{{message}}</p> 
  4.     <p>第一次訪(fǎng)問(wèn)reverseMessage:{{reverseMessage}}</p> 
  5.     <p>第二次訪(fǎng)問(wèn)reverseMessage:{{reverseMessage}}</p> 
  6.     <p>第三次訪(fǎng)問(wèn)reverseMessage:{{reverseMessage}}</p> 
  7.     <p>第四次訪(fǎng)問(wèn)reverseMessage:{{reverseMessage}}</p> 
  8.   </div> 
  9. </template> 
  10.  
  11. <script> 
  12. export default { 
  13.   name'App'
  14.   computed: { 
  15.     reverseMessage: function(value){ 
  16.       console.log(" I'm reverseMessage" ) 
  17.       return this.message.split('').reverse().join(''); 
  18.     } 
  19.   }, 
  20.   data() { 
  21.     return { 
  22.       message: 'Hello' 
  23.     } 
  24.   } 
  25. </script> 

運(yùn)行項(xiàng)目,查看結(jié)果,會(huì)發(fā)現(xiàn)計(jì)算屬性reverseMessage對(duì)應(yīng)的函數(shù)只執(zhí)行了一次。

圖片

 

3. Vue中的watchVue

中的watch又名為偵聽(tīng)屬性,它主要用于偵聽(tīng)數(shù)據(jù)的變化,在數(shù)據(jù)發(fā)生變化的時(shí)候執(zhí)行一些操作。

  1. <template> 
  2.   <div id="app"
  3.     <p>計(jì)數(shù)器:{{counter}}</p> 
  4.     <el-button type="primary" @click="counter++"
  5.       Click 
  6.     </el-button> 
  7.   </div> 
  8. </template> 
  9.  
  10. <script> 
  11. export default { 
  12.   name'App'
  13.   data() { 
  14.     return { 
  15.       counter: 0 
  16.     } 
  17.   }, 
  18.   watch: { 
  19.     /** 
  20.      * @name: counter 
  21.      * @description:  
  22.      *   監(jiān)聽(tīng)Vue data中的counter數(shù)據(jù) 
  23.      *   當(dāng)counter發(fā)生變化時(shí)會(huì)執(zhí)行對(duì)應(yīng)的偵聽(tīng)函數(shù) 
  24.      * @param {*} newValue counter的新值 
  25.      * @param {*} oldValue counter的舊值 
  26.      * @return {*} None 
  27.      */ 
  28.     counter: function(newValue, oldValue){ 
  29.       if(this.counter == 10){ 
  30.         this.counter = 0; 
  31.       } 
  32.     } 
  33.   } 
  34. </script> 

我們定義了一個(gè)偵聽(tīng)屬性counter,該屬性偵聽(tīng)的是Vue data中定義counter數(shù)據(jù),整個(gè)的邏輯就是點(diǎn)擊按鈕counter加1,當(dāng)counter等于10的時(shí)候,將counter置為0。

上面的代碼運(yùn)行后的結(jié)果如下:

 

圖片

 

Vue官網(wǎng)很明確的建議我們這樣使用watch偵聽(tīng)屬性:當(dāng)需要在數(shù)據(jù)變化時(shí)執(zhí)行異步或開(kāi)銷(xiāo)較大的操作時(shí),這個(gè)方式是最有用的。

4. computed和watch之間的抉擇

看完以上兩部分內(nèi)容,關(guān)于Vue中computed和watch的基本用法算是掌握了。但實(shí)際上不止這些,所以接下來(lái)我們?cè)趤?lái)進(jìn)階學(xué)習(xí)一波。

這里我們還原Vue官網(wǎng)中的一個(gè)示例,示例實(shí)現(xiàn)的功能大致如下:

 

圖片

 

該功能可以簡(jiǎn)單的描述為:在firstName和lastName數(shù)據(jù)發(fā)生變化時(shí),對(duì)fullName進(jìn)行更新,其中fullName的值為firstName和lastName的拼接。

首先我們使用watch來(lái)實(shí)現(xiàn)該功能:watch偵聽(tīng)firstName和lastName,當(dāng)這兩個(gè)數(shù)據(jù)發(fā)生變化時(shí)更新fullName的值。

  1. <template> 
  2.   <div id="app"
  3.     <p>firstName: <el-input v-model="firstName" placeholder="請(qǐng)輸入firstName"></el-input></p> 
  4.     <p>lastName: <el-input v-model="lastName" placeholder="請(qǐng)輸入lastName"></el-input></p> 
  5.     <p>fullName: {{fullName}}</p> 
  6.   </div> 
  7. </template> 
  8.  
  9. <script> 
  10. export default { 
  11.   name'App'
  12.   data() { 
  13.     return { 
  14.       firstName: ''
  15.       lastName: ''
  16.       fullName: '(空)' 
  17.     } 
  18.   }, 
  19.   // 使用watch實(shí)現(xiàn) 
  20.   watch: { 
  21.     firstName: function(newValue) { 
  22.       this.fullName = newValue + ' ' + this.lastName; 
  23.     }, 
  24.     lastName: function(newValue){ 
  25.       this.fullName = this.firstName + ' ' + newValue; 
  26.     } 
  27.   } 
  28. </script> 

接著我們?cè)谑褂胏omputed來(lái)實(shí)現(xiàn):定義計(jì)算屬性fullName,將firstName和lastName的值進(jìn)行拼接并返回。

  1. <template> 
  2.   <div id="app"
  3.     <p>firstName: <el-input v-model="firstName" placeholder="請(qǐng)輸入firstName"></el-input></p> 
  4.     <p>lastName: <el-input v-model="lastName" placeholder="請(qǐng)輸入lastName"></el-input></p> 
  5.     <p>fullName: {{fullName}}</p> 
  6.   </div> 
  7. </template> 
  8.  
  9. <script> 
  10. export default { 
  11.   name'App'
  12.   data() { 
  13.     return { 
  14.       firstName: ''
  15.       lastName: '' 
  16.     } 
  17.   } 
  18.   computed: { 
  19.     fullName: function() { 
  20.       return this.firstName + ' ' + this.lastName; 
  21.     } 
  22.   }  
  23. </script> 

我們發(fā)現(xiàn)computed和watch都可以實(shí)現(xiàn)這個(gè)功能,但是我們?cè)趯?duì)比一下這兩種不同的實(shí)現(xiàn)方式:

  1. // 使用computed實(shí)現(xiàn) 
  2. computed: { 
  3.   fullName: function() { 
  4.     return this.firstName + ' ' + this.lastName; 
  5.   } 
  6. },  
  7. // 使用watch實(shí)現(xiàn) 
  8. watch: { 
  9.   firstName: function(newValue) { 
  10.     this.fullName = newValue + ' ' + this.lastName; 
  11.   }, 
  12.   lastName: function(newValue){ 
  13.     this.fullName = this.firstName + ' ' + newValue; 
  14.   } 

對(duì)比之下很明顯的會(huì)發(fā)現(xiàn)發(fā)現(xiàn)computed的實(shí)現(xiàn)方式更簡(jiǎn)潔高級(jí)。

所以在日常項(xiàng)目開(kāi)發(fā)中,對(duì)于computed和watch的使用要慎重選擇:

 

圖片

 

這兩者選擇和使用沒(méi)有對(duì)錯(cuò)之分,只是希望能更好的使用,而不是濫用。

5. 計(jì)算屬性進(jìn)階

接下來(lái)我們?cè)趯?duì)計(jì)算屬性的內(nèi)容進(jìn)行進(jìn)階學(xué)習(xí)。

>>> 5.1 計(jì)算屬性不能和Vue Data屬性同名

在聲明計(jì)算屬性的時(shí)候,計(jì)算屬性是不能和Vue Data中定義的屬性同名,否則會(huì)出現(xiàn)錯(cuò)誤:The computed property "xxxxx" is already defined in data。

如果有閱讀過(guò)Vue源碼的同學(xué)對(duì)這個(gè)原因應(yīng)該會(huì)比較清楚,Vue在初始化的時(shí)候會(huì)按照:initProps-> initMethods -> initData -> initComputed -> initWatch這樣的順序?qū)?shù)據(jù)進(jìn)行初始化,并且會(huì)通過(guò)Object.definedProperty將數(shù)據(jù)定義到vm實(shí)例上,在這個(gè)過(guò)程中同名的屬性會(huì)被后面的同名屬性覆蓋。

通過(guò)打印組件實(shí)例對(duì)象,可以很清楚的看到props、methods、data、computed會(huì)被定義到vm實(shí)例上。

>>> 5.2 計(jì)算屬性的set函數(shù)

在前面代碼示例中,我們的computed是這么實(shí)現(xiàn)的:

  1. computed: { 
  2.   reverseMessage: function(){ 
  3.     return this.message.split('').reverse().join(''); 
  4.   } 
  5. }, 

這種寫(xiě)法實(shí)際上是給reverseMessage提供了一個(gè)get方法,所以上面的寫(xiě)法等同于:

  1. computed: { 
  2.   reverseMessage: { 
  3.     // 計(jì)算屬性的get方法 
  4.     get: function(){ 
  5.       return this.message.split('').reverse().join(''); 
  6.     } 
  7.   } 
  8. }, 

除此之外,我們也可以給計(jì)算屬性提供一個(gè)set方法:

  1. computed: { 
  2.   reverseMessage: { 
  3.     // 計(jì)算屬性的get方法 
  4.     get: function(){ 
  5.       return this.message.split('').reverse().join(''); 
  6.     }, 
  7.     setfunction(newValue){ 
  8.        // set方法的邏輯 
  9.     } 
  10.   } 
  11. }, 

只有我們主動(dòng)修改了計(jì)算屬性的值,set方法才會(huì)被觸發(fā)。

關(guān)于計(jì)算屬性的set方法在實(shí)際的項(xiàng)目開(kāi)發(fā)中暫時(shí)還沒(méi)有遇到,不過(guò)經(jīng)過(guò)一番思考,做出來(lái)下面這樣一個(gè)示例:

 

圖片

 

這個(gè)示例是分鐘和小時(shí)之間的一個(gè)轉(zhuǎn)化,利用計(jì)算屬性的set方法就能很好實(shí)現(xiàn):

  1. <template> 
  2.   <div id="app"
  3.     <p>分鐘<el-input v-model="minute" placeholder="請(qǐng)輸入內(nèi)容"></el-input></p> 
  4.     <p>小時(shí)<el-input v-model="hours" placeholder="請(qǐng)輸入內(nèi)容"></el-input></p> 
  5.   </div> 
  6. </template> 
  7.  
  8. <script> 
  9. export default { 
  10.   name'App'
  11.   data() { 
  12.     return { 
  13.       minute: 60, 
  14.     } 
  15.   }, 
  16.   computed: { 
  17.     hours:{ 
  18.       get: function() { 
  19.         return this.minute / 60; 
  20.       }, 
  21.       setfunction(newValue) { 
  22.         this.minute = newValue * 60; 
  23.       } 
  24.     } 
  25.   }  
  26. </script> 

>>> 5.3 計(jì)算屬性的緩存

前面我們總結(jié)過(guò)計(jì)算屬性存在緩存,并演示相關(guān)的示例。那計(jì)算屬性的緩存是如何實(shí)現(xiàn)的呢?

關(guān)于計(jì)算屬性的緩存這個(gè)知識(shí)點(diǎn)需要我們?nèi)ラ喿xVue的源碼實(shí)現(xiàn),所以我們一起來(lái)看看源碼吧。

相信大家看到源碼這個(gè)詞就會(huì)有點(diǎn)膽戰(zhàn)心驚,不過(guò)不用過(guò)分擔(dān)心,文章寫(xiě)到這里的時(shí)候考慮到本篇文章的內(nèi)容和側(cè)重點(diǎn),所以不會(huì)詳細(xì)去解讀計(jì)算屬性的源碼,著重學(xué)習(xí)計(jì)算屬性的緩存實(shí)現(xiàn),并且點(diǎn)到為止。

那如果你沒(méi)有仔細(xì)解讀過(guò)Vue的響應(yīng)式原理,那建議忽略這一節(jié)的內(nèi)容,等對(duì)源碼中的響應(yīng)式有一定了解之后在來(lái)看這一節(jié)的內(nèi)容會(huì)更容易理解。(我自己之前也寫(xiě)過(guò)的一篇相關(guān)文章:1W字長(zhǎng)文+多圖,帶你了解vue2.x的雙向數(shù)據(jù)綁定源碼實(shí)現(xiàn),點(diǎn)擊文末閱讀原文即可查看)

關(guān)于計(jì)算屬性的入口源代碼如下:

  1. /* 
  2. * Vue版本:v2.6.12 
  3. * 代碼位置:/vue/src/core/instance/state.js 
  4. */ 
  5. export function initState (vm: Component) { 
  6.   // ......省略...... 
  7.   const opts = vm.$options 
  8.   // ......省略...... 
  9.   if (opts.computed) initComputed(vm, opts.computed) 
  10.   // ......省略                                                                       ...... 

接著我們來(lái)看看initComputed:

  1. /* 
  2. * Vue版本:v2.6.12 
  3. * 代碼位置:/vue/src/core/instance/state.js 
  4. * @params: vm        vue實(shí)例對(duì)象 
  5. * @params: computed  所有的計(jì)算屬性 
  6. */ 
  7. function initComputed (vm: Component, computed: Object) { 
  8.   
  9.   /*  
  10.   * Object.create(null):創(chuàng)建一個(gè)空對(duì)象 
  11.   * 定義的const watchers是用于保存所有計(jì)算屬性的Watcher實(shí)例 
  12.   */ 
  13.   const watchers = vm._computedWatchers = Object.create(null
  14.   
  15.   // 遍歷計(jì)算屬性 
  16.   for (const key in computed) { 
  17.     const userDef = computed[key
  18.     /* 
  19.     * 獲取計(jì)算屬性的get方法  
  20.     * 計(jì)算屬性可以是function,默認(rèn)提供的是get方法 
  21.     * 也可以是對(duì)象,分別聲明get、set方法 
  22.     */ 
  23.     const getter = typeof userDef === 'function' ? userDef : userDef.get 
  24.     
  25.     /*  
  26.     * 給計(jì)算屬性創(chuàng)建watcher 
  27.     * @params: vm       vue實(shí)例對(duì)象  
  28.     * @params: getter   計(jì)算屬性的get方法 
  29.     * @params: noop      
  30.           noop是定義在 /vue/src/shared/util.js中的一個(gè)函數(shù) 
  31.           export function noop (a?: any, b?: any, c?: any) {} 
  32.     * @params: computedWatcherOptions 
  33.     *     computedWatcherOptions是一個(gè)對(duì)象,定義在本文件的167行 
  34.     *     const computedWatcherOptions = { lazy: true } 
  35.     */ 
  36.     watchers[key] = new Watcher( 
  37.       vm, 
  38.       getter || noop, 
  39.       noop, 
  40.       computedWatcherOptions 
  41.     ) 
  42.     // 函數(shù)調(diào)用 
  43.     defineComputed(vm, key, userDef) 
  44.   } 

在initComputed這個(gè)函數(shù)中,主要是遍歷計(jì)算屬性,然后在遍歷的過(guò)程中做了下面兩件事:

  • 第一件:為計(jì)算屬性創(chuàng)建watcher,即new Watcher
  • 第二件:調(diào)用defineComputed方法

那首先我們先來(lái)看看new Watcher都做了什么。

為了方便大家看清楚new Watcher的作用,我將Watcher的源碼進(jìn)行了簡(jiǎn)化,保留了一些比較重要的代碼。

同時(shí)代碼中重要的部分都添加了注釋?zhuān)行┳⑨屆枋龅目赡苡悬c(diǎn)重復(fù)或者啰嗦,但主要是想以這種重復(fù)的方式讓大家可以反復(fù)琢磨并理解源碼中的內(nèi)容,方便后續(xù)的理解 ~

  1. /* 
  2. * Vue版本:v2.6.12 
  3. * 代碼位置: /vue/src/core/observer/watcher.js 
  4. * 為了看清楚Watcher的作用 
  5. * 將源碼進(jìn)行簡(jiǎn)化,所以下面是一個(gè)簡(jiǎn)化版的Watcher類(lèi) 
  6. * 同時(shí)部分代碼順序有所調(diào)整 
  7. */ 
  8. export default class Watcher { 
  9.   constructor ( 
  10.     vm: Component, 
  11.     expOrFn: string | Function
  12.     cb: Function
  13.     options?: ?Object, 
  14.   ) { 
  15.     // vm為組件實(shí)例 
  16.     this.vm = vm   
  17.     // expOrFn在new Watcher時(shí)傳遞的參數(shù)為計(jì)算屬性的get方法 
  18.     // 將計(jì)算屬性的get方法賦值給watcher的getter屬性 
  19.     this.getter = expOrFn 
  20.     // cb為noop:export function noop (a?: any, b?: any, c?: any) {} 
  21.     this.cb = cb   
  22.     // option在new Watcher傳遞的參數(shù)值為{lazy: true}  
  23.     // !!操作符即將options.lazy強(qiáng)轉(zhuǎn)為boolean類(lèi)型 
  24.     // 賦值之后this.lazy的值為true 
  25.     this.lazy = !!options.lazy  
  26.     // 賦值之后this.dirty的值true 
  27.     this.dirty = this.lazy  
  28.      
  29.     /* 
  30.     * 在new Watcher的時(shí)候因?yàn)閠his.lazy的值為true 
  31.     * 所以this.value的值還是undefined 
  32.     */ 
  33.     this.value = this.lazy ? undefined : this.get() 
  34.   } 
  35.   get () {  
  36.     const vm = this.vm 
  37.     /* 
  38.     * 在構(gòu)造函數(shù)中,計(jì)算屬性的get方法賦值給了watcher的getter屬性 
  39.     * 所以該行代碼即調(diào)用計(jì)算屬性的get方法,獲取計(jì)算屬性的值 
  40.     */ 
  41.     value = this.getter.call(vm, vm) 
  42.     return value 
  43.   } 
  44.   evaluate () { 
  45.     /* 
  46.     * 調(diào)用watcher的get方法 
  47.     * watcher的get方法邏輯為:調(diào)用計(jì)算屬性的get方法獲取計(jì)算屬性的值并返回 
  48.     * 所以evaluate函數(shù)也就是獲取計(jì)算屬性的值,并賦值給watcher.value 
  49.     * 并且將watcher.dirty置為false,這個(gè)dirty是實(shí)現(xiàn)緩存的關(guān)鍵 
  50.     */  
  51.     this.value = this.get() 
  52.     this.dirty = false 
  53.   } 

看了這個(gè)簡(jiǎn)化版的Watcher以后,想必我們已經(jīng)很清楚的知道了Watcher類(lèi)的實(shí)現(xiàn)。

那接下來(lái)就是關(guān)于緩存的重點(diǎn)了,也就是遍歷計(jì)算屬性做的第二件事:調(diào)用defineComputed函數(shù):

  1. /* 
  2. * Vue版本:v2.6.12 
  3. * 代碼位置:/vue/src/core/instance/state.js 
  4. * @params: target  vue實(shí)例對(duì)象 
  5. * @params: key     計(jì)算屬性名 
  6. * @params: userDef 計(jì)算屬性定義的function或者object 
  7. */ 
  8. export function defineComputed ( 
  9.   target: any
  10.   key: string, 
  11.   userDef: Object | Function 
  12. ) {   
  13.  
  14.   // ......暫時(shí)省略有關(guān)sharedPropertyDefinition的代碼邏輯...... 
  15.    
  16.   /* 
  17.   * sharedPropertyDefinition本身是一個(gè)對(duì)象,定義在本文件31行: 
  18.   * const sharedPropertyDefinition = { 
  19.   *   enumerable: true
  20.   *   configurable: true
  21.   *   get: noop, 
  22.   *   set: noop 
  23.   * } 
  24.   * 最后使用Object.defineProperty傳入對(duì)應(yīng)的參數(shù)使得計(jì)算屬性變得可觀測(cè) 
  25.   */ 
  26.   Object.defineProperty(target, key, sharedPropertyDefinition) 

defineComputed方法最核心也只有一行代碼,也就是使用Object.defineProperty將計(jì)算屬性變得可觀測(cè)。

那么接下來(lái)我們的關(guān)注點(diǎn)就是調(diào)用Object.defineProperty函數(shù)時(shí)傳遞的第三個(gè)參數(shù):sharedPropertyDefinition。

sharedPropertyDefinition是定義在當(dāng)前文件中的一個(gè)對(duì)象,默認(rèn)值如下:

  1. const sharedPropertyDefinition = { 
  2.   enumerable: true
  3.   configurable: true
  4.   get: noop, 
  5.   set: noop 

前面貼出來(lái)的defineComputed源碼中,我注釋說(shuō)明省略了一段有關(guān)sharedPropertyDefinition的代碼邏輯,那省略的這段源代碼就不展示了,它的主要作用就是在對(duì)sharedPropertyDefinition.get和sharedPropertyDefinition.set進(jìn)行重寫(xiě),重寫(xiě)之后sharedPropertyDefinition的值為:

  1. const sharedPropertyDefinition = { 
  2.   enumerable: true
  3.   configurable: true
  4.   get: function(){ 
  5.       // 獲取計(jì)算屬性對(duì)應(yīng)的watcher實(shí)例 
  6.       const watcher = this._computedWatchers && this._computedWatchers[key
  7.       if (watcher) { 
  8.         if (watcher.dirty) { 
  9.           watcher.evaluate() 
  10.         } 
  11.         if (Dep.target) { 
  12.           watcher.depend() 
  13.         } 
  14.         return watcher.value 
  15.       } 
  16.     } 
  17.   }, 
  18.   // set對(duì)應(yīng)的值這里寫(xiě)的是noop 
  19.   // 但是我們要知道set真正的值是我們?yōu)橛?jì)算屬性提供的set函數(shù) 
  20.   // 千萬(wàn)不要理解錯(cuò)了哦  
  21.   set: noop,   

那sharedPropertyDefinition.get函數(shù)的邏輯已經(jīng)非常的清晰了,同時(shí)它的邏輯就是計(jì)算屬性緩存實(shí)現(xiàn)的關(guān)鍵邏輯:在sharedPropertyDefinition.get函數(shù)中,先獲取到計(jì)算屬性對(duì)應(yīng)的watcher實(shí)例;然后判斷watcher.dirty的值,如果該值為false,則直接返回watcher.value;否則調(diào)用watcher.evaluate()重新獲取計(jì)算屬性的值。

關(guān)于計(jì)算屬性緩存的源碼分析就到這里,相信大家對(duì)計(jì)算屬性的緩存實(shí)現(xiàn)已經(jīng)有了一定的認(rèn)識(shí)。不過(guò)僅僅是了解這些還不夠,我們應(yīng)該去通讀計(jì)算屬性的完整源碼實(shí)現(xiàn),才能對(duì)計(jì)算屬性有一個(gè)更通透的認(rèn)識(shí)。

6. 偵聽(tīng)屬性進(jìn)階

>>> 6.1 handler

前面我們是這樣實(shí)現(xiàn)偵聽(tīng)屬性的:

  1. watch: { 
  2.   counter: function(newValue, oldValue){ 
  3.     if(this.counter == 10){ 
  4.       this.counter = 0; 
  5.     } 
  6.   } 

那上面的這種寫(xiě)法等同于給counter提供一個(gè)handler函數(shù):

  1. watch: { 
  2.   counter: { 
  3.     handler: function(newValue, oldValue){ 
  4.       if(this.counter == 10){ 
  5.         this.counter = 0; 
  6.       } 
  7.     } 
  8.   } 

>>> 6.2 immediate

正常情況下,偵聽(tīng)屬性提供的函數(shù)是不會(huì)立即執(zhí)行的,只有在對(duì)應(yīng)的vue data發(fā)生變化時(shí),偵聽(tīng)屬性對(duì)應(yīng)的函數(shù)才會(huì)執(zhí)行。

那如果我們需要偵聽(tīng)屬性對(duì)應(yīng)的函數(shù)立即執(zhí)行一次,就可以給偵聽(tīng)屬性提供一個(gè)immediate選項(xiàng),并設(shè)置其值為true。

  1. watch: { 
  2.   counter: { 
  3.     handler: function(newValue, oldValue){ 
  4.       if(this.counter == 10){ 
  5.         this.counter = 0; 
  6.       } 
  7.     }, 
  8.     immediate: true 
  9.   } 

>>> 6.3 deep

如果我們對(duì)一個(gè)對(duì)象類(lèi)型的vue data進(jìn)行偵聽(tīng),當(dāng)這個(gè)對(duì)象內(nèi)的屬性發(fā)生變化時(shí),默認(rèn)是不會(huì)觸發(fā)偵聽(tīng)函數(shù)的。

  1. <template> 
  2.   <div id="app"
  3.     <p><el-input v-model="person.name" placeholder="請(qǐng)輸入姓名"></el-input></p> 
  4.     <p><el-input v-model="person.age" placeholder="請(qǐng)輸入年齡"></el-input></p> 
  5.   </div> 
  6. </template> 
  7. <script> 
  8. export default { 
  9.   name'App'
  10.   data() { 
  11.     return { 
  12.       person: { 
  13.         name'jack'
  14.         age: 20 
  15.       } 
  16.     } 
  17.   }, 
  18.   watch: { 
  19.     person: function(newValue){ 
  20.       console.log(newValue.name + ' ' + newValue.age); 
  21.     } 
  22.   } 
  23. </script> 

監(jiān)聽(tīng)對(duì)象類(lèi)型的數(shù)據(jù),偵聽(tīng)函數(shù)沒(méi)有觸發(fā):

 

圖片

 

通過(guò)給偵聽(tīng)屬性提供deep: true就可以偵聽(tīng)到對(duì)象內(nèi)部屬性的變化:

  1. watch: { 
  2.   person: { 
  3.     handler: function(newValue){ 
  4.       console.log(newValue.name + ' ' + newValue.age); 
  5.     }, 
  6.     deep: true 
  7.   } 
圖片

 

不過(guò)仔細(xì)觀察上面的示例會(huì)發(fā)現(xiàn)這種方式去監(jiān)聽(tīng)Object類(lèi)型的數(shù)據(jù),Object數(shù)據(jù)內(nèi)部任一屬性發(fā)生變化都會(huì)觸發(fā)偵聽(tīng)函數(shù),那如果我們想單獨(dú)偵聽(tīng)對(duì)象中的某個(gè)屬性,可以使用下面這樣的方式:

  1. watch: { 
  2.   'person.name'function(newValue, oldValue){ 
  3.       // 邏輯 
  4.    } 

7.總結(jié)

到此本篇文章就結(jié)束了,內(nèi)容非常的簡(jiǎn)單易懂,在此將以上的內(nèi)容做以總結(jié):

 

圖片

 

學(xué)無(wú)止境,除了基礎(chǔ)的內(nèi)容之外,很多特性的實(shí)現(xiàn)原理也是我們應(yīng)該關(guān)注的東西,但是介于本篇文章輸出的初衷,所以對(duì)原理實(shí)現(xiàn)并沒(méi)有完整的分析,后面有機(jī)會(huì)在總結(jié)~

原文鏈接:https://mp.weixin.qq.com/s/frWBXTa-EnhkGFn6c1X5NQ

 

責(zé)任編輯:武曉燕 來(lái)源: 不知名寶藏程序媛
相關(guān)推薦

2022-07-14 08:22:48

Computedvue3

2024-03-08 10:38:07

Vue響應(yīng)式數(shù)據(jù)

2021-12-08 09:09:33

Vue 3 Computed Vue2

2023-11-28 17:49:51

watch?computed?性能

2023-03-29 14:25:08

Vue.js前端框架

2022-06-09 08:28:27

Vue3watchwatchEffec

2022-06-26 00:00:02

Vue3響應(yīng)式系統(tǒng)

2023-12-11 07:34:37

Computed計(jì)算屬性Vue3

2021-12-07 05:44:45

Vue 3 Watch WatchEffect

2022-04-11 08:03:30

Vue.jscomputed計(jì)算屬性

2024-10-17 16:08:36

SQL 查詢(xún)SQL

2024-05-27 08:39:17

Vue3變量響應(yīng)式

2022-04-12 08:08:57

watch函數(shù)options封裝

2025-04-07 08:50:36

2024-03-22 08:57:04

Vue3Emoji表情符號(hào)

2024-03-21 08:34:49

Vue3WebSocketHTTP

2009-06-25 15:20:28

CollectionMap

2024-04-08 07:28:27

PiniaVue3狀態(tài)管理庫(kù)

2021-06-26 06:29:14

Vue 2Vue 3開(kāi)發(fā)

2021-09-22 15:00:24

Linuxwatch 命令
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)