欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入理解Vue Computed計(jì)算屬性原理

 更新時(shí)間:2018年05月29日 09:01:17   作者:半年的半年  
Computed 計(jì)算屬性是 Vue 中常用的一個(gè)功能,本篇文章主要介紹了Vue Computed 計(jì)算屬性原理,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

Computed 計(jì)算屬性是 Vue 中常用的一個(gè)功能,但你理解它是怎么工作的嗎?

拿官網(wǎng)簡(jiǎn)單的例子來看一下:

<div id="example">
 <p>Original message: "{{ message }}"</p>
 <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
 el: '#example',
 data: {
  message: 'Hello'
 },
 computed: {
  // a computed getter
  reversedMessage: function () {
   // `this` points to the vm instance
   return this.message.split('').reverse().join('')
  }
 }
})

Situation

Vue 里的 Computed 屬性非常頻繁的被使用到,但并不是很清楚它的實(shí)現(xiàn)原理。比如:計(jì)算屬性如何與屬性建立依賴關(guān)系?屬性發(fā)生變化又如何通知到計(jì)算屬性重新計(jì)算?

關(guān)于如何建立依賴關(guān)系,我的第一個(gè)想到的就是語法解析,但這樣太浪費(fèi)性能,因此排除,第二個(gè)想到的就是利用 JavaScript 單線程的原理和 Vue 的 Getter 設(shè)計(jì),通過一個(gè)簡(jiǎn)單的發(fā)布訂閱,就可以在一次計(jì)算屬性求值的過程中收集到相關(guān)依賴。

因此接下來的任務(wù)就是從 Vue 源碼一步步分析 Computed 的實(shí)現(xiàn)原理。

Task

分析依賴收集實(shí)現(xiàn)原理,分析動(dòng)態(tài)計(jì)算實(shí)現(xiàn)原理。

Action

data 屬性初始化 getter setter:

// src/observer/index.js

// 這里開始轉(zhuǎn)換 data 的 getter setter,原始值已存入到 __ob__ 屬性中
Object.defineProperty(obj, key, {
 enumerable: true,
 configurable: true,
 get: function reactiveGetter () {
  const value = getter ? getter.call(obj) : val
  // 判斷是否處于依賴收集狀態(tài)
  if (Dep.target) {
   // 建立依賴關(guān)系
   dep.depend()
   ...
  }
  return value
 },
 set: function reactiveSetter (newVal) {
  ...
  // 依賴發(fā)生變化,通知到計(jì)算屬性重新計(jì)算
  dep.notify()
 }
})

computed 計(jì)算屬性初始化

// src/core/instance/state.js

// 初始化計(jì)算屬性
function initComputed (vm: Component, computed: Object) {
 ...
 // 遍歷 computed 計(jì)算屬性
 for (const key in computed) {
  ...
  // 創(chuàng)建 Watcher 實(shí)例
  // create internal watcher for the computed property.
  watchers[key] = new Watcher(vm, getter || noop, noop, computedWatcherOptions)

  // 創(chuàng)建屬性 vm.reversedMessage,并將提供的函數(shù)將用作屬性 vm.reversedMessage 的 getter,
  // 最終 computed 與 data 會(huì)一起混合到 vm 下,所以當(dāng) computed 與 data 存在重名屬性時(shí)會(huì)拋出警告
  defineComputed(vm, key, userDef)
  ...
 }
}

export function defineComputed (target: any, key: string, userDef: Object | Function) {
 ...
 // 創(chuàng)建 get set 方法
 sharedPropertyDefinition.get = createComputedGetter(key)
 sharedPropertyDefinition.set = noop
 ...
 // 創(chuàng)建屬性 vm.reversedMessage,并初始化 getter setter
 Object.defineProperty(target, key, sharedPropertyDefinition)
}

function createComputedGetter (key) {
 return function computedGetter () {
  const watcher = this._computedWatchers && this._computedWatchers[key]
  if (watcher) {
   if (watcher.dirty) {
    // watcher 暴露 evaluate 方法用于取值操作
    watcher.evaluate()
   }
   // 同第1步,判斷是否處于依賴收集狀態(tài)
   if (Dep.target) {
    watcher.depend()
   }
   return watcher.value
  }
 }
}

無論是屬性還是計(jì)算屬性,都會(huì)生成一個(gè)對(duì)應(yīng)的 watcher 實(shí)例。

// src/core/observer/watcher.js

// 當(dāng)通過 vm.reversedMessage 獲取計(jì)算屬性時(shí),就會(huì)進(jìn)到這個(gè) getter 方法
get () {
 // this 指的是 watcher 實(shí)例
 // 將當(dāng)前 watcher 實(shí)例暫存到 Dep.target,這就表示開啟了依賴收集任務(wù)
 pushTarget(this)
 let value
 const vm = this.vm
 try {
  // 在執(zhí)行 vm.reversedMessage 的函調(diào)函數(shù)時(shí),會(huì)觸發(fā)屬性(步驟1)和計(jì)算屬性(步驟2)的 getter
  // 在這個(gè)執(zhí)行過程中,就可以收集到 vm.reversedMessage 的依賴了
  value = this.getter.call(vm, vm)
 } catch (e) {
  if (this.user) {
   handleError(e, vm, `getter for watcher "${this.expression}"`)
  } else {
   throw e
  }
 } finally {
  if (this.deep) {
   traverse(value)
  }
  // 結(jié)束依賴收集任務(wù)
  popTarget()
  this.cleanupDeps()
 }
 return value
}

上面多出提到了 dep.depend, dep.notify, Dep.target,那么 Dep 究竟是什么呢?

Dep 的代碼短小精悍,但卻承擔(dān)著非常重要的依賴收集環(huán)節(jié)。

// src/core/observer/dep.js

export default class Dep {
 static target: ?Watcher;
 id: number;
 subs: Array<Watcher>;

 constructor () {
  this.id = uid++
  this.subs = []
 }

 addSub (sub: Watcher) {
  this.subs.push(sub)
 }

 removeSub (sub: Watcher) {
  remove(this.subs, sub)
 }

 depend () {
  if (Dep.target) {
   Dep.target.addDep(this)
  }
 }

 notify () {
  const subs = this.subs.slice()
  for (let i = 0, l = subs.length; i < l; i++) {
   // 更新 watcher 的值,與 watcher.evaluate() 類似,
   // 但 update 是給依賴變化時(shí)使用的,包含對(duì) watch 的處理
   subs[i].update()
  }
 }
}

// 當(dāng)首次計(jì)算 computed 屬性的值時(shí),Dep 將會(huì)在計(jì)算期間對(duì)依賴進(jìn)行收集
Dep.target = null
const targetStack = []

export function pushTarget (_target: Watcher) {
 // 在一次依賴收集期間,如果有其他依賴收集任務(wù)開始(比如:當(dāng)前 computed 計(jì)算屬性嵌套其他 computed 計(jì)算屬性),
 // 那么將會(huì)把當(dāng)前 target 暫存到 targetStack,先進(jìn)行其他 target 的依賴收集,
 if (Dep.target) targetStack.push(Dep.target)
 Dep.target = _target
}

export function popTarget () {
 // 當(dāng)嵌套的依賴收集任務(wù)完成后,將 target 恢復(fù)為上一層的 Watcher,并繼續(xù)做依賴收集
 Dep.target = targetStack.pop()
}

Result

總結(jié)一下依賴收集、動(dòng)態(tài)計(jì)算的流程:

1. data 屬性初始化 getter setter

2. computed 計(jì)算屬性初始化,提供的函數(shù)將用作屬性 vm.reversedMessage 的 getter

3. 當(dāng)首次獲取 reversedMessage 計(jì)算屬性的值時(shí),Dep 開始依賴收集

4. 在執(zhí)行 message getter 方法時(shí),如果 Dep 處于依賴收集狀態(tài),則判定 message 為 reversedMessage 的依賴,并建立依賴關(guān)系

5. 當(dāng) message 發(fā)生變化時(shí),根據(jù)依賴關(guān)系,觸發(fā) reverseMessage 的重新計(jì)算
到此,整個(gè) Computed 的工作流程就理清楚了。

Vue 是一個(gè)設(shè)計(jì)非常優(yōu)美的框架,使用 Getter Setter 設(shè)計(jì)使依賴關(guān)系實(shí)現(xiàn)的非常順其自然,使用計(jì)算與渲染分離的設(shè)計(jì)(優(yōu)先使用 MutationObserver,降級(jí)使用 setTimeout)也非常貼合瀏覽器計(jì)算引擎與排版引擎分離的的設(shè)計(jì)原理。

如果你想成為一名架構(gòu)師,不能只停留在框架的 API 使用層面。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • vue3 中使用 reactive 的問題小結(jié)

    vue3 中使用 reactive 的問題小結(jié)

    在 Vue 3 中,如果你使用 reactive 來定義一個(gè)響應(yīng)式對(duì)象,那么這個(gè)對(duì)象的屬性是不能被重新賦值的,因?yàn)?nbsp;reactive 會(huì)將對(duì)象的屬性轉(zhuǎn)換為 getter/setter,這樣 Vue 才能追蹤到屬性的變化,這篇文章主要介紹了vue3 中使用 reactive 的問題,需要的朋友可以參考下
    2024-03-03
  • 三分鐘讓你快速學(xué)會(huì)axios在vue項(xiàng)目中的基本用法(推薦!)

    三分鐘讓你快速學(xué)會(huì)axios在vue項(xiàng)目中的基本用法(推薦!)

    Axios是一個(gè)基于Promise用于瀏覽器和nodejs的HTTP客戶端,下面這篇文章主要給大家介紹了如何通過三分鐘讓你快速學(xué)會(huì)axios在vue項(xiàng)目中的基本用法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • Vue為什么要謹(jǐn)慎使用$attrs與$listeners

    Vue為什么要謹(jǐn)慎使用$attrs與$listeners

    這篇文章主要介紹了Vue為什么要謹(jǐn)慎使用$attrs與$listeners,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Vue?Vuex搭建vuex環(huán)境及vuex求和案例分享

    Vue?Vuex搭建vuex環(huán)境及vuex求和案例分享

    這篇文章主要介紹了Vue?Vuex搭建vuex環(huán)境及vuex求和案例分享,?Vue?中實(shí)現(xiàn)集中式狀態(tài)管理的一個(gè)?Vue?插件,對(duì)?vue?應(yīng)用中多個(gè)組件的共享狀態(tài)進(jìn)行集中式的管理,下文相關(guān)介紹,需要的朋友可以參考一下
    2022-04-04
  • vue利用Moment插件格式化時(shí)間的實(shí)例代碼

    vue利用Moment插件格式化時(shí)間的實(shí)例代碼

    Moment.js 是一個(gè) JavaScript 日期處理類庫,用于解析、檢驗(yàn)、操作、以及顯示日期。這篇文章主要給大家介紹了關(guān)于vue利用Moment插件格式化時(shí)間的相關(guān)資料,需要的朋友可以參考下
    2021-05-05
  • Vue+Echarts報(bào)錯(cuò)Cannot?set?properties?of?undefined?(setting?‘plate‘)

    Vue+Echarts報(bào)錯(cuò)Cannot?set?properties?of?undefined?(settin

    這篇文章主要介紹了Vue+Echarts報(bào)錯(cuò)Cannot?set?properties?of?undefined?(setting?‘plate‘)的問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • vue前后端分離如何解決每次請(qǐng)求session都會(huì)變的問題

    vue前后端分離如何解決每次請(qǐng)求session都會(huì)變的問題

    這篇文章主要介紹了vue前后端分離如何解決每次請(qǐng)求session都會(huì)變的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • 解決vux 中popup 組件Mask 遮罩在最上層的問題

    解決vux 中popup 組件Mask 遮罩在最上層的問題

    這篇文章主要介紹了解決vux 中popup 組件Mask 遮罩在最上層的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • Vue生命周期詳解

    Vue生命周期詳解

    本文詳細(xì)講解了Vue的生命周期,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • vue中$set與$delete的使用及說明

    vue中$set與$delete的使用及說明

    這篇文章主要介紹了vue中$set與$delete的使用及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10

最新評(píng)論