摘要:的數據劫持版本內部使用了來實現數據與視圖的雙向綁定,體現在對數據的讀寫處理過程中。這樣就形成了數據的雙向綁定。
MVVM由以下三個內容組成
View:視圖模板
Model:數據模型
ViewModel:作為橋梁負責溝通View和Model,自動渲染模板
在JQuery時期,如果需要刷新UI時,需要先取到對應的DOM再更新UI,這樣數據和業務的邏輯就和頁面有強耦合。
在MVVM中,UI是挺數據驅動的,數據一旦改變就會刷新相應的UI,UI變化也會改變相應的數據。這種方式在開發中只需要關心數據的變化,不用直接去操作DOM。并且可以將一些可復用的邏輯放在一個ViewModel中,多個View復用這個ViewModel。
在 MVVM 中,最核心的也就是數據雙向綁定,例如 Angluar 的臟數據檢測,Vue 的數據劫持,React的數據綁定
Angluar 的臟數據檢測當觸發了指定事件后進入臟數據檢測,這時期會調用$digest循環遍歷所有的數據觀察者,判斷當前值是否和先前的值有區別,如果檢測到變化的話,會調用$watch函數,然后再次調用$digest循環直到發現沒有變化。所以這個過程可能會循環幾次,一直到不再有數據變化發生后,將變更的數據發送到視圖,更新頁面展現。如果是手動對 ViewModel 的數據進行變更,為確保變更同步到視圖,需要手動觸發一次“臟值檢測”。
臟數據檢測雖然需要每次去循環遍歷查看是否有數據變化,存在低效的問題,與Vue的雙向綁定原理不同,但是臟數據檢測能夠同時檢測出要更新的值,再去統一更新UI,這樣也可以減少操作DOM的次數。
Vue 的數據劫持Vue2.0版本內部使用了Object.defineProperty()來實現數據與視圖的雙向綁定,體現在對數據的讀寫處理過程中。即Object.defineProperty()中定義的數據set、get函數。
使用Object.defineProperty()實現Vue2.0雙向綁定的小demo:
---------------------------------------------------------------------- var obj = {}; Object.defineProperty(obj, "prop", { get: function() { return val; }, set: function(newVal) { val = newVal; document.getElementById("input").value = val; document.getElementById("text").innerHTML = val; } }); document.addEventListener("keyup", function(e) { obj.prop = e.target.value; });
如上所述,vue.js通過Object.defineProperty()來劫持各個屬性的setter,getter。再結合發布者-訂閱者的方式,發布消息給訂閱者,觸發相應的監聽回調。通過Directives指令去對DOM做封裝,當數據發生變化,會通知指令去修改對應的DOM,數據驅動DOM的變化。vue.js還會對操作做一些監聽(DOM Listener),當我們修改視圖的時候,vue.js監聽到這些變化,從而改變數據。這樣就形成了數據的雙向綁定。
在對數據進行讀取時,如果當前有 Watcher(對數據的觀察者,watcher 會負責將獲取的新數據發送給視圖),那將該 Watcher 綁定到當前的數據上(dep.depend(),dep 關聯當前數據和所有的 watcher 的依賴關系),是一個檢查并記錄依賴的過程。而在對數據進行賦值時,如果數據發生改變,則通知所有的 watcher(借助 dep.notify())。這樣,即便是我們手動改變了數據,框架也能夠自動將數據同步到視圖。
完整的簡易雙向綁定代碼如下:
var data = { name: "yck" } observe(data) let name = data.name // -> get value data.name = "yyy" // -> change value function observe(obj) { // 判斷類型 if (!obj || typeof obj !== "object") { return } Object.keys(obj).forEach(key => { defineReactive(obj, key, obj[key]) }) } function defineReactive(obj, key, val) { // 遞歸子屬性 observe(val) Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() { console.log("get value") return val }, set: function reactiveSetter(newVal) { console.log("change value") val = newVal } }) }
以上代碼簡單的實現了如何監聽數據的set和get,下面再給屬性添加發布訂閱
{{name}}
在解析模板的時候遇到{{ name }}就給屬性name添加發布訂閱
// 通過 Dep 解耦 class Dep { constructor() { this.subs = [] } addSub(sub) { // sub 是 Watcher 實例 this.subs.push(sub) } notify() { this.subs.forEach(sub => { sub.update() }) } } // 全局屬性,通過該屬性配置 Watcher Dep.target = null function update(value) { document.querySelector("div").innerText = value } class Watcher { constructor(obj, key, cb) { // 將 Dep.target 指向自己 // 然后觸發屬性的 getter 添加監聽 // 最后將 Dep.target 置空 Dep.target = this this.cb = cb this.obj = obj this.key = key this.value = obj[key] Dep.target = null } update() { // 獲得新值 this.value = this.obj[this.key] // 調用 update 方法更新 Dom this.cb(this.value) } } var data = { name: "yck" } observe(data) // 模擬解析到 `{{name}}` 觸發的操作 new Watcher(data, "name", update) // update Dom innerText data.name = "yyy"
下面對defineReactive函數進行改造:
function defineReactive(obj, key, val) { // 遞歸子屬性 observe(val) let dp = new Dep() Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() { console.log("get value") // 將 Watcher 添加到訂閱 if (Dep.target) { dp.addSub(Dep.target) } return val }, set: function reactiveSetter(newVal) { console.log("change value") val = newVal // 執行 watcher 的 update 方法 dp.notify() } }) }
核心思路就是手動觸發一次屬性的 getter 來實現發布訂閱的添加。
Vue3.0將用proxy代替Object.defineProperty()
Object.defineProperty()的缺陷:
1.只能對屬性進行數據劫持,所以需要深度遍歷整個對象
2.對于數組不能監聽到數據的變化
雖然 Vue 2.0中能檢測到數組數據的變化,但是其實是使用了 hack 的辦法,并且也是有缺陷的
const arrayProto = Array.prototype export const arrayMethods = Object.create(arrayProto) // hack 以下幾個函數 const methodsToPatch = [ "push", "pop", "shift", "unshift", "splice", "sort", "reverse" ] methodsToPatch.forEach(function(method) { // 獲得原生函數 const original = arrayProto[method] def(arrayMethods, method, function mutator(...args) { // 調用原生函數 const result = original.apply(this, args) const ob = this.__ob__ let inserted switch (method) { case "push": case "unshift": inserted = args break case "splice": inserted = args.slice(2) break } if (inserted) ob.observeArray(inserted) // 觸發更新 ob.dep.notify() return result }) })
Proxy原生支持監聽數組變化,并且可以直接對整個對象進行攔截,所以Vue3.0使用 Proxy 替換 Object.defineProperty
let onWatch = (obj, setBind, getLogger) => { let handler = { get(target, property, receiver) { getLogger(target, property) return Reflect.get(target, property, receiver) }, set(target, property, value, receiver) { setBind(value) return Reflect.set(target, property, value) } } return new Proxy(obj, handler) } let obj = { a: 1 } let value let p = onWatch( obj, v => { value = v }, (target, property) => { console.log(`Get "${property}" = ${target[property]}`) } ) p.a = 2 // bind `value` to `2` p.a // -> Get "a" = 2React的數據綁定
React的數據綁定是虛擬DOM樹的更新相關:
屬性更新,組件自己處理
結構更新,重新“渲染”子樹(虛擬DOM),找出最小改動步驟,打包DOM操作,給真實DOM樹打補丁
單純從數據綁定來看,React虛擬DOM沒有數據綁定,因為setState()不維護上一個狀態(狀態丟棄)
從數據更新機制來看,React類似于提供數據模型的方式(必須通過state更新)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/101461.html
摘要:發布訂閱現在每個人應該都用微信吧,一個人可以關注多個公眾號,多個人可以同時關注相同的公眾號。公眾號每周都會更新內容,并推送給我們,把寫好的文章在微信管理平臺更新就好了,點擊推送,就相當于發布。 什么是MVVM MVVM——Model-View-ViewModle的縮寫,MVC設計模式的改進版。Model是我們應用中的數據模型,View是我們的UI層,通過ViewModle,可以把我們M...
摘要:,而且每種框架雙向數據綁定的實現方式都不太一致,比如內部使用的是臟檢查,而內部實現方式的本質是設置屬性訪問器。在中也有類似的概念,不過不叫魔術方法,而是叫做訪問器。 緣起前幾天在看一些流行的迷你mvvm框架(比如avalon.js、 vue.js 這種較輕的框架,而非Angularjs、Emberjs這種較重的框架)的實現。現代流行的mvvm框架一般都會將數據雙向綁定(two-ways...
摘要:,而且每種框架雙向數據綁定的實現方式都不太一致,比如內部使用的是臟檢查,而內部實現方式的本質是設置屬性訪問器。在中也有類似的概念,不過不叫魔術方法,而是叫做訪問器。 緣起前幾天在看一些流行的迷你mvvm框架(比如avalon.js、 vue.js 這種較輕的框架,而非Angularjs、Emberjs這種較重的框架)的實現?,F代流行的mvvm框架一般都會將數據雙向綁定(two-ways...
摘要:當我們的視圖和數據任何一方發生變化的時候,我們希望能夠通知對方也更新,這就是所謂的數據雙向綁定。返回值返回傳入函數的對象,即第一個參數該方法重點是描述,對象里目前存在的屬性描述符有兩種主要形式數據描述符和存取描述符。 前言 談起當前前端最熱門的 js 框架,必少不了 Vue、React、Angular,對于大多數人來說,我們更多的是在使用框架,對于框架解決痛點背后使用的基本原理往往關注...
閱讀 2812·2019-08-30 15:55
閱讀 2858·2019-08-30 15:53
閱讀 2296·2019-08-26 13:47
閱讀 2558·2019-08-26 13:43
閱讀 3157·2019-08-26 13:33
閱讀 2805·2019-08-26 11:53
閱讀 1798·2019-08-23 18:35
閱讀 801·2019-08-23 17:16