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

Vue之關(guān)于異步更新細(xì)節(jié)

 更新時(shí)間:2024年06月07日 15:04:13   作者:玉案軒窗  
這篇文章主要介紹了Vue之關(guān)于異步更新細(xì)節(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

前言

Vue官網(wǎng)對(duì)于異步更新的介紹如下:

  • Vue 在更新 DOM 時(shí)是異步執(zhí)行的。
  • 只要偵聽(tīng)到數(shù)據(jù)變化,Vue 將開(kāi)啟一個(gè)隊(duì)列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)變更。
  • 如果同一個(gè) watcher 被多次觸發(fā),只會(huì)被推入到隊(duì)列中一次。
  • 這種在緩沖時(shí)去除重復(fù)數(shù)據(jù)對(duì)于避免不必要的計(jì)算和 DOM 操作是非常重要的

Vue使用Object.defineProperty對(duì)數(shù)據(jù)劫持后,當(dāng)對(duì)對(duì)象進(jìn)行set操作,就會(huì)觸發(fā)視圖更新。

更新邏輯

以下面實(shí)例來(lái)分析視圖更新處理邏輯:

<div>{{ message }}</div>
<button @click="handleClick">更新</button>

new Vue({
	data: {
		message: ''
	},
	methods: {
		handleClick() {
			this.message = Date.now();
		}
	}
})

當(dāng)點(diǎn)擊更新按鈕后會(huì)對(duì)已劫持的屬性message做賦值操作,此時(shí)會(huì)觸發(fā)Object.defineProperty的set操作。

Object.defineProperty set操作

Object.defineProperty的set函數(shù)的設(shè)置,實(shí)際上最核心的邏輯就是觸發(fā)視圖更新,具體代碼邏輯如下:

set: function reactiveSetter (newVal) {
	// 其他邏輯
	
    // 觸發(fā)視圖更新
    dep.notify();
}

每個(gè)屬性都會(huì)對(duì)應(yīng)一個(gè)Dep對(duì)象,當(dāng)對(duì)屬性進(jìn)行賦值時(shí)就會(huì)調(diào)用Dep的notify實(shí)例方法,該實(shí)例方法的功能就是是通知視圖需要更新。

Dep notify實(shí)例方法

notify實(shí)例方法的代碼邏輯如下:

Dep.prototype.notify = function notify () {
  // stabilize the subscriber list first
  var subs = this.subs.slice();
  for (var i = 0, l = subs.length; i < l; i++) {
    subs[i].update();
  }
};

subs中存儲(chǔ)是watcher對(duì)象,每個(gè)Vue實(shí)例都存在一個(gè)與視圖更新關(guān)聯(lián)的watcher對(duì)象,該對(duì)象的創(chuàng)建是在$mount階段,具體看查看之前的文章Vue實(shí)例創(chuàng)建整體流程

代表屬性的Dep對(duì)象與watcher對(duì)象的關(guān)聯(lián)是在render函數(shù)調(diào)用階段具體屬性獲取時(shí)建立的即依賴(lài)收集

notify方法會(huì)執(zhí)行與當(dāng)前屬性關(guān)聯(lián)的所有watcher對(duì)象的update方法,必然會(huì)存在一個(gè)視圖更新相關(guān)的watcher。

watcher對(duì)象的按照分類(lèi)實(shí)際上分為兩類(lèi):

  • 視圖更新相關(guān)的,每一個(gè)Vue實(shí)例都存在一個(gè)此類(lèi)的watcher對(duì)象
  • 邏輯計(jì)算相關(guān)的,計(jì)算屬性和watch監(jiān)聽(tīng)所創(chuàng)建的watcher對(duì)象

Watcher update實(shí)例方法

update實(shí)例方法的代碼邏輯具體如下:

Watcher.prototype.update = function update () {
  /* istanbul ignore else */
  if (this.lazy) {
    this.dirty = true;
  } else if (this.sync) {
    this.run();
  } else {
    queueWatcher(this);
  }
};

lazy、sync都是Watcher的屬性,分別表示:

  • lazy:表示懶處理,即延遲相關(guān)處理,用于處理計(jì)算屬性
  • computedsync:表示同步執(zhí)行,即觸發(fā)屬性更新就立即更新視圖

從上面邏輯中可知,默認(rèn)是queueWatcher處理即開(kāi)啟一個(gè)隊(duì)列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)變更,即視圖是異步更新的。

這里需要注意的一點(diǎn)是:

queueWatcher中必然存在視圖更新的watcher對(duì)象,不會(huì)存在計(jì)算屬性computed對(duì)應(yīng)的watcher(computed對(duì)應(yīng)的watcher對(duì)象lazy屬性默認(rèn)為true),可能存在watch API對(duì)應(yīng)的用戶(hù)性質(zhì)的watcher對(duì)象

queueWatcher執(zhí)行邏輯

function queueWatcher (watcher) {
  var id = watcher.id;
  if (has[id] == null) {
    has[id] = true;
    if (!flushing) {
      queue.push(watcher);
    } else {
      // if already flushing, splice the watcher based on its id
      // if already past its id, it will be run next immediately.
      var i = queue.length - 1;
      while (i > index && queue[i].id > watcher.id) {
        i--;
      }
      queue.splice(i + 1, 0, watcher);
    }
    // queue the flush
    if (!waiting) {
      waiting = true;
      nextTick(flushSchedulerQueue);
    }
  }
}

實(shí)際上面邏輯主要分成3點(diǎn):

  • 對(duì)于同一個(gè)watcher對(duì)象,使用has對(duì)象結(jié)構(gòu)+id為key來(lái)判斷隊(duì)列中是否已存在對(duì)應(yīng)watcher對(duì)象,如果存在就不會(huì)將其添加到queue中
  • 通過(guò)flushing標(biāo)識(shí)區(qū)分當(dāng)在清空隊(duì)列過(guò)程中和正常情況下,如何向queue中添加watcher
  • 通過(guò)waiting標(biāo)識(shí)區(qū)分是否要執(zhí)行nextTick即清空queue的動(dòng)作

因?yàn)閝ueue是全局變量,在此步驟之前就將watcher對(duì)象添加到queue,如果waiting為true就標(biāo)識(shí)已經(jīng)調(diào)用nextTick實(shí)現(xiàn)異步處理queue了,就不要再次調(diào)用nextTick

從上面整體邏輯可知,queueWacther的邏輯主要就兩點(diǎn):

  • 判斷是否重復(fù)watcher,對(duì)于不重復(fù)的watcher將其添加到queue中
  • 調(diào)用nextTick開(kāi)啟異步處理queue操作即flushSchedulerQueue函數(shù)執(zhí)行

nextTick + flushSchedulerQueue

nextTick函數(shù)實(shí)際上跟$nextTick是相同的邏輯,主要的區(qū)別就是上下文的不同,即函數(shù)的this綁定值的不同。

使用macroTask API還是microTask API來(lái)執(zhí)行flushSchedulerQueue

而flushSchedulerQueue函數(shù)就是queue的具體處理邏輯,主要邏輯如下:

function flushSchedulerQueue () {
  flushing = true;
  var watcher, id;

  // Sort queue before flush.
  // This ensures that:
  // 1. Components are updated from parent to child. (because parent is always
  //    created before the child)
  // 2. A component's user watchers are run before its render watcher (because
  //    user watchers are created before the render watcher)
  // 3. If a component is destroyed during a parent component's watcher run,
  //    its watchers can be skipped.
  queue.sort(function (a, b) { return a.id - b.id; });

  for (index = 0; index < queue.length; index++) {
    watcher = queue[index];
    id = watcher.id;
    has[id] = null;
    watcher.run();
  }

  var activatedQueue = activatedChildren.slice();
  var updatedQueue = queue.slice();

  resetSchedulerState();

  // call component updated and activated hooks
  callActivatedHooks(activatedQueue);
  callUpdatedHooks(updatedQueue);
}

flushSchedulerQueue函數(shù)的主要邏輯可以總結(jié)成如下幾點(diǎn):

  • 對(duì)隊(duì)列queue中watcher對(duì)象進(jìn)行排序
  • 遍歷queue執(zhí)行每個(gè)watcher對(duì)象的run方法
  • 重置控制queue的相關(guān)狀態(tài),用于下一輪更新
  • 執(zhí)行組件的updated和activated生命周期

這里就不展開(kāi)了,需要注意的是activated是針對(duì)于keep-alive下組件的特殊處理,updated生命周期是先子組件再父組件的,隊(duì)列queue的watcher對(duì)象是按照父組件子組件順序排列的,所以在源碼中updated生命周期的觸發(fā)是倒序遍歷queue觸發(fā)的。

首先說(shuō)說(shuō)watcher對(duì)象的run實(shí)例方法,該方法的主要邏輯就是執(zhí)行watcher對(duì)象的getter屬性和cb屬性對(duì)應(yīng)的函數(shù)。

上面說(shuō)過(guò)watcher對(duì)象的按照分類(lèi)實(shí)際上分為兩類(lèi):

  • 視圖更新相關(guān)的,每一個(gè)Vue實(shí)例都存在一個(gè)此類(lèi)的watcher對(duì)象
  • 邏輯計(jì)算相關(guān)的,計(jì)算屬性和watch監(jiān)聽(tīng)所創(chuàng)建的watcher對(duì)象

watcher對(duì)象的getter屬性和cb屬性就是對(duì)應(yīng)著上面各類(lèi)watcher的實(shí)際處理邏輯,例如watch API對(duì)應(yīng)的getter屬性就是監(jiān)聽(tīng)項(xiàng),cb屬性才是具體的處理邏輯。

為什么需要對(duì)queue中watcher對(duì)象進(jìn)行排序?

實(shí)際上Vue源碼中有相關(guān)說(shuō)明,這主要涉及到嵌套組件Vue實(shí)例創(chuàng)建、render watch和用戶(hù)watch創(chuàng)建的時(shí)機(jī)。

每個(gè)組件都是一個(gè)Vue實(shí)例,嵌套組件創(chuàng)建總是從父組件Vue實(shí)例開(kāi)始創(chuàng)建的,在父組件patch階段才創(chuàng)建子組件的Vue實(shí)例。

而這個(gè)順序決定了watcher對(duì)象的id值大小問(wèn)題:

父組件的所有watcher對(duì)象id < 子組件的所有watcher對(duì)象id

render watch實(shí)際上就是與視圖更新相關(guān)的watcher對(duì)象,該對(duì)象是其對(duì)應(yīng)的Vue實(shí)例創(chuàng)建的末期即掛載階段才創(chuàng)建的,是晚于用戶(hù)watch即計(jì)算屬性computed和watch API創(chuàng)建的watcher對(duì)象,所以:

render watch的id < 所有用戶(hù)watch的id的

子組件可能是更新觸發(fā)源,如果父組件也需要更新視圖,這樣queue隊(duì)列中子組件的watcher對(duì)象位置會(huì)在父組件的watcher對(duì)象之前,對(duì)queue中watcher對(duì)象進(jìn)行排序就保證了:

視圖更新時(shí) 父組件 總是先于 子組件開(kāi)始更新操作,而每個(gè)組件對(duì)應(yīng)的視圖渲染的watcher最后再執(zhí)行(即用戶(hù)watcher對(duì)象對(duì)應(yīng)的邏輯先執(zhí)行)

總結(jié)

Vue異步更新的過(guò)程還是非常清晰的:

  • 對(duì)屬性賦值觸發(fā)Dep對(duì)象notify方法執(zhí)行
  • 繼而執(zhí)行Watcher對(duì)象的update方法將對(duì)象保存到隊(duì)列queue中
  • 繼而調(diào)用mircoTask API或macroTask API執(zhí)行queue中任務(wù)
  • 對(duì)隊(duì)列中watcher進(jìn)行排序,保證順序執(zhí)行的正確性,調(diào)用其對(duì)應(yīng)run方法來(lái)實(shí)現(xiàn)視圖更新和相關(guān)邏輯更新操作

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • VueJs使用Amaze ui調(diào)整列表和內(nèi)容頁(yè)面

    VueJs使用Amaze ui調(diào)整列表和內(nèi)容頁(yè)面

    這篇文章主要介紹了VueJs 填坑日記之使用Amaze ui調(diào)整列表和內(nèi)容頁(yè)面,需要的朋友可以參考下
    2017-11-11
  • Vue?仿QQ左滑刪除組件功能

    Vue?仿QQ左滑刪除組件功能

    前幾天朋友在做vue項(xiàng)目開(kāi)發(fā)時(shí),有人反映?IOS?上面的滑動(dòng)點(diǎn)擊有點(diǎn)問(wèn)題,讓我們來(lái)幫忙解決,于是我就重寫(xiě)了代碼,下面把vue仿qq左滑刪除組件功能分享到腳本之家平臺(tái),需要的朋友參考下吧
    2018-03-03
  • vue組件實(shí)例解析

    vue組件實(shí)例解析

    Tag組件其實(shí)是一個(gè)很小的組件,業(yè)務(wù)價(jià)值很低,主要用于Vue新手入門(mén)。主要實(shí)現(xiàn)Vue常用的父組件改變子組件的值,view改變model,model的變化反應(yīng)到view上,事件的綁定等功能。下面跟著小編一起來(lái)看下吧
    2017-01-01
  • vue?this.$toast?失效問(wèn)題解決方案

    vue?this.$toast?失效問(wèn)題解決方案

    這篇文章主要介紹了vue?this.$toast?失效問(wèn)題匯總,本文給大家分享完美解決方案,感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • vue如何自定義地址設(shè)置@

    vue如何自定義地址設(shè)置@

    這篇文章主要介紹了vue如何自定義地址設(shè)置@,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Vue3路由配置createRouter、createWebHistory、useRouter和useRoute詳解

    Vue3路由配置createRouter、createWebHistory、useRouter和useRoute詳解

    Vue3和Vue2基本差不多,只不過(guò)需要將createRouter、createWebHistory從vue-router中引入,再進(jìn)行使用,下面這篇文章主要給大家介紹了關(guān)于Vue3路由配置createRouter、createWebHistory、useRouter和useRoute的相關(guān)資料,需要的朋友可以參考下
    2023-02-02
  • Vue.set 全局操作簡(jiǎn)單示例

    Vue.set 全局操作簡(jiǎn)單示例

    這篇文章主要介紹了Vue.set 全局操作,結(jié)合簡(jiǎn)單實(shí)例形式分析了Vue.set 全局操作相關(guān)使用技巧與注意事項(xiàng),需要的朋友可以參考下
    2019-09-09
  • vue cli3 實(shí)現(xiàn)分環(huán)境打包的步驟

    vue cli3 實(shí)現(xiàn)分環(huán)境打包的步驟

    這篇文章主要介紹了vue cli3 實(shí)現(xiàn)分環(huán)境打包的步驟,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • vue開(kāi)發(fā)中數(shù)據(jù)更新但視圖不刷新的解決方法

    vue開(kāi)發(fā)中數(shù)據(jù)更新但視圖不刷新的解決方法

    在開(kāi)發(fā)中我們處理數(shù)據(jù)時(shí)會(huì)遇到數(shù)據(jù)更新了,但視圖并沒(méi)有更新,這種情況往往是數(shù)據(jù)嵌套層數(shù)過(guò)多導(dǎo)致的問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于vue開(kāi)發(fā)中數(shù)據(jù)更新但視圖不刷新的解決方法,需要的朋友可以參考下
    2022-11-11
  • 淺談使用mpvue開(kāi)發(fā)小程序需要注意和了解的知識(shí)點(diǎn)

    淺談使用mpvue開(kāi)發(fā)小程序需要注意和了解的知識(shí)點(diǎn)

    這篇文章主要介紹了淺談使用mpvue開(kāi)發(fā)小程序需要注意和了解的知識(shí)點(diǎn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05

最新評(píng)論