Vue.js原理分析之observer模塊詳解
介紹
observer是Vue核心中最重要的一個模塊(個人認(rèn)為),能夠?qū)崿F(xiàn)視圖與數(shù)據(jù)的響應(yīng)式更新,底層全憑observer的支持。
注意:本文是針對Vue@2.1.8進(jìn)行分析
observer模塊在Vue項目中的代碼位置是src/core/observer,模塊共分為這幾個部分:
- Observer: 數(shù)據(jù)的觀察者,讓數(shù)據(jù)對象的讀寫操作都處于自己的監(jiān)管之下
- Watcher: 數(shù)據(jù)的訂閱者,數(shù)據(jù)的變化會通知到Watcher,然后由Watcher進(jìn)行相應(yīng)的操作,例如更新視圖
- Dep: Observer與Watcher的紐帶,當(dāng)數(shù)據(jù)變化時,會被Observer觀察到,然后由Dep通知到Watcher
示意圖如下:
Observer
Observer類定義在src/core/observer/index.js中,先來看一下Observer的構(gòu)造函數(shù)
constructor (value: any) { this.value = value this.dep = new Dep() this.vmCount = 0 def(value, '__ob__', this) if (Array.isArray(value)) { const augment = hasProto ? protoAugment : copyAugment augment(value, arrayMethods, arrayKeys) this.observeArray(value) } else { this.walk(value) } }
value是需要被觀察的數(shù)據(jù)對象,在構(gòu)造函數(shù)中,會給value增加__ob__屬性,作為數(shù)據(jù)已經(jīng)被Observer觀察的標(biāo)志。如果value是數(shù)組,就使用observeArray遍歷value,對value中每一個元素調(diào)用observe分別進(jìn)行觀察。如果value是對象,則使用walk遍歷value上每個key,對每個key調(diào)用defineReactive來獲得該key的set/get控制權(quán)。
解釋下上面用到的幾個函數(shù)的功能:
- observeArray: 遍歷數(shù)組,對數(shù)組的每個元素調(diào)用observe
- observe: 檢查對象上是否有__ob__屬性,如果存在,則表明該對象已經(jīng)處于Observer的觀察中,如果不存在,則new Observer來觀察對象(其實還有一些判斷邏輯,為了便于理解就不贅述了)
- walk: 遍歷對象的每個key,對對象上每個key的數(shù)據(jù)調(diào)用defineReactive
- defineReactive: 通過Object.defineProperty設(shè)置對象的key屬性,使得能夠捕獲到該屬性值的set/get動作。一般是由Watcher的實例對象進(jìn)行g(shù)et操作,此時Watcher的實例對象將被自動添加到Dep實例的依賴數(shù)組中,在外部操作觸發(fā)了set時,將通過Dep實例的notify來通知所有依賴的watcher進(jìn)行更新。
如果不太理解上面的文字描述可以看一下圖:
Dep
Dep是Observer與Watcher之間的紐帶,也可以認(rèn)為Dep是服務(wù)于Observer的訂閱系統(tǒng)。Watcher訂閱某個Observer的Dep,當(dāng)Observer觀察的數(shù)據(jù)發(fā)生變化時,通過Dep通知各個已經(jīng)訂閱的Watcher。
Dep提供了幾個接口:
- addSub: 接收的參數(shù)為Watcher實例,并把Watcher實例存入記錄依賴的數(shù)組中
- removeSub: 與addSub對應(yīng),作用是將Watcher實例從記錄依賴的數(shù)組中移除
- depend: Dep.target上存放這當(dāng)前需要操作的Watcher實例,調(diào)用depend會調(diào)用該Watcher實例的addDep方法,addDep的功能可以看下面對Watcher的介紹
- notify: 通知依賴數(shù)組中所有的watcher進(jìn)行更新操作
Watcher
Watcher是用來訂閱數(shù)據(jù)的變化的并執(zhí)行相應(yīng)操作(例如更新視圖)的。Watcher的構(gòu)造器函數(shù)定義如下:
constructor (vm, expOrFn, cb, options) { this.vm = vm vm._watchers.push(this) // options if (options) { this.deep = !!options.deep this.user = !!options.user this.lazy = !!options.lazy this.sync = !!options.sync } else { this.deep = this.user = this.lazy = this.sync = false } this.cb = cb this.id = ++uid // uid for batching this.active = true this.dirty = this.lazy // for lazy watchers this.deps = [] this.newDeps = [] this.depIds = new Set() this.newDepIds = new Set() this.expression = process.env.NODE_ENV !== 'production' ? expOrFn.toString() : '' if (typeof expOrFn === 'function') { this.getter = expOrFn } else { this.getter = parsePath(expOrFn) if (!this.getter) { this.getter = function () {} process.env.NODE_ENV !== 'production' && warn( `Failed watching path: "${expOrFn}" ` + 'Watcher only accepts simple dot-delimited paths. ' + 'For full control, use a function instead.', vm ) } } this.value = this.lazy ? undefined : this.get() }
參數(shù)中,vm表示組件實例,expOrFn表示要訂閱的數(shù)據(jù)字段(字符串表示,例如a.b.c)或是一個要執(zhí)行的函數(shù),cb表示watcher運行后的回調(diào)函數(shù),options是選項對象,包含deep、user、lazy等配置。
watcher實例上有這些方法:
- get: 將Dep.target設(shè)置為當(dāng)前watcher實例,在內(nèi)部調(diào)用this.getter,如果此時某個被Observer觀察的數(shù)據(jù)對象被取值了,那么當(dāng)前watcher實例將會自動訂閱數(shù)據(jù)對象的Dep實例
- addDep: 接收參數(shù)dep(Dep實例),讓當(dāng)前watcher訂閱dep
- cleanupDeps: 清除newDepIds和newDep上記錄的對dep的訂閱信息
- update: 立刻運行watcher或者將watcher加入隊列中等待統(tǒng)一flush
- run: 運行watcher,調(diào)用this.get()求值,然后觸發(fā)回調(diào)
- evaluate: 調(diào)用this.get()求值
- depend: 遍歷this.deps,讓當(dāng)前watcher實例訂閱所有dep
- teardown: 去除當(dāng)前watcher實例所有的訂閱
Array methods
在src/core/observer/array.js中,Vue框架對數(shù)組的push、pop、shift、unshift、sort、splice、reverse方法進(jìn)行了改造,在調(diào)用數(shù)組的這些方法時,自動觸發(fā)dep.notify(),解決了調(diào)用這些函數(shù)改變數(shù)組后無法觸發(fā)更新的問題。
在Vue的官方文檔中對這個也有說明:http://cn.vuejs.org/v2/guide/list.html#變異方法
總結(jié)
以上就是這篇文中的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。
相關(guān)文章
vue中el-autocomplete與el-select的異同
本文主要介紹了vue中el-autocomplete與el-select的異同,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05Vue判斷字符串(或數(shù)組)中是否包含某個元素的多種方法
在我們前端日常開發(fā)中經(jīng)常會遇到判斷一個字符串中是否包含某個元素的需求,下面這篇文章主要給大家介紹了關(guān)于Vue判斷字符串(或數(shù)組)中是否包含某個元素的多種方法,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09使用Vue開發(fā)自己的Chrome擴(kuò)展程序過程詳解
這篇文章主要介紹了使用Vue開發(fā)自己的Chrome擴(kuò)展程序過程詳解,瀏覽器擴(kuò)展程序是可以修改和增強 Web 瀏覽器功能的小程序。它們可用于各種任務(wù),例如阻止廣告,管理密碼,組織標(biāo)簽,改變網(wǎng)頁的外觀和行為等等。,需要的朋友可以參考下2019-06-06