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

vue中created、watch和computed的執(zhí)行順序詳解

 更新時(shí)間:2022年11月26日 09:38:21   作者:qb  
由于vue的雙向數(shù)據(jù)綁定,自動(dòng)更新數(shù)據(jù)的機(jī)制,在數(shù)據(jù)變化后,對(duì)此數(shù)據(jù)依賴?的所有數(shù)據(jù),watch事件都會(huì)被更新、觸發(fā),下面這篇文章主要給大家介紹了關(guān)于vue中created、watch和computed的執(zhí)行順序,需要的朋友可以參考下

 前言

面試題:vuecreated、watch(immediate: true)computed的執(zhí)行順序是啥?

先看個(gè)簡(jiǎn)單的例子:

// main.js
import Vue from "vue";

new Vue({
  el: "#app",
  template: `<div>
    <div>{{computedCount}}</div>
  </div>`,
  data() {
    return {
      count: 1,
    }
  },
  watch: {
    count: {
      handler() {
        console.log('watch');
      },
      immediate: true,
    }
  },
  computed: {
    computedCount() {
      console.log('computed');
      return this.count + 1;
    }
  },
  created() {
    console.log('created');
  },
});

當(dāng)前例子的執(zhí)行順序?yàn)椋?code>watch --> created --> computed。

為什么?

new Vue的實(shí)例化過(guò)程中,會(huì)執(zhí)行初始化方法this._init,其中有代碼:

Vue.prototype._init = function (options) {
    // ...
    initState(vm);
    // ...
    callHook(vm, 'created');
    // ...
    if (vm.$options.el) {
      vm.$mount(vm.$options.el);
    }
}

function initState (vm) {
  vm._watchers = [];
  var 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);
  }
}

猛一看代碼,是不是發(fā)現(xiàn)先執(zhí)行的initComputed(vm, opts.computed),然后執(zhí)行initWatch(vm, opts.watch),再執(zhí)行callHook(vm, 'created'),那為啥不是computed --> watch --> created呢?

不要著急,聽(tīng)我娓娓道來(lái)。

1、關(guān)于initComputed

const computedWatcherOptions = { lazy: true }
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 (!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') {
        // ...
    }
  }
}

在通過(guò)initComputed初始化計(jì)算屬性的時(shí)候,通過(guò)遍歷的方式去處理當(dāng)前組件中的computed。首先,在進(jìn)行計(jì)算屬性實(shí)例化的時(shí)候,將{ lazy: true }作為參數(shù)傳入,并且實(shí)例化的Watcher中的getter就是當(dāng)前例子中的computedCount函數(shù);其次,通過(guò)defineComputed(vm, key, userDef)的方式在當(dāng)前組件實(shí)例vm上為key進(jìn)行userDef的處理。具體為:

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
  }
  // ...
  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
    }
  }
}

從以上可以看出,這里通過(guò)Object.defineProperty(target, key, sharedPropertyDefinition)的方式,將函數(shù)computedGetter作為get函數(shù),只有當(dāng)對(duì)key進(jìn)行訪問(wèn)的時(shí)候,才會(huì)觸發(fā)其內(nèi)部的邏輯。內(nèi)部邏輯watcher.evaluate()為:

evaluate () {
    this.value = this.get()
    this.dirty = false
}

get中有主要邏輯:

value = this.getter.call(vm, vm)

這里的this.getter就是當(dāng)前例子中的:

computedCount() {
  console.log('computed');
  return this.count + 1;
}

也就是說(shuō),只有當(dāng)獲取computedCount的時(shí)候才會(huì)觸發(fā)computed的計(jì)算,也就是在進(jìn)行vm.$mount(vm.$options.el)階段才會(huì)執(zhí)行到console.log('computed')。

2、關(guān)于initWatch

function initWatch (vm: Component, watch: Object) {
  for (const key in watch) {
    const handler = watch[key]
    if (Array.isArray(handler)) {
      for (let i = 0; i < handler.length; i++) {
        createWatcher(vm, key, handler[i])
      }
    } else {
      createWatcher(vm, key, handler)
    }
  }
}
function createWatcher (
  vm: Component,
  expOrFn: string | Function,
  handler: any,
  options?: Object
) {
  if (isPlainObject(handler)) {
    options = handler
    handler = handler.handler
  }
  if (typeof handler === 'string') {
    handler = vm[handler]
  }
  return vm.$watch(expOrFn, handler, options)
}

在通過(guò)initWatch初始化偵聽(tīng)器的時(shí)候,如果watch為數(shù)組,則遍歷執(zhí)行createWatcher,否則直接執(zhí)行createWatcher。如果handler是對(duì)象或者字符串時(shí),將其進(jìn)行處理,最終作為參數(shù)傳入vm.$watch中去,具體為:

Vue.prototype.$watch = function (
    expOrFn: string | Function,
    cb: any,
    options?: Object
  ): Function {
    const vm: Component = this
    if (isPlainObject(cb)) {
      return createWatcher(vm, expOrFn, cb, options)
    }
    options = options || {}
    options.user = true
    const watcher = new Watcher(vm, expOrFn, cb, options)
    if (options.immediate) {
      try {
        cb.call(vm, watcher.value)
      } catch (error) {
        handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)
      }
    }
    return function unwatchFn () {
      watcher.teardown()
    }
  }

這里獲取到的options中會(huì)有immediate: true的鍵值,同時(shí)通過(guò)options.user = true設(shè)置usertrue,再將其作為參數(shù)傳入去進(jìn)行Watcher的實(shí)例化。

當(dāng)前例子中options.immediatetrue,所以會(huì)執(zhí)行cb.call(vm, watcher.value),也就是以vm為主體,立刻執(zhí)行cb。當(dāng)前例子中cb就是handler

handler() {
    console.log('watch');
},

這里就解釋了當(dāng)前例子中console.log('watch')是最先執(zhí)行的。

然后,執(zhí)行完initComputedinitWatch以后,就會(huì)通過(guò)callHook(vm, 'created')執(zhí)行到生命周期中的console.log('created')了。

最后通過(guò)vm.$mount(vm.$options.el)進(jìn)行頁(yè)面渲染的時(shí)候,會(huì)先去創(chuàng)建vNode,這時(shí)就需要獲取到computedCount的值,進(jìn)而觸發(fā)其get函數(shù)的后續(xù)邏輯,最終執(zhí)行到console.log('computed')。

附:為什么vue中的watch在mounted之后執(zhí)行

首先,在調(diào)用mounted的時(shí)候,會(huì)進(jìn)入到defineReactive函數(shù),然后調(diào)用函數(shù)里面的set方法,將mounted中賦的新的值給傳遞過(guò)去,并通過(guò)調(diào)用dep.notify( )把消息發(fā)送給訂閱者,繼而更新訂閱者的列表

后面才開(kāi)始渲染watch進(jìn)入Watcher的class

總結(jié)

關(guān)于vuecreatedwatch的執(zhí)行順序相對(duì)比較簡(jiǎn)單,而其中computed是通過(guò)Object.defineProperty為當(dāng)前vm進(jìn)行定義,再到后續(xù)創(chuàng)建vNode階段才去觸發(fā)執(zhí)行其get函數(shù),最終執(zhí)行到計(jì)算屬性computed對(duì)應(yīng)的邏輯。

到此這篇關(guān)于vue中created、watch和computed執(zhí)行順序詳解的文章就介紹到這了,更多相關(guān)vue created、watch和computed執(zhí)行順序內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue文件運(yùn)行的方法教學(xué)

    vue文件運(yùn)行的方法教學(xué)

    在本篇文章里小編給大家分享了關(guān)于vue文件運(yùn)行的方法教學(xué)內(nèi)容,有需要的朋友們跟著學(xué)習(xí)下。
    2019-02-02
  • 詳解關(guān)于Vue單元測(cè)試的幾個(gè)坑

    詳解關(guān)于Vue單元測(cè)試的幾個(gè)坑

    這篇文章主要介紹了關(guān)于Vue單元測(cè)試的幾個(gè)坑,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • Vue實(shí)現(xiàn)當(dāng)前頁(yè)面刷新的七種方法總結(jié)

    Vue實(shí)現(xiàn)當(dāng)前頁(yè)面刷新的七種方法總結(jié)

    大家在vue項(xiàng)目中當(dāng)刪除或者增加一條記錄的時(shí)候希望當(dāng)前頁(yè)面可以重新刷新,但是vue框架自帶的router是不支持刷新當(dāng)前頁(yè)面功能,所以本文就給大家分享了七種vue實(shí)現(xiàn)當(dāng)前頁(yè)面刷新的方法,需要的朋友可以參考下
    2023-07-07
  • vue中七牛插件使用的實(shí)例代碼

    vue中七牛插件使用的實(shí)例代碼

    本篇文章主要介紹了vue中七牛插件使用,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-07-07
  • vue實(shí)現(xiàn)三級(jí)聯(lián)動(dòng)動(dòng)態(tài)菜單

    vue實(shí)現(xiàn)三級(jí)聯(lián)動(dòng)動(dòng)態(tài)菜單

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)三級(jí)聯(lián)動(dòng)動(dòng)態(tài)菜單,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • vue3 自定義指令詳情

    vue3 自定義指令詳情

    這篇文章主要介紹了 vue3 自定義指令詳情,自定義指令分為全局和局部?jī)煞N類型,大家并不陌生。今天我們就講講在 vue3 中,自定義指令定義、使用以及鉤子函數(shù)都有哪些變化?,需要的朋友可以參考一下
    2021-11-11
  • nuxt踩坑之Vuex狀態(tài)樹(shù)的模塊方式使用詳解

    nuxt踩坑之Vuex狀態(tài)樹(shù)的模塊方式使用詳解

    這篇文章主要介紹了nuxt踩坑之Vuex狀態(tài)樹(shù)的模塊方式使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Vue實(shí)現(xiàn)雙向綁定的原理以及響應(yīng)式數(shù)據(jù)的方法

    Vue實(shí)現(xiàn)雙向綁定的原理以及響應(yīng)式數(shù)據(jù)的方法

    這篇文章主要介紹了Vue實(shí)現(xiàn)雙向綁定的原理以及響應(yīng)式數(shù)據(jù)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • uni-app中使用ECharts配置四種不同的圖表(示例詳解)

    uni-app中使用ECharts配置四種不同的圖表(示例詳解)

    在uni-app中集成ECharts可以為我們的應(yīng)用提供強(qiáng)大的圖表功能,我們?cè)敿?xì)說(shuō)一下如何在uni-app中使用ECharts,并配置四種不同的圖表,感興趣的朋友跟隨小編一起看看吧
    2024-01-01
  • Fragment 占位組件不生成標(biāo)簽與路由組件lazyLoad案例

    Fragment 占位組件不生成標(biāo)簽與路由組件lazyLoad案例

    這篇文章主要為大家介紹了Fragment 占位組件不生成標(biāo)簽與路由組件lazyLoad案例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10

最新評(píng)論

威海市| 荆门市| 龙胜| 略阳县| 迭部县| 景宁| 乐陵市| 南岸区| 个旧市| 普陀区| 台江县| 图木舒克市| 温宿县| 阜阳市| 溧水县| 兰考县| 屯门区| 霍州市| 沂水县| 牙克石市| 鱼台县| 华坪县| 林芝县| 天全县| 龙门县| 宁乡县| 册亨县| 舞钢市| 凉城县| 察雅县| 云阳县| 延吉市| 乌拉特中旗| 五原县| 康定县| 安福县| 祁连县| 合江县| 西盟| 象州县| 海城市|