數(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ù)也隨之改變
-
數(shù)據(jù)驅(qū)動(dòng)
Vue 最獨(dú)特的特性之一,開發(fā)過程中僅需要關(guān)注數(shù)據(jù)本身,不需要關(guān)心數(shù)據(jù)是如何渲染到視圖
數(shù)據(jù)響應(yīng)式核心原理
-
使用Object.defineProperty(),將data中的property轉(zhuǎn)為getter/setter,從而實(shí)現(xiàn)響應(yīng)式
// 模擬 Vue 中的 data 選項(xiàng) let data = { msg: 'hello' } // 模擬 Vue 的實(shí)例 let vm = {} // 數(shù)據(jù)劫持:當(dāng)訪問或者設(shè)置vm中的成員時(shí),做一些干預(yù)操作 Object.defineProperty(vm, 'msg', { // 可枚舉 enumrable: true, // 可配置(可以使用 delete 刪除,可以通過 defineProperty 重新定義) configurable: true, // getter get () { console.log('get:', data.msg) return data.msg } // setter 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)
如果一個(gè)對象中有多個(gè)屬性需要設(shè)置為響應(yīng)式時(shí)
// 遍歷 data 對象的所有屬性
Object.keys(data).forEach(key => {
// 把 data 中的屬性,轉(zhuǎn)換成 vm 的 setter/setter
Object.defineProperty(vm, key, {
enumerable: true,
configurable: true,
get () {
console.log('get: ', key, data[key])
return data[key]
},
set (newValue) {
console.log('set: ', key, newValue)
if (newValue === data[key]) {
return
}
data[key] = newValue
// 數(shù)據(jù)更改,更新 DOM 的值
document.querySelector('#app').textContent = data[key]
}
})
})
-
Vue3.0
使用Proxy實(shí)現(xiàn),直接監(jiān)聽對象,而不再是屬性,弊端是IE不支持
// 模擬 Vue 中的 data 選項(xiàng) let data = { msg: 'hello', count: 0 } let vm = new Proxy(data, { // 當(dāng)訪問vm的成員會(huì)執(zhí)行 get (target, key) { console.log('get:', key , target[key]) return target[key] }, // 設(shè)置vm成員時(shí)執(zhí)行 set (target, key, newValue) { console.log('set:', key, newValue) if (target[key] === newValue) return target[key] = newValue document.querySelector('#app').textContent = newValue } }) // 測試 vm.msg = 'Hello World' console.log(vm.msg)
發(fā)布訂閱模式和觀察者模式
-
發(fā)布訂閱模式
我們假定,存在一個(gè)"信號中心",某個(gè)任務(wù)執(zhí)行完成,就向信號中心"發(fā)布"(publish)一個(gè)信 號,其他任務(wù)可以向信號中心"訂閱"(subscribe)這個(gè)信號,從而知道什么時(shí)候自己可以開始執(zhí) 行。這就叫做"發(fā)布/訂閱模式"(publish-subscribe pattern)
-
this.$emit('myEvent') this.$on('myEvent', () => { console.log('myEvent') })
模擬實(shí)現(xiàn)vue自定義事件
// 事件觸發(fā)器 class EventEmitter { constructor () { // { 'click': [fn1, fn2], 'change': [fn] } this.subs = Object.create(null) } // 注冊事件 $on (eventType, handler) { this.subs[eventType] = this.subs[eventType] || [] this.subs[eventType].push(handler) } // 觸發(fā)事件 $emit (eventType) { if (this.subs[eventType]) { this.subs[eventType].forEach(handler => { handler() }) } } } // 測試 let em = new EventEmitter() em.$on('click', () => { console.log('click1') }) em.$on('click', () => { console.log('click2') }) em.$emit('click')
-
兄弟組件通信過程
// 事件中心 eventBus.js let bus = new vue() // 組件A 發(fā)布者 fn1 () { bus.$emit('add-todo', { text: this.newTodoText }) } // 組件B 訂閱者 created () { bus.$on('add-todo', this.addTodo) }
-
觀察者模式
有觀察者(訂閱者)——Watcher和目標(biāo)(發(fā)布者)——Dep組成,當(dāng)事件發(fā)生時(shí),通過notify通知所有觀察者,觀察者模式?jīng)]有事件中心
// 目標(biāo)(發(fā)布者)
class Dep {
constructor() {
// 存儲所有觀察者
this.subs = []
}
// 添加觀察者
addSub(sub) {
if (sub && sub.update) {
this.subs.push(sub)
}
}
// 通知所有觀察者
notify() {
this.subs.forEach((sub) => {
sub.update()
})
}
}
// 觀察者(訂閱者)
class Watcher {
update() {
console.log('update')
}
}
// 測試
let dep = new Dep()
let watcher = new Watcher()
dep.addSub(watcher)
dep.notify()
-
總結(jié)
觀察者模式是由具體目標(biāo)調(diào)度,比如當(dāng)事件觸發(fā),Dep 就會(huì)去調(diào)用觀察者的方法,所以觀察者模式的訂閱者與發(fā)布者之間是存在依賴的。
發(fā)布/訂閱模式由統(tǒng)一調(diào)度中心調(diào)用,因此發(fā)布者和訂閱者不需要知道對方的存在。