1. 程式人生 > >Vue.js 內部執行機制(六)---- 批量非同步更新策略及 nextTick 原理

Vue.js 內部執行機制(六)---- 批量非同步更新策略及 nextTick 原理

之前我們學到了 Vue 更新資料是如何更新檢視的。

簡單回顧

資料更新(setter)-> 通知依賴收集集合(Dep) -> 呼叫所有觀察者(Watcher) -> 比對節點樹(patch) -> 檢視

在更新檢視這一步,使用非同步更新策略

為什麼呢?引用小冊中的例子,下面有一個這樣的 Vue 元件

<template>
  <div>
    <div>{{number}}</div>
    <div @click="handleClick">click</div>
  </div>
</template>
<script>
export default {
    data () {
        return {
            number: 0
        };
    },
    methods: {
        handleClick () {
            for(let i = 0; i < 1000; i++) {
                this.number++;
            }
        }
    }
}
</script>

在for迴圈中,我們連續更改了1000次繫結資料 number ,如果使用同步更新,則需要1000次的 patch ,也就是1000次的 Diff ,1000次更新,這就很可怕了。

所以,在 Vue 裡使用非同步更新的方法,每次觸發某個資料的 setter 方法後,對應的 Watcher 物件其實會被 push 進一個佇列 queue 中,在下一個 tick(代表一次非同步) 的時候將這個佇列 queue 全部拿出來 run( Watcher 物件的一個方法,用來觸發 patch 操作) 一遍。

nextTick

在 Vue 裡,實現了一個 nextTick 函式,主要用來非同步操作,引數為一個 callback 函式,會被存放在 callback 佇列中,在下一個 tick 時觸發佇列中的所有 callback 事件。

在 Vue 原始碼中,使用 setImmediate、MessageChannel、setTimeout 來實現 macroTimerFunc(nextTick 中使用的非同步方法),使用 Promise 來實現 microTimerFunc ,感興趣可以看看 next-tick 。下面我們同樣用 setTimeout 來舉例。

/* cb 函式集合、 pending 是一個標記位,代表一個等待的狀態 */
let callbacks = [];
let pending = false;

/**
    * 非同步鉤子函式
    * 目的是在當前呼叫棧執行完畢以後(不一定立即)才會去執行這個事件
    */
function nextTick(cb) {
    callbacks.push(cb);
    
    /* 第一次執行時,設定非同步執行器 timeout 當前執行棧執行完,呼叫 flushCallbacks */
    if (!pending) {
        pending = true;
        setTimeout(flushCallbacks, 0);
    }
}

/* cb 集合執行函式 */
function flushCallbacks() {
    pending = false;
    /* 生成 cb 集合副本 copies */
    const copies = callbacks.slice(0);
    /* cb 集合長度賦值為0 */
    callbacks.length = 0;
    for (let i = 0; i < copies.length; i++) {
        copies[i]();
    }
}

注意:Vue文件中說明,在DOM更新之後立即執行callback函式,可以使用Vue.$nextTick(),我理解是應該是將這些callback函式,放在dom更新的函式後面

重寫Watcher

在前面說到,連續更新1000次 number ,不可能連續渲染檢視1000次,因此 有一個 queue 佇列來收集過濾 Watcher ,同一個 Watcher 在同一個 tick 的時候只執行一次。

/* 用來給 Watcher 作唯一標識 */
let uid = 0;

/* 觀察者 */
class Watcher {
    constructor() {
        /* 標識 */
        this.id = ++uid;
    }

    /* 呼叫 queueWatcher 將 Watcher 推入過濾佇列 */
    update() {
        console.log('watch' + this.id + ' update');
        queueWatcher(this);
    }

    /* 更新檢視函式 在函式裡觸發 patch  */
    run() {
        console.log('watch' + this.id + '檢視更新啦~');
    }
}

注意:  update 方法,在修改資料後由 Dep 物件來呼叫,而 run 函式才是真正觸發 patch 函式更新檢視的方法

queueWatcher

用來存放並過濾相同 Watcher 的函式,第一次呼叫時,會將呼叫執行 Watcher 佇列的函式推入 nextTick 函式,達到非同步更新的效果。

/* 用來區分當前 Watcher 是否已存放的 map  */
let has = {};
/* 存取 Watcher 的佇列 */
let queue = [];
/* 標識位,標記是否已經向 nextTick 傳遞了 flushSchedulerQueue 方法 */
let waiting = false;

function queueWatcher(watcher) {
    const id = watcher.id;
    /* 相同的 Watcher 不會被重複傳入 */
    if (has[id] == null) {
        has[id] = true;
        queue.push(watcher);

        /* 第一次被呼叫,將 flushSchedulerQueue 函式推入 nextTick */
        if (!waiting) {
            waiting = true;
            nextTick(flushSchedulerQueue);
        }
    }
}

flushSchedulerQueue

用來呼叫所有佇列中的 Watcher.run() 函式,觸發 patch 函式。

/* 用來呼叫所有佇列中的 Watcher.run() 函式,觸發 patch 函式 */
function flushSchedulerQueue() {
    let watcher, id;

    for (index = 0; index < queue.length; index++) {
        watcher = queue[index];
        id = watcher.id;
        /* 將區分 map 歸為原始值 */
        has[id] = null;
        watcher.run();
    }

    waiting = false;
}

參考文章

相關推薦

Vue.js 內部執行機制---- 批量非同步更新策略 nextTick 原理

之前我們學到了 Vue 更新資料是如何更新檢視的。 簡單回顧 資料更新(setter)-> 通知依賴收集集合(Dep) -> 呼叫所有觀察者(Watcher) -> 比對節點樹(patch) -> 檢視 在更新檢視這一步,使用非同步更新策略 為

Vue.js 內部執行機制 (二) ---- 響應式系統的依賴收集追蹤原理

 為什麼需要依賴收集?  1、在 Vue 中,我們可能更新了不用更新檢視的資料,如果沒有依賴收集,則也會呼叫更新檢視的 cb 函式,顯然這是不合理的 2、Vue 頁面中可能多處引用同一個 Vue 元件物件,更新響應式資料時,則應當更新多處檢視,這些都涉及依賴收集  首先

Windows執行使用事件機制解決執行緒同步問題

事件相關函式: 1.建立事件:CreateEvent HANDLE CreateEvent ( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState,

spring 內部工作機制

出了 流水線 tor 應用程序 java反射機制 post 技術 process 加載 本章節講Spring容器從加載配置文件到創建出一個完整Bean的作業流程及參與的角色。 Spring 啟動時讀取應用程序提供的Bean配置信息,並在Spring容器中生成一份相應

Web框架——XWAF的程式碼結構和執行機制4

    XWAF是一套基於Servlet和java反射技術的Web應用程式框架,它利用Servlet執行機制在伺服器上載入和執行,接管客戶端請求,依靠ServletRequest物件獲取使用者請求資訊,使用ServletResponse物件返回處理結果。   

vue.js匯入css庫elementUi的方法

1.安裝以下模組,讓webpack可以解析css檔案   npm install style-loader --save-dev npm install css-loader --save-dev npm install file-loader --s

深入理解java多執行

關於java多執行緒的概念以及基本用法:java多執行緒基礎 6,單例模式與多執行緒 如何使單例模式遇到多執行緒是安全的這是下面要討論的內容 6.1,立即載入 立即載入就是在使用類的時候已經將物件建立完畢,例如String s = new Stri

Vue.js學習使用心得

一、計算屬性 計算屬性關鍵詞: computed <body> <div id="app"> <p>原始字串: {{ message }}</p> <p>計算後反轉字串: {{ reversedMessage }}</p>

VsCode + node+ vue.js 開發環境搭建

1.安裝最新的 vscode ,這個很容易安裝。 2.要在機器上開發 vue.js 有兩種模式,一種直接用 .js ,這種方式不用 安裝.node  環境,另一種用 webpack, 3.在機器安裝 node node-v10.8.0-x64.msi,在安裝過程中可能會出

深入探究immutable.js的實現機制

Immutable.js 採用了持久化資料結構和結構共享,保證每一個物件都是不可變的,任何新增、修改、刪除等操作都會生成一個新的物件,且通過結構共享等方式大幅提高效能。網上已經有很多文章簡單介紹了 Immutable.js 的原理,但基本都是淺嘗輒止,我也是搜了很久

Java 多執行之Java記憶體模型

1. 併發程式設計的兩個問題 在併發程式設計中, 需要處理兩個關鍵問題: 執行緒之間如何通訊及執行緒之間如何同步 通訊指的是執行緒之間是以何種機制來交換資訊, 在指令式程式設計中, 執行緒之間的通訊機制有兩種:共享記憶體和訊息傳遞。在共享記憶體的模型中, 執行緒之間共享程式的公共狀態, 通過讀寫記憶體中的

vue.js實現初瞭解

1. vue 2.0是用Flow做靜態型別檢查, 3.0對TypeScript的支援更好了; 2. vue.js是基於Rollup(更輕量,適合js庫的構建)構建的,它的構建相關配置都在scripts目錄下; 3. Runtime Only 版本(template模板編譯為render函式) 和

Java多執行之Deque與LinkedBlockingDeque深入分析

1、LinkedBlockingDeque資料結構 雙向併發阻塞佇列。所謂雙向是指可以從佇列的頭和尾同時操作,併發只是執行緒安全的實現,阻塞允許在入隊出隊不滿足條件時掛起執行緒,這裡說的佇列是指支援FIFO/FILO實現的連結串列。 首先看下LinkedBlockingDeque的資料結構。通常情況

多程序與多執行--LinuxThreads(轉)

(注:這篇文章轉自網路,雖然Linux從核心2.6開始,多執行緒已使用NPTL技術,但是這篇文章對我們理解多執行緒技術還是挺有用的)Linux核心對多程序和多執行緒的支援方式:        執行緒機制支援併發程式設計技術,在多處理器上能真正保證並行處理。而在linux實現執行緒很特別,linux把所有的執行

InnoDB日誌管理機制 – 運維派

本文接上文,開始接受MySQL的日誌刷盤和掃描問題,在本文中涉及到很多程式碼片段解析,程式碼註釋是關鍵,建議收藏本文並在電腦上閱讀。 日誌刷盤時機 前面已經介紹了大部分關於REDO日誌的內容了,但還有一個問題沒有講,就是日誌刷盤的時機,也就是什麼時候才會將日誌刷入磁碟。 現在已經知道,當MTR提交時

js執行worker

    瀏覽器端js是單執行緒執行,所以當js執行高負載運算時,UI渲染就會阻塞,頁面就會出現卡頓,使用者體驗就不是很好     js為此也提供了非同步操作,例如: 定時器(setTimeout 和 setInterval),Ajax請求等,但非同步終究還是單執行緒,

十二vue.js元件——元件基礎1

1)學習元件的原因 概述 元件是vue.js最核心的功能,也是整個框架設計最精彩的地方,當然也是最難的地方。 原因 Vue.js的元件就是為了提高程式碼重用性和高擴充套件性的,特別是將ui與js作為一個整體進行復用與擴充套件。 一些具體的場景 1.UI構建---

Docker系列~搭建Tomcat和JDK執行環境

1 Docker與虛擬機器 2 搭建過程 2.1 準備宿主系統 準備一個 CentOS 7作業系統,具體要求如下: 必須是 64 位作業系統 建議核心在 3.8 以上 通過以下命令檢視您的 CentOS 核心:

執行:ThreadLocal 關鍵字

包括: 一. 什麼是 ThreadLocal 二. ThreadLocal 類中的方法簡介 三. 如何使用ThreadLocal      3.1 3個執行緒共享一個物件,各自產生序列號     

ASP.NET之旅--淺談Asp.net的執行機制

      上節中我們從Http請求在Asp.net中的執行過程進行了分析,但是對於真正核心的東西我們並沒有說明,那接下來我們將問題上拋,從底層類和物件的建立層面上來看Asp.net的執行機制。 三、Asp.net底層執行機制    1、理解HTTP.SYS