1. 程式人生 > >【Vue原理】Watch

【Vue原理】Watch

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧

【Vue原理】Watch - 原始碼版

今天繼續探索 Watch 原始碼,廢話不多說了

帶著我的幾個疑問開始

1、什麼時候初始化

2、怎麼確定監聽哪些值

3、深度監聽怎麼回事

4、怎麼觸發我的函式

這些問題的答案會摻雜在原始碼的解析中,我發現這幾篇的寫作套路都差不多.....

也可以看查一下我的白話版

【Vue原理】Watch - 白話版


什麼時候初始化

首先,從這個問題開始我們今天的探索之旅,請看原始碼

function Vue(){
    ... 其他處理
    initState(this)

    ...解析模板,生成DOM 插入頁面

}



function initState(vm) {


    ...處理 data,props,computed 等資料

    if (opts.watch) {
        initWatch(this, vm.$options.watch);
    }
}

沒錯,當你呼叫 Vue 建立例項過程中,會去處理各種選項,其中包括處理 watch


initWatch

處理 watch的方法是 initWatch,下面就呈上 原始碼

function initWatch(vm, watch) {    

    for (var key in watch) {    

        var watchOpt = watch[key];
        createWatcher(vm, key, handler);
    }
}

然後這段原始碼並沒有做什麼驚天地泣鬼神的事情,只是簡簡單單的一個遍歷,然後每個watch 都使用一個叫什麼 createWatcher 的東西去處理

這個函式到底是幹嘛的?暗藏著什麼祕密,歡迎來到我們今晚的 《走近科學》《原始碼解析》

createWatcher 來看看他的真身

function createWatcher(
    // expOrFn 是 key,handler 可能是物件
    vm, expOrFn, handler,opts

) {    

    // 監聽屬性的值是一個物件,包含handler,deep,immediate

    if (typeof handler ==="object") {
        opts= handler
        handler = handler.handler
    }    



    // 回撥函式是一個字串,從 vm 獲取

    if (typeof handler === 'string') {
        handler = vm[handler]
    }    



    // expOrFn 是 key,options 是watch 的全部選項

    vm.$watch(expOrFn, handler, opts)
}

大概就這樣吧

1、獲取到監聽回撥

2、呼叫 vm.$watch

1、獲取監聽回撥

首先,你傳入的 watch 配置可能是這三種(還有更多,差不多,不解釋,累死我)

公眾號

如果配置是個物件,就取handler 欄位

如果配置是函式,那麼直接就是 監聽回撥

如果配置是字串,從例項上獲取函式

2、呼叫 vm.$watch

看著這個方法,簡直 mmp,還有完沒完,我從一個函式進入一個函式,又從這個函式進入到另一個函式,迷宮啊這是.....

顯然你不用急,下面還有更多......

好吧,還是先看原始碼

Vue.prototype.$watch = function(
    // expOrFn 是 監聽的 key,cb 是監聽回撥,opts 是所有選項

    expOrFn, cb, opts

){    

    // expOrFn 是 監聽的 key,cb 是監聽的回撥,opts 是 監聽的所有選項

    var watcher = new Watcher(this, expOrFn, cb, opts);    



    // 設定了立即執行,所以馬上執行回撥

    if (opts.immediate) {
        cb.call(this, watcher.value);
    }
};

看完了吧?這麼短,你們肯定看得懂的啦,就兩件事

1、判斷是否立即執行監聽回撥

如果你設定了 immediate 的話,表示不用等我資料變化,初始化時馬上執行一遍,執行的程式碼就是直接呼叫 回撥,繫結上下文,傳入監聽值

2、每個 watch 配發 watcher

程式碼從這裡開始變得沉重,各位觀眾,喝口水,恰口飯,屏息觀看操作

看看 watcher 的原始碼

“watcher 的原始碼之前的文章也講過很多,但是對於每種選項的涉及的細節是不一樣的,所以每次都放上來,但是隻放跟本內容相關的部分程式碼,其他的省去以便我們快速理解”

var Watcher = function (vm, key, cb, opt) {  

    this.vm = vm;    

    this.deep = opt.deep;    

    this.cb = cb;  

    // 這裡省略處理 xx.xx.xx 這種較複雜的key
    this.getter = function(obj) {        

        return obj[key]

    };    

    // this.get 作用就是執行 this.getter函式

    this.value = this.get();
};

再看看,新建 watcher 的時候 ,傳入了什麼

1、監聽的 key

2、監聽回撥 (Watch 中的cb)

3、監聽配置的options

這裡會涉及到三個問題,現在來解釋

1、怎麼對設定的 key 進行監聽?

我們要先對 Watch 中的 this.getter 的函式進行理解,他的本質是為了獲取物件的key值

然後 getter 是在 watcher.get 中執行的,看下 get 原始碼

// 對本問題進行了獨家簡單化的原始碼

Watcher.prototype.get = function() {    

    var value = this.getter(this.vm);    

    return value

};

你能看到,Watch 在結尾會立即執行一次 watcher.get,其中便會執行 getter,便會根據你監聽的key,去例項上讀取並返回,存放在 watcher.value 上

看到了嗎,從例項上讀取屬性,這句話。

首先,watch 初始化之前,data 應該初始化完畢了,每個 data 資料都已經是響應式的

使用例子來說明一下

公眾號

當 watch.getter 執行,而讀取了 vm.name 的時候,name的依賴收集器就會收集到 watch-watcher

於是 name 變化的時候,會可以通知到 watch,監聽就成功了

2、如何進行深度監聽?

首先,深度監聽,是你設定了 deep 的時候,如下

公眾號

然後,觀察上面的 Watch 原始碼,deep 會儲存在watcher 中,以便後用

話鋒一轉

上一問題說過,在 新建 watcher 的時候,會馬上執行一個 get,上個問題的 get 原始碼簡化很多,把 處理深度監聽的部分去掉了,這裡露出來了

Watcher.prototype.get = function() {
    Dep.target= this

    var value = this.getter(this.vm)    

    if (this.deep)  traverse(value)

    Dep.target= null
    return value
};

沒錯,處理深度監聽只有一條語句!

if (this.deep)  traverse(value)

value 是 getter 從例項上讀取監聽key 得到的值,沒有疑問

但是 traverse 是何方神聖?come on 讓我們深入....

function traverse(val) {    

    var i, keys;    



    // 陣列逐個遍歷

    if (Array.isArray(val)) {

        i = val.length;        

        // val[i] 就是讀取值了,然後值的物件就能收集到 watch-watcher

        while (i--) {
           traverse(val[i])
        }
    }    

    else {

        keys = Object.keys(val);
        i = keys.length;        

        // val[keys[i]] 就是讀取值了,然後值的物件就能收集到 watch-watcher

        while (i--) {
           traverse(val[keys[i]])
        }
    }
}

你看它這段程式碼長,其實是個紙老虎,做的就是一個事情,不斷遞迴深入讀取物件

他的想法是這樣的

因為讀取,就可以讓這個屬性收集到 watch-watcher 的原則

就算是深層級的物件,其中的每個屬性也都是響應式的,每個屬性都有自己的依賴收集器

通過不斷深入的讀取每個屬性,這樣每個屬性就都可以收集到 watch-watcher 了

這樣不管物件內多深的屬性變化,都會通知到 watch-watcher

於是這樣就完成了深度監聽

3、監聽值變化,如何觸發監聽函式?

通過上面的問題,我們已經瞭解了大部分了

監聽的資料變化的時候,就能通知 watch-watcher 更新,所謂通知更新,就是手動呼叫 watch.update

速度看下 watcher.update 原始碼

Watcher.prototype.update= function() {    

    var value = this.get();    

    if (this.deep) {        

        var oldValue = this.value;        

        this.value = value;        

        // cb 是監聽回撥
        this.cb.call(this.vm, value, oldValue);
    }
};

很簡單嘛,就是讀取一遍值,然後儲存新值,接著 呼叫 監聽回撥,並傳入新值和 舊值

ok,就這樣

相關推薦

Vue原理Watch

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】Watch - 原始碼版 今天繼續探索 Watch 原始碼,廢話不多說了 帶著我的幾個疑問開始 1、什麼時候初始化 2、怎麼確定監聽哪些值 3、深度監聽怎麼回事 4、怎麼觸發我的函式 這些問題的答案會摻

Vue原理Props

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】Props - 原始碼版 今天記錄 Props 原始碼流程,哎,這東西,就算是研究過了,也真是會隨著時間慢慢忘記的。 幸好我做了詳細的文章,忘記了什麼的,回憶起來必然是很快的。 好的,回到正題,Props 請你

Vue原理Mixins

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】Mixins - 原始碼版 今天探索的是 mixins 的原始碼,mixins 根據不同的選項型別會做不同的處理 篇幅會有些長,你知道的,有很多種選項型別的嘛,但不是很難。只是涉及原始碼難免會有些煩, 不過這篇文

Vue原理依賴更新

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】依賴更新 - 原始碼版 如果對依賴收集完全沒有概念的同學,可以先看我這篇白話版 響應式原理 - 白話版 我們已經講過了 依賴收集 【Vue原理】依賴收集 - 原始碼版之基本資料型別 【Vue

Vue原理VNode

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】VNode - 原始碼版 今天就來探索 VNode 的原始碼,VNode 是 Vue2 渲染機制中很重要的一部分,是深入Vue 必須瞭解的部分 我們以4個問題來開始我們的探索 1、vnode 是什麼及其作用

Vue原理Event

如果你覺得排版難看,請點選 下面連結 或者 拉到 下面關注公眾號也可以吧 【Vue原理】Event - 原始碼版 之 自定義事件 Vue 的自定義事件很簡單,就是使用 觀察者模式 進行事件的監聽和分發 Vue 封裝的這個觀察者模式,可以說是很完善了,這個可以獨立抽取出來的在其他專案中使用的程式碼,

Vue原理NextTick

寫文章不容易,點個讚唄兄弟 專注 Vue 原始碼分享,文章分為白話版和 原始碼版,白話版助於理解工作原理,原始碼版助於瞭解內部詳

Vue原理Render

寫文章不容易,點個讚唄兄弟 專注 Vue 原始碼分享,文章分為白話版和 原始碼版,白話版助於理解工作原理,原始碼版助於瞭解內部詳

Vue原理Diff

寫文章不容易,點個讚唄兄弟 <br> <br> 專注 Vue 原始碼分享,文章分為白話版和 原始碼版,

vue.js入門

emp 寫到 logs 組件 images href one mooc 渲染 慕課網視頻學習筆記:http://www.imooc.com/learn/694 1.將html、js、css寫到一個後綴名.vue的文件中,區分這三種類型是通過<template>、

底層原理四位計算機的原理及其實現

一點 led燈 waiting lean div rm2 src and nvt 你是否想過,計算機為什麽會加減乘除?或者更直接一點,計算機的原理到底是什麽? Waitingforfriday有一篇詳細的教程,講解了如何自己動手,制作一臺四位計算機。從中可以看到,二進制、數

計算機原理程序執行過程

進程 cnblogs div 空間 時間片 chat 內存管理 tro alt 本章主要介紹程序執行過程中操作系統、CPU都幹了什麽 運行前 程序在運行前,只是在硬盤上待著,此時就是一堆二進制代碼而已,沒有任何作用。 程序只有進入了內存才能運行,但是要進入內存,則需要服從操

計算機原理CPU部分.md

工作 信號 通過 臃腫 流水線 處理 2.6 操作 ade 本文由CPU阿甘改編而得,主要講的是系統啟動和程序執行時CPU做的工作。 CPU的構成 中央處理器(CPU,Central Processing Unit)由運算器、控制器、Cache等。 控制器:主要是對指令進

組成原理第一章 計算機系統概述

表示 運算 傳遞 intro 指令 掌握 周期 style 主存 重點掌握:MAR和MDR的含義,主存容量大小、CPU執行時間的計算,性能指標CPI、MIPS、主頻等等。 1. 存儲單元:CPU訪問存儲器的基本單位,每個單元有一個地址。通常是字節大小的整數倍。 2. CPU

編譯原理c++實現自下而上語法分析器

不可 acm times style size PC -i 表達式 鏈接 寫在前面:本博客為本人原創,嚴禁任何形式的轉載!本博客只允許放在博客園(.cnblogs.com),如果您在其他網站看到這篇博文,請通過下面這個唯一的合法鏈接轉到原文! 本博客全網唯一合法URL:ht

計數原理UVA11538 Chess Queen

put typename return col nbsp 題意 putchar cst putc 傳送門 Description   給你一個n*m的棋盤,在棋盤上放置一黑一白兩個皇後,求兩個皇後能夠互相攻擊的方案個數 Input   多組數據,每組數據包括:

MongoDB 復制集 第 二 部 之選舉原理

command primary red and 優先權 mongo mongodb 主機 set 目錄: 1·復制與選舉的原理與驗證2·oplog 日誌調整3·配置復制集的優先級4·部署認證的復制5·總結 復制與選舉的原理: 上一篇文章搭建了多臺實例,部署成復制集,

Memcached 主主復制 + Keepalived 高可用架構附上原理

ima pki nag ali event 主服務器 figure tel outer 目錄: 1·Memcached 主主復制概念2·Memcached 高可用的實現3·案例部署4·總結 Memcached 主主復制概念 (1)主主復制概念: Memcached

Windows原理非同步IO-_APC(非同步過程呼叫)

// 同步IO的缺點是, 在讀寫檔案時, 如果檔案太大, 或者讀寫的時間太長, 就會在讀寫函式中 // 阻塞住. // 非同步IO解決了這個問題, 非同步IO讀寫檔案時, 檔案再大也不會阻塞住 // 但是非同步IO要完成這樣的特性是有一點付出的 // 非同步讀寫檔案後, 需要通過一些方式

Windows原理IO非同步-等待事件物件

/* 同步IO的缺點是, 在讀寫檔案時, 如果檔案太大, 或者讀寫的時間太長, 就會在讀寫函式中 阻塞住. 非同步IO解決了這個問題, 非同步IO讀寫檔案時, 檔案再大也不會阻塞住 但是非同步IO要完成這樣的特性是有一點付出的 非同步讀寫檔案後, 需要通過一些方式才能知道檔案讀寫(IO