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

Vue Computed底層原理深入探究

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

今天面了家小公司,上來(lái)直接問(wèn) computed 底層原理,面試官是這樣問(wèn)的,data 中定義了 a 和 b 變量。computed 里面定義了 c 屬性,c 的結(jié)果依賴與 a 和 b,模板中使用了變量 c。假設(shè)改變了 a,請(qǐng)問(wèn)底層是如何收集依賴,如何觸發(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)
}

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

這里查源碼后,對(duì) computed 原理簡(jiǎn)述如下:

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 中每一個(gè)屬性,并且 new Watcher(computed watcher),可以看到傳入 Watcher 構(gòu)造函數(shù)的 cb 是 noop,它是一個(gè)空函數(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 對(duì)屬性進(jìn)行劫持,獲取是返回對(duì)應(yīng)的 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 組件在初始化時(shí),initState -> initComputed -> new Watcher() 計(jì)算watcher,傳入的 callback 是 computer 屬性的 getter,由于 computed 是 lazy: true watcher,所以 new Watcher 時(shí)并不會(huì)立即執(zhí)行 watcher 實(shí)例上的 get(), 而是在 defineComputed 函數(shù)里面調(diào)用 createComputedGetter 函數(shù),在 createComputedGetter 函數(shù)執(zhí)行了 watcher.evaluate() 進(jìn)而執(zhí)行了 watcher.get().

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

也就是執(zhí)行 computer 屬性配置的 getter,執(zhí)行g(shù)etter 就會(huì)訪問(wèn)所依賴的每一個(gè)值,就會(huì)被 Object.defineProperty 劫持到進(jìn)入 get ,執(zhí)行 dep.depend() , 會(huì)為每一個(gè)屬性對(duì)應(yīng)的 dep 實(shí)例添加一個(gè) computed watcher,同時(shí)這個(gè) computed watcher 也會(huì)保存對(duì)應(yīng)的 dep。

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

答案:因?yàn)?this.a 的依賴中不僅有 computed watcher 還有一個(gè) render watcher

原因:

$mount 是會(huì)執(zhí)行 mountComponent,會(huì)創(chuàng)建一個(gè) render watcher,它會(huì)立即執(zhí)行 cb(目前 Dep.target 是 render watcher),調(diào)用 render 函數(shù)在底層編譯模板,最后訪問(wèn)屬性計(jì)算屬性 c,訪問(wèn)計(jì)算屬性 c 就必定會(huì)訪問(wèn) a,當(dāng)訪問(wèn) a 時(shí)會(huì)觸發(fā) defineComputed 中的 Object.defineProperty 進(jìn)而劫持調(diào)用 createComputedGetter,進(jìn)而調(diào)用 watcher.depend(),這個(gè) 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,會(huì)將 computed watcher 里面的 deps 保存在所有依賴調(diào)用 deps[i].depend(),進(jìn)而調(diào)用 Dep 類中的 Dep.target.addDep(this),使得 render watcher 中保存了當(dāng)前的 dep,dep 中同時(shí)保存了 render watcher。

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

所以,修改 a 屬性的值,最后 notify 會(huì)清空這個(gè) 保存 watcher 的隊(duì)列,進(jìn)行頁(yè)面更新!Okay!

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

相關(guān)文章

  • Vue3+axios+Mock.js實(shí)現(xiàn)登錄功能的示例代碼

    Vue3+axios+Mock.js實(shí)現(xiàn)登錄功能的示例代碼

    本文主要介紹了Vue3+axios+Mock.js實(shí)現(xiàn)登錄功能的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • vue實(shí)現(xiàn)h5掃碼的代碼示例

    vue實(shí)現(xiàn)h5掃碼的代碼示例

    html5-qrcode是一個(gè)基于JavaScript?輕量級(jí)和跨平臺(tái)的掃碼插件,允許用戶使用攝像頭掃描二維碼,并且解析為文本或者url,本文給大家介紹了vue實(shí)現(xiàn)h5掃碼,需要的朋友可以參考下
    2024-01-01
  • Vue3+Element-Plus實(shí)現(xiàn)左側(cè)菜單折疊與展開(kāi)功能示例

    Vue3+Element-Plus實(shí)現(xiàn)左側(cè)菜單折疊與展開(kāi)功能示例

    本文主要介紹了Vue3+Element-Plus實(shí)現(xiàn)左側(cè)菜單折疊與展開(kāi)功能示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • vue動(dòng)態(tài)路由刷新失效以及404頁(yè)面處理辦法

    vue動(dòng)態(tài)路由刷新失效以及404頁(yè)面處理辦法

    作為一個(gè)前端新手,項(xiàng)目中遇到權(quán)限處理時(shí),通常會(huì)采用動(dòng)態(tài)添加路由的方法來(lái)實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于vue動(dòng)態(tài)路由刷新失效以及404頁(yè)面處理辦法的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • vue中環(huán)境變量的使用與配置講解

    vue中環(huán)境變量的使用與配置講解

    這篇文章主要介紹了vue中環(huán)境變量的使用與配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • 記一次vue跨域的解決

    記一次vue跨域的解決

    這篇文章主要介紹了vue跨域的解決方法,幫助大家解決前端跨域問(wèn)題,感興趣的朋友可以參考下
    2020-10-10
  • vue實(shí)現(xiàn)過(guò)渡動(dòng)畫Message消息提示組件示例詳解

    vue實(shí)現(xiàn)過(guò)渡動(dòng)畫Message消息提示組件示例詳解

    這篇文章主要為大家介紹了vue實(shí)現(xiàn)過(guò)渡動(dòng)畫Message消息提示組件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • element table列表根據(jù)數(shù)據(jù)設(shè)置背景色

    element table列表根據(jù)數(shù)據(jù)設(shè)置背景色

    在使用elementui中的el-table時(shí),需要將表的背景色和字體顏色設(shè)置為新顏色,本文就來(lái)介紹一下element table列表根據(jù)數(shù)據(jù)設(shè)置背景色,感興趣的可以了解一下
    2023-08-08
  • vue使用file-saver本地文件導(dǎo)出功能

    vue使用file-saver本地文件導(dǎo)出功能

    這篇文章主要介紹了vue使用file-saver本地文件導(dǎo)出,大家需要安裝xlsx和file-saver,然后創(chuàng)建localExports.js文件,具體實(shí)現(xiàn)代碼跟隨小編一起看看吧
    2022-01-01
  • vue使用iview的modal彈窗嵌套modal出現(xiàn)格式錯(cuò)誤的解決

    vue使用iview的modal彈窗嵌套modal出現(xiàn)格式錯(cuò)誤的解決

    這篇文章主要介紹了vue使用iview的modal彈窗嵌套modal出現(xiàn)格式錯(cuò)誤的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09

最新評(píng)論