您好,登錄后才能下訂單哦!
這篇“VUE響應式原理實例代碼分析”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“VUE響應式原理實例代碼分析”文章吧。
1.defineProperty 的應用
在Vue2.X 響應式中使用到了 defineProperty 進行數據劫持,所以我們對它必須有一定的了解,那么我們先來了解它的使用方法把, 這里我們來使用 defineProperty來模擬 Vue 中的 data。
<body> <div id="app"></div> <script> // 模擬 Vue的data let data = { msg: '', } // 模擬 Vue 實例 let vm = {} // 對 vm 的 msg 進行數據劫持 Object.defineProperty(vm, 'msg', { // 獲取數據 get() { return data.msg }, // 設置 msg set(newValue) { // 如果傳入的值相等就不用修改 if (newValue === data.msg) return // 修改數據 data.msg = newValue document.querySelector('#app').textContent = data.msg }, }) // 這樣子就調用了 defineProperty vm.msg 的 set vm.msg = '1234' </script> </body>
2.defineProperty修改多個參數為響應式
修改多個參數
看了上面的方法只能修改一個屬性,實際上我們 data 中數據不可能只有一個,我們何不定義一個方法把data中的數據進行遍歷都修改成響應式呢
<body> <div id="app"></div> <script> // 模擬 Vue的data let data = { msg: '哈哈', age: '18', } // 模擬 Vue 實例 let vm = {} // 把多個屬性轉化 響應式 function proxyData() { // 把data 中每一項都[msg,age] 拿出來操作 Object.keys(data).forEach((key) => { // 對 vm 的 屬性 進行數據劫持 Object.defineProperty(vm, key, { // 可枚舉 enumerable: true, // 可配置 configurable: true, // 獲取數據 get() { return data[key] }, // 設置 屬性值 set(newValue) { // 如果傳入的值相等就不用修改 if (newValue === data[key]) return // 修改數據 data[key] = newValue document.querySelector('#app').textContent = data[key] }, }) }) } // 調用方法 proxyData(data) </script> </body>
3.Proxy
在Vue3 中使用 Proxy 來設置響應式的屬性
先來了解下 Proxy 的兩個參數
new Proxy(target,handler)
target
:要使用 Proxy
包裝的目標對象(可以是任何類型的對象,包括原生數組,函數,甚至另一個代理)
handler
:一個通常以函數作為屬性的對象,各屬性中的函數分別定義了在執行各種操作時代理 p
的行為
其實 和 Vue2.X實現的邏輯差不多,不過實現的方法不一樣
那么就放上代碼了
<body> <div id="app"></div> <script> // 模擬 Vue data let data = { msg: '', age: '', } // 模擬 Vue 的一個實例 // Proxy 第一個 let vm = new Proxy(data, { // get() 獲取值 // target 表示需要代理的對象這里指的就是 data // key 就是對象的 鍵 get(target, key) { return target[key] }, // 設置值 // newValue 是設置的值 set(target, key, newValue) { // 也先判斷下是否和之前的值一樣 節省性能 if (target[key] === newValue) return // 進行設置值 target[key] = newValue document.querySelector('#app').textContent = target[key] }, }) </script> </body>
觸發set 和 get 的方法
// 觸發了set方法 vm.msg = 'haha' // 觸發了get方法 console.log(vm.msg)
4.發布訂閱模式
在Vue 響應式中應用到了 發布訂閱模式 我們先來了解下
首先來說簡單介紹下 一共有三個角色
發布者、 訂閱者、 信號中心 舉個現實中例子 作者(發布者)寫一篇文章 發到了掘金(信號中心) ,掘金可以處理文章然后推送到了首頁,然后各自大佬(訂閱者)就可以訂閱文章
在Vue 中的例子 就是EventBus $on
$emit
那么我們就簡單模仿一下 Vue 的事件總線吧
之前代碼縮進4個單位有點寬,這里改成2個
<body> <div id="app"></div> <script> class Vue { constructor() { // 用來存儲事件 // 存儲的 例子 this.subs = { 'myclick': [fn1, fn2, fn3] ,'inputchange': [fn1, fn2] } this.subs = {} } // 實現 $on 方法 type是任務隊列的類型 ,fn是方法 $on(type, fn) { // 判斷在 subs是否有當前類型的 方法隊列存在 if (!this.subs[type]) { // 沒有就新增一個 默認為空數組 this.subs[type] = [] } // 把方法加到該類型中 this.subs[type].push(fn) } // 實現 $emit 方法 $emit(type) { // 首先得判斷該方法是否存在 if (this.subs[type]) { // 獲取到參數 const args = Array.prototype.slice.call(arguments, 1) // 循環隊列調用 fn this.subs[type].forEach((fn) => fn(...args)) } } } // 使用 const eventHub = new Vue() // 使用 $on 添加一個 sum 類型的 方法到 subs['sum']中 eventHub.$on('sum', function () { let count = [...arguments].reduce((x, y) => x + y) console.log(count) }) // 觸發 sum 方法 eventHub.$emit('sum', 1, 2, 4, 5, 6, 7, 8, 9, 10) </script> </body>
5.觀察者模式
與 發布訂閱 的差異
與發布訂閱者不同 觀察者中 發布者和訂閱者(觀察者)是相互依賴的 必須要求觀察者訂閱內容改變事件 ,而發布訂閱者是由調度中心進行調度,那么看看觀察者模式 是如何相互依賴,下面就舉個簡單例子
<body> <div id="app"></div> <script> // 目標 class Subject { constructor() { this.observerLists = [] } // 添加觀察者 addObs(obs) { // 判斷觀察者是否有 和 存在更新訂閱的方法 if (obs && obs.update) { // 添加到觀察者列表中 this.observerLists.push(obs) } } // 通知觀察者 notify() { this.observerLists.forEach((obs) => { // 每個觀察者收到通知后 會更新事件 obs.update() }) } // 清空觀察者 empty() { this.subs = [] } } class Observer { // 定義觀察者內容更新事件 update() { // 在更新事件要處理的邏輯 console.log('目標更新了') } } // 使用 // 創建目標 let sub = new Subject() // 創建觀察者 let obs1 = new Observer() let obs2 = new Observer() // 把觀察者添加到列表中 sub.addObs(obs1) sub.addObs(obs2) // 目標開啟了通知 每個觀察者者都會自己觸發 update 更新事件 sub.notify() </script> </body>
6.模擬Vue的響應式原理
這里來實現一個小型簡單的 Vue 主要實現以下的功能
接收初始化的參數,這里只舉幾個簡單的例子 el data options
通過私有方法 _proxyData 把data 注冊到 Vue 中 轉成getter setter
使用 observer 把 data 中的屬性轉為 響應式 添加到 自身身上
使用 observer 方法監聽 data 的所有屬性變化來 通過觀察者模式 更新視圖
使用 compiler 編譯元素節點上面指令 和 文本節點差值表達式
在這里獲取到 el
data
通過 _proxyData 把 data的屬性 注冊到Vue 并轉成 getter setter
/* vue.js */ class Vue { constructor(options) { // 獲取到傳入的對象 沒有默認為空對象 this.$options = options || {} // 獲取 el this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el // 獲取 data this.$data = options.data || {} // 調用 _proxyData 處理 data中的屬性 this._proxyData(this.$data) } // 把data 中的屬性注冊到 Vue _proxyData(data) { Object.keys(data).forEach((key) => { // 進行數據劫持 // 把每個data的屬性 到添加到 Vue 轉化為 getter setter方法 Object.defineProperty(this, key, { // 設置可以枚舉 enumerable: true, // 設置可以配置 configurable: true, // 獲取數據 get() { return data[key] }, // 設置數據 set(newValue) { // 判斷新值和舊值是否相等 if (newValue === data[key]) return // 設置新值 data[key] = newValue }, }) }) } }
在這里把 data 中的 屬性變為響應式加在自身的身上,還有一個主要功能就是 觀察者模式在 第 4.dep.js
會有詳細的使用
/* observer.js */ class Observer { constructor(data) { // 用來遍歷 data this.walk(data) } // 遍歷 data 轉為響應式 walk(data) { // 判斷 data是否為空 和 對象 if (!data || typeof data !== 'object') return // 遍歷 data Object.keys(data).forEach((key) => { // 轉為響應式 this.defineReactive(data, key, data[key]) }) } // 轉為響應式 // 要注意的 和vue.js 寫的不同的是 // vue.js中是將 屬性給了 Vue 轉為 getter setter // 這里是 將data中的屬性轉為getter setter defineReactive(obj, key, value) { // 如果是對象類型的 也調用walk 變成響應式,不是對象類型的直接在walk會被return this.walk(value) // 保存一下 this const self = this Object.defineProperty(obj, key, { // 設置可枚舉 enumerable: true, // 設置可配置 configurable: true, // 獲取值 get() { return value }, // 設置值 set(newValue) { // 判斷舊值和新值是否相等 if (newValue === value) return // 設置新值 value = newValue // 賦值的話如果是newValue是對象,對象里面的屬性也應該設置為響應式的 self.walk(newValue) }, }) } }
在html中引入的話注意順序
<script src="./js/observer.js"></script> <script src="./js/vue.js"></script>
然后在vue.js 中使用 Observer
/* vue.js */ class Vue { constructor(options) { ... // 使用 Obsever 把data中的數據轉為響應式 new Observer(this.$data) } // 把data 中的屬性注冊到 Vue _proxyData(data) { ... } }
看到這里為什么做了兩個重復性的操作呢?重復性兩次把 data的屬性轉為響應式
在obsever.js 中是把 data 的所有屬性 加到 data 自身 變為響應式 轉成 getter setter方式
在vue.js 中 也把 data的 的所有屬性 加到 Vue 上,是為了以后方面操作可以用 Vue 的實例直接訪問到 或者在 Vue 中使用 this 訪問
使用例子:
<body> <div id="app"></div> <script src="./js/observer.js"></script> <script src="./js/vue.js"></script> <script> let vm = new Vue({ el: '#app', data: { msg: '123', age: 21, }, }) </script> </body>
這樣在Vue
和 $data
中都存在了 所有的data 屬性了 并且是響應式的
comilper.js在這個文件里實現對文本節點 和 元素節點指令編譯 主要是為了舉例子 當然這個寫的很簡單 指令主要實現 v-text v-model
/* compiler.js */ class Compiler { // vm 指 Vue 實例 constructor(vm) { // 拿到 vm this.vm = vm // 拿到 el this.el = vm.$el // 編譯模板 this.compile(this.el) } // 編譯模板 compile(el) { // 獲取子節點 如果使用 forEach遍歷就把偽數組轉為真的數組 let childNodes = [...el.childNodes] childNodes.forEach((node) => { // 根據不同的節點類型進行編譯 // 文本類型的節點 if (this.isTextNode(node)) { // 編譯文本節點 this.compileText(node) } else if (this.isElementNode(node)) { //元素節點 this.compileElement(node) } // 判斷是否還存在子節點考慮遞歸 if (node.childNodes && node.childNodes.length) { // 繼續遞歸編譯模板 this.compile(node) } }) } // 編譯文本節點(簡單的實現) compileText(node) { // 核心思想利用把正則表達式把{{}}去掉找到里面的變量 // 再去Vue找這個變量賦值給node.textContent let reg = /\{\{(.+?)\}\}/ // 獲取節點的文本內容 let val = node.textContent // 判斷是否有 {{}} if (reg.test(val)) { // 獲取分組一 也就是 {{}} 里面的內容 去除前后空格 let key = RegExp.$1.trim() // 進行替換再賦值給node node.textContent = val.replace(reg, this.vm[key]) } } // 編譯元素節點這里只處理指令 compileElement(node) { // 獲取到元素節點上面的所有屬性進行遍歷 ![...node.attributes].forEach((attr) => { // 獲取屬性名 let attrName = attr.name // 判斷是否是 v- 開頭的指令 if (this.isDirective(attrName)) { // 除去 v- 方便操作 attrName = attrName.substr(2) // 獲取 指令的值就是 v-text = "msg" 中msg // msg 作為 key 去Vue 找這個變量 let key = attr.value // 指令操作 執行指令方法 // vue指令很多為了避免大量個 if判斷這里就寫個 uapdate 方法 this.update(node, key, attrName) } }) } // 添加指令方法 并且執行 update(node, key, attrName) { // 比如添加 textUpdater 就是用來處理 v-text 方法 // 我們應該就內置一個 textUpdater 方法進行調用 // 加個后綴加什么無所謂但是要定義相應的方法 let updateFn = this[attrName + 'Updater'] // 如果存在這個內置方法 就可以調用了 updateFn && updateFn(node, key, this.vm[key]) } // 提前寫好 相應的指定方法比如這個 v-text // 使用的時候 和 Vue 的一樣 textUpdater(node, key, value) { node.textContent = value } // v-model modelUpdater(node, key, value) { node.value = value } // 判斷元素的屬性是否是 vue 指令 isDirective(attr) { return attr.startsWith('v-') } // 判斷是否是元素節點 isElementNode(node) { return node.nodeType === 1 } // 判斷是否是 文本 節點 isTextNode(node) { return node.nodeType === 3 } }
寫一個Dep類 它相當于 觀察者中的發布者 每個響應式屬性都會創建這么一個 Dep 對象 ,負責收集該依賴屬性的Watcher對象 (是在使用響應式數據的時候做的操作)
當我們對響應式屬性在 setter 中進行更新的時候,會調用 Dep 中 notify 方法發送更新通知
然后去調用 Watcher 中的 update 實現視圖的更新操作(是當數據發生變化的時候去通知觀察者調用觀察者的update更新視圖)
總的來說 在Dep(這里指發布者) 中負責收集依賴 添加觀察者(這里指Wathcer),然后在 setter 數據更新的時候通知觀察者
說的這么多重復的話,大家應該知道是在哪個階段 收集依賴 哪個階段 通知觀察者了吧,下面就來實現一下吧
先寫Dep類
/* dep.js */ class Dep { constructor() { // 存儲觀察者 this.subs = [] } // 添加觀察者 addSub(sub) { // 判斷觀察者是否存在 和 是否擁有update方法 if (sub && sub.update) { this.subs.push(sub) } } // 通知方法 notify() { // 觸發每個觀察者的更新方法 this.subs.forEach((sub) => { sub.update() }) } }
在 obsever.js 中使用Dep
在 get 中添加 Dep.target (觀察者)
在 set 中 觸發 notify (通知)
/* observer.js */ class Observer { ... } // 遍歷 data 轉為響應式 walk(data) { ... } // 這里是 將data中的屬性轉為getter setter defineReactive(obj, key, value) { ... // 創建 Dep 對象 let dep = new Dep() Object.defineProperty(obj, key, { ... // 獲取值 get() { // 在這里添加觀察者對象 Dep.target 表示觀察者 Dep.target && dep.addSub(Dep.target) return value }, // 設置值 set(newValue) { if (newValue === value) return value = newValue self.walk(newValue) // 觸發通知 更新視圖 dep.notify() }, }) } }
**watcher **的作用 數據更新后 收到通知之后 調用 update 進行更新
/* watcher.js */ class Watcher { constructor(vm, key, cb) { // vm 是 Vue 實例 this.vm = vm // key 是 data 中的屬性 this.key = key // cb 回調函數 更新視圖的具體方法 this.cb = cb // 把觀察者的存放在 Dep.target Dep.target = this // 舊數據 更新視圖的時候要進行比較 // 還有一點就是 vm[key] 這個時候就觸發了 get 方法 // 之前在 get 把 觀察者 通過dep.addSub(Dep.target) 添加到了 dep.subs中 this.oldValue = vm[key] // Dep.target 就不用存在了 因為上面的操作已經存好了 Dep.target = null } // 觀察者中的必備方法 用來更新視圖 update() { // 獲取新值 let newValue = this.vm[this.key] // 比較舊值和新值 if (newValue === this.oldValue) return // 調用具體的更新方法 this.cb(newValue) } }
那么去哪里創建 Watcher 呢?還記得在 compiler.js中 對文本節點的編譯操作嗎
在編譯完文本節點后 在這里添加一個 Watcher
還有 v-text v-model 指令 當編譯的是元素節點 就添加一個 Watcher
/* compiler.js */ class Compiler { // vm 指 Vue 實例 constructor(vm) { // 拿到 vm this.vm = vm // 拿到 el this.el = vm.$el // 編譯模板 this.compile(this.el) } // 編譯模板 compile(el) { let childNodes = [...el.childNodes] childNodes.forEach((node) => { if (this.isTextNode(node)) { // 編譯文本節點 this.compileText(node) } ... } // 編譯文本節點(簡單的實現) compileText(node) { let reg = /\{\{(.+)\}\}/ let val = node.textContent if (reg.test(val)) { let key = RegExp.$1.trim() node.textContent = val.replace(reg, this.vm[key]) // 創建觀察者 new Watcher(this.vm, key, newValue => { node.textContent = newValue }) } } ... // v-text textUpdater(node, key, value) { node.textContent = value // 創建觀察者2 new Watcher(this.vm, key, (newValue) => { node.textContent = newValue }) } // v-model modelUpdater(node, key, value) { node.value = value // 創建觀察者 new Watcher(this.vm, key, (newValue) => { node.value = newValue }) // 這里實現雙向綁定 監聽input 事件修改 data中的屬性 node.addEventListener('input', () => { this.vm[key] = node.value }) } }
當 我們改變 響應式屬性的時候 觸發了 set() 方法 ,然后里面 發布者 dep.notify 方法啟動了,拿到了 所有的 觀察者 watcher 實例去執行 update 方法調用了回調函數 cb(newValue) 方法并把 新值傳遞到了 cb() 當中 cb方法是的具體更新視圖的方法 去更新視圖
比如上面的例子里的第三個參數 cb方法
new Watcher(this.vm, key, newValue => { node.textContent = newValue })
還有一點要實現v-model的雙向綁定
不僅要通過修改數據來觸發更新視圖,還得為node添加 input 事件 改變 data數據中的屬性
來達到雙向綁定的效果
7.測試下自己寫的
到了目前為止 響應式 和 雙向綁定 都基本實現了 那么來寫個例子測試下
<body> <div id="app"> {{msg}} <br /> {{age}} <br /> <div v-text="msg"></div> <input v-model="msg" type="text" /> </div> <script src="./js/dep.js"></script> <script src="./js/watcher.js"></script> <script src="./js/compiler.js"></script> <script src="./js/observer.js"></script> <script src="./js/vue.js"></script> <script> let vm = new Vue({ el: '#app', data: { msg: '123', age: 21, }, }) </script> </body>
8.五個文件代碼
這里直接把5個文件個代碼貼出來 上面有的地方省略了,下面是完整的方便大家閱讀
vue.js
/* vue.js */ class Vue { constructor(options) { // 獲取到傳入的對象 沒有默認為空對象 this.$options = options || {} // 獲取 el this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el // 獲取 data this.$data = options.data || {} // 調用 _proxyData 處理 data中的屬性 this._proxyData(this.$data) // 使用 Obsever 把data中的數據轉為響應式 new Observer(this.$data) // 編譯模板 new Compiler(this) } // 把data 中的屬性注冊到 Vue _proxyData(data) { Object.keys(data).forEach((key) => { // 進行數據劫持 // 把每個data的屬性 到添加到 Vue 轉化為 getter setter方法 Object.defineProperty(this, key, { // 設置可以枚舉 enumerable: true, // 設置可以配置 configurable: true, // 獲取數據 get() { return data[key] }, // 設置數據 set(newValue) { // 判斷新值和舊值是否相等 if (newValue === data[key]) return // 設置新值 data[key] = newValue }, }) }) } }
obsever.js
/* observer.js */ class Observer { constructor(data) { // 用來遍歷 data this.walk(data) } // 遍歷 data 轉為響應式 walk(data) { // 判斷 data是否為空 和 對象 if (!data || typeof data !== 'object') return // 遍歷 data Object.keys(data).forEach((key) => { // 轉為響應式 this.defineReactive(data, key, data[key]) }) } // 轉為響應式 // 要注意的 和vue.js 寫的不同的是 // vue.js中是將 屬性給了 Vue 轉為 getter setter // 這里是 將data中的屬性轉為getter setter defineReactive(obj, key, value) { // 如果是對象類型的 也調用walk 變成響應式,不是對象類型的直接在walk會被return this.walk(value) // 保存一下 this const self = this // 創建 Dep 對象 let dep = new Dep() Object.defineProperty(obj, key, { // 設置可枚舉 enumerable: true, // 設置可配置 configurable: true, // 獲取值 get() { // 在這里添加觀察者對象 Dep.target 表示觀察者 Dep.target && dep.addSub(Dep.target) return value }, // 設置值 set(newValue) { // 判斷舊值和新值是否相等 if (newValue === value) return // 設置新值 value = newValue // 賦值的話如果是newValue是對象,對象里面的屬性也應該設置為響應式的 self.walk(newValue) // 觸發通知 更新視圖 dep.notify() }, }) } }
compiler.js
/* compiler.js */ class Compiler { // vm 指 Vue 實例 constructor(vm) { // 拿到 vm this.vm = vm // 拿到 el this.el = vm.$el // 編譯模板 this.compile(this.el) } // 編譯模板 compile(el) { // 獲取子節點 如果使用 forEach遍歷就把偽數組轉為真的數組 let childNodes = [...el.childNodes] childNodes.forEach((node) => { // 根據不同的節點類型進行編譯 // 文本類型的節點 if (this.isTextNode(node)) { // 編譯文本節點 this.compileText(node) } else if (this.isElementNode(node)) { //元素節點 this.compileElement(node) } // 判斷是否還存在子節點考慮遞歸 if (node.childNodes && node.childNodes.length) { // 繼續遞歸編譯模板 this.compile(node) } }) } // 編譯文本節點(簡單的實現) compileText(node) { // 核心思想利用把正則表達式把{{}}去掉找到里面的變量 // 再去Vue找這個變量賦值給node.textContent let reg = /\{\{(.+?)\}\}/ // 獲取節點的文本內容 let val = node.textContent // 判斷是否有 {{}} if (reg.test(val)) { // 獲取分組一 也就是 {{}} 里面的內容 去除前后空格 let key = RegExp.$1.trim() // 進行替換再賦值給node node.textContent = val.replace(reg, this.vm[key]) // 創建觀察者 new Watcher(this.vm, key, (newValue) => { node.textContent = newValue }) } } // 編譯元素節點這里只處理指令 compileElement(node) { // 獲取到元素節點上面的所有屬性進行遍歷 ![...node.attributes].forEach((attr) => { // 獲取屬性名 let attrName = attr.name // 判斷是否是 v- 開頭的指令 if (this.isDirective(attrName)) { // 除去 v- 方便操作 attrName = attrName.substr(2) // 獲取 指令的值就是 v-text = "msg" 中msg // msg 作為 key 去Vue 找這個變量 let key = attr.value // 指令操作 執行指令方法 // vue指令很多為了避免大量個 if判斷這里就寫個 uapdate 方法 this.update(node, key, attrName) } }) } // 添加指令方法 并且執行 update(node, key, attrName) { // 比如添加 textUpdater 就是用來處理 v-text 方法 // 我們應該就內置一個 textUpdater 方法進行調用 // 加個后綴加什么無所謂但是要定義相應的方法 let updateFn = this[attrName + 'Updater'] // 如果存在這個內置方法 就可以調用了 updateFn && updateFn.call(this, node, key, this.vm[key]) } // 提前寫好 相應的指定方法比如這個 v-text // 使用的時候 和 Vue 的一樣 textUpdater(node, key, value) { node.textContent = value // 創建觀察者 new Watcher(this.vm, key, (newValue) => { node.textContent = newValue }) } // v-model modelUpdater(node, key, value) { node.value = value // 創建觀察者 new Watcher(this.vm, key, (newValue) => { node.value = newValue }) // 這里實現雙向綁定 node.addEventListener('input', () => { this.vm[key] = node.value }) } // 判斷元素的屬性是否是 vue 指令 isDirective(attr) { return attr.startsWith('v-') } // 判斷是否是元素節點 isElementNode(node) { return node.nodeType === 1 } // 判斷是否是 文本 節點 isTextNode(node) { return node.nodeType === 3 } }
dep.js
/* dep.js */ class Dep { constructor() { // 存儲觀察者 this.subs = [] } // 添加觀察者 addSub(sub) { // 判斷觀察者是否存在 和 是否擁有update方法 if (sub && sub.update) { this.subs.push(sub) } } // 通知方法 notify() { // 觸發每個觀察者的更新方法 this.subs.forEach((sub) => { sub.update() }) } }
watcher.js
/* watcher.js */ class Watcher { constructor(vm, key, cb) { // vm 是 Vue 實例 this.vm = vm // key 是 data 中的屬性 this.key = key // cb 回調函數 更新視圖的具體方法 this.cb = cb // 把觀察者的存放在 Dep.target Dep.target = this // 舊數據 更新視圖的時候要進行比較 // 還有一點就是 vm[key] 這個時候就觸發了 get 方法 // 之前在 get 把 觀察者 通過dep.addSub(Dep.target) 添加到了 dep.subs中 this.oldValue = vm[key] // Dep.target 就不用存在了 因為上面的操作已經存好了 Dep.target = null } // 觀察者中的必備方法 用來更新視圖 update() { // 獲取新值 let newValue = this.vm[this.key] // 比較舊值和新值 if (newValue === this.oldValue) return // 調用具體的更新方法 this.cb(newValue) } }
以上就是關于“VUE響應式原理實例代碼分析”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。