国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

深入理解Vue Computed計算屬性原理

2019-11-19 13:45:43
字體:
來源:轉載
供稿:網友

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

拿官網簡單的例子來看一下:

<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 屬性非常頻繁的被使用到,但并不是很清楚它的實現原理。比如:計算屬性如何與屬性建立依賴關系?屬性發生變化又如何通知到計算屬性重新計算?

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

因此接下來的任務就是從 Vue 源碼一步步分析 Computed 的實現原理。

Task

分析依賴收集實現原理,分析動態計算實現原理。

Action

data 屬性初始化 getter setter:

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

computed 計算屬性初始化

// src/core/instance/state.js// 初始化計算屬性function initComputed (vm: Component, computed: Object) { ... // 遍歷 computed 計算屬性 for (const key in computed) {  ...  // 創建 Watcher 實例  // create internal watcher for the computed property.  watchers[key] = new Watcher(vm, getter || noop, noop, computedWatcherOptions)  // 創建屬性 vm.reversedMessage,并將提供的函數將用作屬性 vm.reversedMessage 的 getter,  // 最終 computed 與 data 會一起混合到 vm 下,所以當 computed 與 data 存在重名屬性時會拋出警告  defineComputed(vm, key, userDef)  ... }}export function defineComputed (target: any, key: string, userDef: Object | Function) { ... // 創建 get set 方法 sharedPropertyDefinition.get = createComputedGetter(key) sharedPropertyDefinition.set = noop ... // 創建屬性 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步,判斷是否處于依賴收集狀態   if (Dep.target) {    watcher.depend()   }   return watcher.value  } }}

無論是屬性還是計算屬性,都會生成一個對應的 watcher 實例。

// src/core/observer/watcher.js// 當通過 vm.reversedMessage 獲取計算屬性時,就會進到這個 getter 方法get () { // this 指的是 watcher 實例 // 將當前 watcher 實例暫存到 Dep.target,這就表示開啟了依賴收集任務 pushTarget(this) let value const vm = this.vm try {  // 在執行 vm.reversedMessage 的函調函數時,會觸發屬性(步驟1)和計算屬性(步驟2)的 getter  // 在這個執行過程中,就可以收集到 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)  }  // 結束依賴收集任務  popTarget()  this.cleanupDeps() } return value}

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

Dep 的代碼短小精悍,但卻承擔著非常重要的依賴收集環節。

// src/core/observer/dep.jsexport 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 是給依賴變化時使用的,包含對 watch 的處理   subs[i].update()  } }}// 當首次計算 computed 屬性的值時,Dep 將會在計算期間對依賴進行收集Dep.target = nullconst targetStack = []export function pushTarget (_target: Watcher) { // 在一次依賴收集期間,如果有其他依賴收集任務開始(比如:當前 computed 計算屬性嵌套其他 computed 計算屬性), // 那么將會把當前 target 暫存到 targetStack,先進行其他 target 的依賴收集, if (Dep.target) targetStack.push(Dep.target) Dep.target = _target}export function popTarget () { // 當嵌套的依賴收集任務完成后,將 target 恢復為上一層的 Watcher,并繼續做依賴收集 Dep.target = targetStack.pop()}

Result

總結一下依賴收集、動態計算的流程:

1. data 屬性初始化 getter setter

2. computed 計算屬性初始化,提供的函數將用作屬性 vm.reversedMessage 的 getter

3. 當首次獲取 reversedMessage 計算屬性的值時,Dep 開始依賴收集

4. 在執行 message getter 方法時,如果 Dep 處于依賴收集狀態,則判定 message 為 reversedMessage 的依賴,并建立依賴關系

5. 當 message 發生變化時,根據依賴關系,觸發 reverseMessage 的重新計算
到此,整個 Computed 的工作流程就理清楚了。

Vue 是一個設計非常優美的框架,使用 Getter Setter 設計使依賴關系實現的非常順其自然,使用計算與渲染分離的設計(優先使用 MutationObserver,降級使用 setTimeout)也非常貼合瀏覽器計算引擎與排版引擎分離的的設計原理。

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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 浮山县| 梁山县| 雷波县| 旺苍县| 碌曲县| 荥阳市| 舒兰市| 兴宁市| 肃南| 成武县| 南木林县| 东乡族自治县| 绥江县| 凤台县| 中宁县| 柞水县| 肇庆市| 屏山县| 原阳县| 卓尼县| 全南县| 武山县| 和林格尔县| 灌阳县| 江川县| 普陀区| 平昌县| 张家界市| 科技| 尚志市| 株洲市| 峨眉山市| 航空| 陇川县| 城口县| 厦门市| 萝北县| 伊宁县| 金寨县| 宜春市| 同江市|