摘要:具體代碼執行方式進入到的目錄下,命令行運行即可。確保為一個對象如果對象下有則不需要再次生成函數返回該對象的實例,這里判斷了如果該對象下已經有實例,則直接返回,不再去生產實例。這就確保了一個對象下的實例僅被實例化一次。
看這篇之前,如果沒有看過之前的文章,可拉到文章末尾查看之前的文章。
回顧在 step4 中,我們大致實現了一個 MVVM 的框架,由3個部分組成:
defineReactive 控制了對象屬性,使變為可監聽結構
Dep 收集管理依賴
Watcher 一個抽象的依賴
defineReactive 和 Dep 改造了對象下的某個屬性,將目標變成了觀察者模式中的目標,當目標發生變化時,會調用觀察者;
Watcher 就是一個具體的觀察者,會注冊到目標中。
之前的代碼實現了觀察者模式,使得數據的變化得以響應,但是還是有兩個需要優化的地方:
如果我們想讓對象的屬性都得以響應,那我們必須對對象下的所有屬性進行遍歷,依次調用 defineReactive 這不是很方便
代碼都在一個文件中,不利于管理
解決 問題2先解決第二個問題,我們僅僅需要把代碼進行劃分即可,然后用 webpack/babel 打包即可,當然這里就不說如何去配置 webpack ,使用 webpack/babel 我們就可以使用 ES6 的語法和模塊系統了。
但是為了偷懶,我把代碼直接放在 node 環境中執行了,但是 import 語法需要特定的 node 版本,我這里使用的是 8.11.1(版本網上都應該是支持的),同時需要特定的文件后綴(.mjs)和命令 node --experimental-modules xxx.mjs。
具體代碼
執行方式進入到 step5 的目錄下,命令行運行 node --experimental-modules test.mjs 即可。
當然你也可以用 webpack/babel 進行打包和轉碼,然后放到瀏覽器上運行即可。
問題1對于問題1,我們需要做的僅僅是實現一個方法進行遍歷對象屬性即可。我們把這個過程抽象成一個對象 Observe 。至于為什么要把這個過程抽象成一個對象,后面會說。
注: 由于是在 node 環境下運行代碼,這里就直接用 ES6 的語法了。同樣的我把別的模塊也用 ES6 語法寫了一遍。
export class Observer { constructor(value) { this.value = value this.walk(value) // 標志這個對象已經被遍歷過,同時保存 Observer Object.defineProperty(value, "__ob__", { value: this, enumerable: false, writable: true, configurable: true }) } walk(obj) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i], obj[keys[i]]) } } }
從代碼可以看出,這個類在實例化的時候自動遍歷了傳入參數下的所有屬性(value),并把每個屬性都應用了 defineReactive 。
為了確保傳入的值為對象,我們再寫一個方法來判斷。
export function observe (value) { // 確保 observe 為一個對象 if (typeof value !== "object") { return } let ob // 如果對象下有 Observer 則不需要再次生成 Observer if (value.hasOwnProperty("__ob__") && value.__ob__ instanceof Observer) { ob = value.__ob__ } else if (Object.isExtensible(value)) { ob = new Observer(value) } return ob }
函數返回該對象的 Observer 實例,這里判斷了如果該對象下已經有 Observer 實例,則直接返回,不再去生產 Observer 實例。這就確保了一個對象下的 Observer 實例僅被實例化一次。
上面代碼實現了對某個對象下所有屬性的轉化,但是如果對象下的某個屬性是對象呢?
所以我們還需改造一下 defineReactive 具體代碼為:
export function defineReactive(object, key, value) { let dep = new Dep() // 遍歷 value 下的屬性,由于在 observe 中已經判斷是否為對象,這里就不判斷了 observe(value) Object.defineProperty(object, key, { configurable: true, enumerable: true, get: function () { if (Dep.target) { dep.addSub(Dep.target) Dep.target.addDep(dep) } return value }, set: function (newValue) { if (newValue !== value) { value = newValue dep.notify() } } }) }
ok 我們來測試下
import Watcher from "./Watcher" import {observe} from "./Observe" let object = { num1: 1, num2: 1, objectTest: { num3: 1 } } observe(object) let watcher = new Watcher(object, function () { return this.num1 + this.num2 + this.objectTest.num3 }, function (newValue, oldValue) { console.log(`監聽函數,${object.num1} + ${object.num2} + ${object.objectTest.num3} = ${newValue}`) }) object.num1 = 2 // 監聽函數,2 + 1 + 1 = 4 object.objectTest.num3 = 2 // 監聽函數,2 + 1 + 2 = 5
當然為了更好的了解這個過程,最好把 step5 目錄中的代碼拉下來一起看。至于之前實現的功能這里就不專門寫測試了。
最后最后解釋下為什么要把遍歷對象屬性這個過程抽象成一個對象
對象在 js 下存放是是引用,也就是說有可能幾個對象下的某個屬性是同一個對象下的引用,如下
let obj1 = {num1: 1} let obj2 = {obj: obj1} let obj3 = {obj: obj1}
如果我們抽象成對象,而僅僅是函數調用的話,那么 obj1 這個對象就會遍歷兩次,而抽象成一個對象的話,我們可以把這個對象保存在 obj1 下(__ob__ 屬性),遍歷的時候判斷一下就好。
當然解決上面問題我們也可以在 obj1 下設置一個標志位即可,但是這個對象在之后會有特殊的用途,先這樣寫吧。(與數組和 Vue.set 有關)
在代碼中我為 Dep 和 Watch 添加了 id 這個暫時用不到,先加上。
點擊查看相關代碼
系列文章地址VUE - MVVM - part1 - defineProperty
VUE - MVVM - part2 - Dep
VUE - MVVM - part3 - Watcher
VUE - MVVM - part4 - 優化Watcher
VUE - MVVM - part5 - Observe
VUE - MVVM - part6 - Array
VUE - MVVM - part7 - Event
VUE - MVVM - part8 - 優化Event
VUE - MVVM - part9 - Vue
VUE - MVVM - part10 - Computed
VUE - MVVM - part11 - Extend
VUE - MVVM - part12 - props
VUE - MVVM - part13 - inject & 總結
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/94305.html
摘要:回顧在前面的幾個中,我們實現對象的屬性的監聽,但是有關于數組的行為我們一直沒有處理。并且上述的幾個數組方法是數組對象提供的,我們要想辦法去觸發下的函數。在設置值的時候就能成功觸發依賴。 看這篇之前,如果沒有看過之前的文章,可拉到文章末尾查看之前的文章。 回顧 在前面的幾個 step 中,我們實現對象的屬性的監聽,但是有關于數組的行為我們一直沒有處理。我們先分析下導致數組有哪些行為: ...
摘要:調用父類的方法類在我們上一步已經實現。我們先實現的綁定,因為是要被監聽,所以要進行進一步的處理。調用父類的方法方法綁定完事,其實就這么簡單。 看這篇之前,如果沒有看過之前的文章,可拉到文章末尾查看之前的文章。 前言 激動人心的時候即將來臨,之前我們做的 8 步,其實都在為這一步打基礎,這一步,我們來簡單實現一個 Vue 對象,還沒有看過之前代碼的同學,請確認看過之前的文章。 主要實現內...
摘要:在中關于如何實現在網上可以搜出不少,在看了部分源碼后,梳理一下內容。換個說法,當我們取值的時候,函數自動幫我們添加了針對當前值的依賴,當這個值發生變化的時候,處理了這些依賴,比如說節點的變化。 在 VUE 中關于如何實現在網上可以搜出不少,在看了部分源碼后,梳理一下內容。 首先,我們需要了解一下 js 中的一個 API :Object.defineProperty(obj, prop,...
摘要:事件是什么在標準瀏覽器中,我們經常使用來為一個添加一個事件等。仔細看這些情況,歸結到代碼中,無非就是一個行為或情況的名稱,和一些列的動作,而在中動作就是,一系列的動作就是一個函數的集合。 看這篇之前,如果沒有看過之前的文章,可拉到文章末尾查看之前的文章。 事件是什么? 在標準瀏覽器中,我們經常使用:addEventListener 來為一個 DOM 添加一個事件(click、mouse...
摘要:看這篇之前,如果沒有看過之前的文章,移步拉到文章末尾查看之前的文章。而該組件實例的父實例卻并不固定,所以我們將這些在使用時才能確定的參數在組件實例化的時候傳入。系列文章地址優化優化總結 看這篇之前,如果沒有看過之前的文章,移步拉到文章末尾查看之前的文章。 前言 在上一步,我們實現 extend 方法,用于擴展 Vue 類,而我們知道子組件需要通過 extend 方法來實現,我們從測試例...
閱讀 564·2023-04-25 16:00
閱讀 1598·2019-08-26 13:54
閱讀 2496·2019-08-26 13:47
閱讀 3402·2019-08-26 13:39
閱讀 1037·2019-08-26 13:37
閱讀 2734·2019-08-26 10:21
閱讀 3534·2019-08-23 18:19
閱讀 1601·2019-08-23 18:02