最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue Computed底層原理深入探究

 更新時間:2022年08月22日 14:36:33   作者:蘭亭古墨  
computed是vue的配置選項,它的值是一個對象,其中可定義多個計算屬性,每個計算屬性就是一個函數(shù),下面這篇文章主要給大家介紹了關于vue中計算屬性computed的詳細講解,需要的朋友可以參考下

今天面了家小公司,上來直接問 computed 底層原理,面試官是這樣問的,data 中定義了 a 和 b 變量。computed 里面定義了 c 屬性,c 的結果依賴與 a 和 b,模板中使用了變量 c。假設改變了 a,請問底層是如何收集依賴,如何觸發(fā)更新的?

<div>{<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->{ c }}</div>
data(){
	return {
		a: 'foo',
		b: 'bar'
	}
},
computed: {
	c() { 
		return this.a + ' - ' + this.b;
	 }
},
mounted(){
	setTimeout(() => { this.a = 'FOO' }, 1000)
}

頁面效果:先顯示了 foo - bar,一秒之后顯示 FOO - bar

這里查源碼后,對 computed 原理簡述如下:

initState 函數(shù)中初始化了 initComputed

export function initState (vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  if (opts.props) initProps(vm, opts.props)
  if (opts.methods) initMethods(vm, opts.methods)
  if (opts.data) {
    initData(vm)
  } else {
    observe(vm._data = {}, true /* asRootData */)
  }
  if (opts.computed) initComputed(vm, opts.computed)
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
  }
}

initComputed 函數(shù)中遍歷 computed 中每一個屬性,并且 new Watcher(computed watcher),可以看到傳入 Watcher 構造函數(shù)的 cb 是 noop,它是一個空函數(shù)。并且 defineComputed

function initComputed (vm: Component, computed: Object) {
  // $flow-disable-line
  const watchers = vm._computedWatchers = Object.create(null)
  // computed properties are just getters during SSR
  const isSSR = isServerRendering()
  for (const key in computed) {
    const userDef = computed[key]
    const getter = typeof userDef === 'function' ? userDef : userDef.get
    if (process.env.NODE_ENV !== 'production' && getter == null) {
      warn(
        `Getter is missing for computed property "${key}".`,
        vm
      )
    }
    if (!isSSR) {
      // create internal watcher for the computed property.
      watchers[key] = new Watcher(
        vm,
        getter || noop,
        noop,
        computedWatcherOptions
      )
    }
    // component-defined computed properties are already defined on the
    // component prototype. We only need to define computed properties defined
    // at instantiation here.
    if (!(key in vm)) {
      defineComputed(vm, key, userDef)
    } else if (process.env.NODE_ENV !== 'production') {
      if (key in vm.$data) {
        warn(`The computed property "${key}" is already defined in data.`, vm)
      } else if (vm.$options.props && key in vm.$options.props) {
        warn(`The computed property "${key}" is already defined as a prop.`, vm)
      } else if (vm.$options.methods && key in vm.$options.methods) {
        warn(`The computed property "${key}" is already defined as a method.`, vm)
      }
    }
  }
}

defineComputed 中使用了 Object.defineProperty 對屬性進行劫持,獲取是返回對應的 getter 即 createComputedGetter

export function defineComputed (
  target: any,
  key: string,
  userDef: Object | Function
) {
  const shouldCache = !isServerRendering()
  if (typeof userDef === 'function') {
    sharedPropertyDefinition.get = shouldCache
      ? createComputedGetter(key)
      : createGetterInvoker(userDef)
    sharedPropertyDefinition.set = noop
  } else {
    sharedPropertyDefinition.get = userDef.get
      ? shouldCache && userDef.cache !== false
        ? createComputedGetter(key)
        : createGetterInvoker(userDef.get)
      : noop
    sharedPropertyDefinition.set = userDef.set || noop
  }
  if (process.env.NODE_ENV !== 'production' &&
      sharedPropertyDefinition.set === noop) {
    sharedPropertyDefinition.set = function () {
      warn(
        `Computed property "${key}" was assigned to but it has no setter.`,
        this
      )
    }
  }
  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()
      }
      if (Dep.target) {
        watcher.depend()
      }
      return watcher.value
    }
  }
}

看到這里,我們大致可以理解,Vue 組件在初始化時,initState -> initComputed -> new Watcher() 計算watcher,傳入的 callback 是 computer 屬性的 getter,由于 computed 是 lazy: true watcher,所以 new Watcher 時并不會立即執(zhí)行 watcher 實例上的 get(), 而是在 defineComputed 函數(shù)里面調(diào)用 createComputedGetter 函數(shù),在 createComputedGetter 函數(shù)執(zhí)行了 watcher.evaluate() 進而執(zhí)行了 watcher.get().

執(zhí)行 watcher.get() 首先 pushTarget(this),將 Dep.target 賦值為當前的 computed watcher,然后執(zhí)行 this.getter

也就是執(zhí)行 computer 屬性配置的 getter,執(zhí)行getter 就會訪問所依賴的每一個值,就會被 Object.defineProperty 劫持到進入 get ,執(zhí)行 dep.depend() , 會為每一個屬性對應的 dep 實例添加一個 computed watcher,同時這個 computed watcher 也會保存對應的 dep。

說了這么多都在講 computed watcher,那修改 this.a 頁面為什么會發(fā)生更新呢?

答案:因為 this.a 的依賴中不僅有 computed watcher 還有一個 render watcher

原因:

$mount 是會執(zhí)行 mountComponent,會創(chuàng)建一個 render watcher,它會立即執(zhí)行 cb(目前 Dep.target 是 render watcher),調(diào)用 render 函數(shù)在底層編譯模板,最后訪問屬性計算屬性 c,訪問計算屬性 c 就必定會訪問 a,當訪問 a 時會觸發(fā) defineComputed 中的 Object.defineProperty 進而劫持調(diào)用 createComputedGetter,進而調(diào)用 watcher.depend(),這個 watcher 是 computed watcher

function createComputedGetter (key) {
  return function computedGetter () {
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
      if (watcher.dirty) {
        watcher.evaluate()
      }
      if (Dep.target) {
        watcher.depend()
      }
      return watcher.value
    }
  }
}
// Watcher.js
depend () {
  let i = this.deps.length
  while (i--) {
    this.deps[i].depend()
  }
}

調(diào)用 watcher.depend() , this 指的是 computed watcher,會將 computed watcher 里面的 deps 保存在所有依賴調(diào)用 deps[i].depend(),進而調(diào)用 Dep 類中的 Dep.target.addDep(this),使得 render watcher 中保存了當前的 dep,dep 中同時保存了 render watcher。

dep 中同時保存了 render watcher。就可以看出,示例中的屬性 a 的 dep 中也會保存 render watcher,所以 a 屬性的 dep 中有兩個 watcher: [computedWatcher, renderWatcher]

所以,修改 a 屬性的值,最后 notify 會清空這個 保存 watcher 的隊列,進行頁面更新!Okay!

到此這篇關于Vue Computed底層原理深入探究的文章就介紹到這了,更多相關Vue Computed內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 詳解vue或uni-app的跨域問題解決方案

    詳解vue或uni-app的跨域問題解決方案

    這篇文章主要介紹了詳解vue或uni-app的跨域問題解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-02-02
  • vue3中ace-editor的簡單使用方法實例

    vue3中ace-editor的簡單使用方法實例

    這篇文章主要給大家介紹了關于vue3中ace-editor簡單使用的相關資料,ace-editor是一種代碼編輯器,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2023-10-10
  • Vue實現(xiàn)圖片輪播組件思路及實例解析

    Vue實現(xiàn)圖片輪播組件思路及實例解析

    這篇文章主要介紹了Vue實現(xiàn)圖片輪播組件思路及實例解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-05-05
  • 如何處理vue router 路由傳參刷新頁面參數(shù)丟失

    如何處理vue router 路由傳參刷新頁面參數(shù)丟失

    這篇文章主要介紹了如何處理vue router 路由傳參刷新頁面參數(shù)丟失,對vue感興趣的同學,可以參考下
    2021-05-05
  • vuex中store存儲store.commit和store.dispatch的用法

    vuex中store存儲store.commit和store.dispatch的用法

    這篇文章主要介紹了vuex中store存儲store.commit和store.dispatch的用法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-07-07
  • 用了這么久的Vue3你真的了解Proxy了嗎

    用了這么久的Vue3你真的了解Proxy了嗎

    Proxy是ES6引入的一個新特性,它允許你創(chuàng)建一個代理對象,用于攔截對目標對象的訪問,但用了這么久的vue3,你真的懂Proxy嗎,本文就來和大家深入聊聊Proxy吧
    2023-06-06
  • vantUI 獲得piker選中值的自定義ID操作

    vantUI 獲得piker選中值的自定義ID操作

    這篇文章主要介紹了vantUI 獲得piker選中值的自定義ID操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • vue實現(xiàn)記事本小功能

    vue實現(xiàn)記事本小功能

    這篇文章主要為大家詳細介紹了vue實現(xiàn)記事本小功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Vue下拉框加分頁搜索功能的實現(xiàn)方法

    Vue下拉框加分頁搜索功能的實現(xiàn)方法

    開發(fā)任務中有這樣一個需求,下拉框中需要展示超過5000條數(shù)據(jù),甚至更多,這數(shù)據(jù)量直接整個頁面卡死了,就想到在下拉框中加分頁,下面小編通過實例代碼介紹下Vue下拉框加分頁搜索功能的實現(xiàn),感興趣的朋友一起看看吧
    2022-11-11
  • vue-echarts如何實現(xiàn)圖表組件封裝詳解

    vue-echarts如何實現(xiàn)圖表組件封裝詳解

    由于在項目中需要對數(shù)據(jù)進行可視化處理,也就是用圖表展示,所以下面這篇文章主要給大家介紹了關于vue-echarts如何實現(xiàn)圖表組件封裝的相關資料,需要的朋友可以參考下
    2022-05-05

最新評論

衡阳市| 英德市| 金寨县| 泰和县| 宣汉县| 衡阳县| 会宁县| 泌阳县| 铁力市| 全南县| 涿鹿县| 登封市| 黄浦区| 长兴县| 新丰县| 蒙山县| 尉犁县| 吴旗县| 武鸣县| 汶上县| 元江| 泗洪县| 舟曲县| 金坛市| 桐柏县| 盐池县| 临夏县| 扎鲁特旗| 岐山县| 海淀区| 盐源县| 溧阳市| 库尔勒市| 黄陵县| 什邡市| 沛县| 佛坪县| 六安市| 绵阳市| 通渭县| 元氏县|