數(shù)據(jù)驅(qū)動(dòng)
- 數(shù)據(jù)響應(yīng)式、雙向綁定、數(shù)據(jù)驅(qū)動(dòng)
- 數(shù)據(jù)響應(yīng)式
數(shù)據(jù)模型僅僅是普通的 JavaScript 對象,而當(dāng)我們修改數(shù)據(jù)時(shí),視圖會(huì)進(jìn)行更新,避免了繁瑣的 DOM 操作,提高開發(fā)效率 - 雙向綁定
數(shù)據(jù)改變,視圖改變;視圖改變,數(shù)據(jù)也隨之改變
我們可以使用 v-model 在表單元素上創(chuàng)建雙向數(shù)據(jù)綁定 - 數(shù)據(jù)驅(qū)動(dòng)是 Vue 最獨(dú)特的特性之一
開發(fā)過程中僅需要關(guān)注數(shù)據(jù)本身,不需要關(guān)心數(shù)據(jù)是如何渲染到視圖
數(shù)據(jù)響應(yīng)式的核心原理
Vue 2.x
- Object.defineProperty
- 瀏覽器兼容 IE8 以上(不兼容 IE8)
// 模擬 Vue 中的 data 選項(xiàng)
let data = { msg: 'hello' }
// 模擬 Vue 的實(shí)例
let vm = {}
// 數(shù)據(jù)劫持:當(dāng)訪問或者設(shè)置 vm 中的成員的時(shí)候,做一些干預(yù)操作
Object.defineProperty(vm, 'msg', {
// 可枚舉(可遍歷)
enumerable: true,
// 可配置(可以使用 delete 刪除,可以通過 defineProperty 重新定義)
configurable: true,
// 當(dāng)獲取值的時(shí)候執(zhí)行
get () {
console.log('get: ', data.msg)
return data.msg
},
// 當(dāng)設(shè)置值的時(shí)候執(zhí)行
set (newValue) {
console.log('set: ', newValue)
if (newValue === data.msg) {
return
}
data.msg = newValue
// 數(shù)據(jù)更改,更新 DOM 的值
document.querySelector('#app').textContent = data.msg
}
})
// 測試 vm.msg = 'Hello World'
console.log(vm.msg)
Vue 3.x
- Proxy
- 直接監(jiān)聽對象,而非屬性。
- ES 6中新增,IE 不支持,性能由瀏覽器優(yōu)化
// 模擬 Vue 中的 data 選項(xiàng)
let data = { msg: 'hello', count: 0 }
// 模擬 Vue 實(shí)例
let vm = new Proxy(data, {
// 當(dāng)訪問 vm 的成員會(huì)執(zhí)行
get (target, key) {
console.log('get, key: ', key, target[key])
return target[key]
},
// 當(dāng)設(shè)置 vm 的成員會(huì)執(zhí)行
set (target, key, newValue) {
console.log('set, key: ', key, newValue)
if (target[key] === newValue) {
return
}
target[key] = newValue
document.querySelector('#app').textContent = target[key]
}
})
// 測試
vm.msg = 'Hello World'
console.log(vm.msg)
發(fā)布訂閱模式和觀察者模式
發(fā)布/訂閱模式
- 訂閱者
- 發(fā)布者
- 信號(hào)中心
我們假定,存在一個(gè)"信號(hào)中心",某個(gè)任務(wù)執(zhí)行完成,就向信號(hào)中心"發(fā)布"(publish)一個(gè)信號(hào),其他任務(wù)可以向信號(hào)中心"訂閱"(subscribe)這個(gè)信號(hào),從而知道什么時(shí)候自己可以開始執(zhí)行。這就叫做"發(fā)布/訂閱模式"(publish-subscribe pattern)
觀察者模式
- 觀察者(訂閱者) -- Watcher
update():當(dāng)事件發(fā)生時(shí),具體要做的事情 - 目標(biāo)(發(fā)布者) -- Dep
subs 數(shù)組:存儲(chǔ)所有的觀察者
addSub():添加觀察者
notify():當(dāng)事件發(fā)生,調(diào)用所有觀察者的 update() 方法 - 沒有事件中心
總結(jié):
觀察者模式是由具體目標(biāo)調(diào)度,比如當(dāng)事件觸發(fā),Dep 就會(huì)去調(diào)用觀察者的方法,所以觀察者模式的訂閱者與發(fā)布者之間是存在依賴的。
發(fā)布/訂閱模式由統(tǒng)一調(diào)度中心調(diào)用,因此發(fā)布者和訂閱者不需要知道對方的存在。
Vue 響應(yīng)式原理模擬
- 整體分析
- Vue
把 data 中的成員注入到 Vue 實(shí)例,并且把 data 中的成員轉(zhuǎn)成 getter/setter - Observer
能夠?qū)?shù)據(jù)對象的所有屬性進(jìn)行監(jiān)聽,如有變動(dòng)可拿到最新值并通知 Dep - Compiler
解析每個(gè)元素中的指令/插值表達(dá)式,并替換成相應(yīng)的數(shù)據(jù) - Dep
添加觀察者(watcher),當(dāng)數(shù)據(jù)變化通知所有觀察者 -
Watcher
數(shù)據(jù)變化更新視圖
Vue
負(fù)責(zé)接收初始化的參數(shù)(選項(xiàng))
負(fù)責(zé)把 data 中的屬性注入到 Vue 實(shí)例,轉(zhuǎn)換成 getter/setter
負(fù)責(zé)調(diào)用 observer 監(jiān)聽 data 中所有屬性的變化
負(fù)責(zé)調(diào)用 compiler 解析指令/插值表達(dá)式Observer
負(fù)責(zé)把 data 選項(xiàng)中的屬性轉(zhuǎn)換成響應(yīng)式數(shù)據(jù)
data 中的某個(gè)屬性也是對象,把該屬性轉(zhuǎn)換成響應(yīng)式數(shù)據(jù)
數(shù)據(jù)變化發(fā)送通知Compiler
負(fù)責(zé)編譯模板,解析指令/插值表達(dá)式
負(fù)責(zé)頁面的首次渲染
當(dāng)數(shù)據(jù)變化后重新渲染視圖-
Dep(Dependency)
收集依賴,添加觀察者(watcher)
通知所有觀察者
image.png -
Watcher
當(dāng)數(shù)據(jù)變化觸發(fā)依賴,dep 通知所有的 Watcher 實(shí)例更新視圖
自身實(shí)例化的時(shí)候往 dep 對象中添加自己
7.總結(jié)

- 給屬性重新賦值成對象,是否是響應(yīng)式的?
是 - 給 Vue 實(shí)例新增一個(gè)成員是否是響應(yīng)式的?
否
檢測變化的注意事項(xiàng)


