一. 数据驱动
数据响应式:数据改变,则视图改变,避免频繁的Dom操作,提高运行效率
双向绑定:数据改变,则视图改变;视图改变,则数据也随之改变
数据驱动:开发过程中只需关注数据本身,不用关心数据如何渲染出视图
二. Vue.js 数据响应式原理
2.1 Vue2数据响应式原理 - Object.defineProperty - 兼容IE9及以上
<html>
<head>
</head>
<body>
<h1>Vue响应式原理-Vue2 Object.defineProperty()</h1>
<div id="app"></div>
</body>
<script>
let data = {
msg: 'hello~'
}
let vm = {} // 模拟Vue实例
// 数据劫持:当访问或设置vm中成员时,做一些干预操作
Object.defineProperty(vm, 'msg', {
// 可枚举(可遍历)
enumerable: true,
// 可配置(可用delete删除,可通过defineProperty重新定义)
configurable: true,
// 获取数据时执行 访问器
get() {
console.log('get: ', data.msg)
return data.msg
},
// 当设置值时执行 设置器
set(newValue) {
console.log('set: ', newValue)
if (newValue === data.msg) {
return
}
data.msg = newValue
document.querySelector('#app').textContent = data.msg
}
})
// 测试
vm.msg = 'Hello~ World~'
setTimeout(() => {
console.log(vm.msg)
}, 12000)
</script>
</html>
2.2 Vue3数据响应式原理 - new Proxy() - ES6中新增 IE不支持
new Proxy()相比于Object.defineProperty()是直接监听对象,而非属性,性能优于Object.defineProperty()。
<html>
<head>
</head>
<body>
<h1>Vue响应式原理-Vue3 new Proxy()</h1>
<div id="app"></div>
</body>
<script>
let data = {
msg: 'hello',
count: 0
}
// 模拟Vue实例
let vm = new Proxy(data, {
// 执行代理行为的函数
// 当访问vm的成员会执行
get(target, key) {
console.log('get, key: ', key, target[key])
return target[key]
},
// 当设置vm的成员会执行
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)
</script>
</html>
三. 发布订阅模式和观察者模式
3.1 发布订阅模式-有事件中心
例1 Vue的自定义事件:
let vm = new Vue()
vm.$on('dataChange', () => { // 事件订阅
console.log('dataChange')
})
vm.$on('dataChange', () => { // 事件订阅
console.log('dataChange1')
})
vm.$emit('dataChange') // 事件发布
例2 Vue的兄弟组件通信:
// eventBus.js
// 事件中心
let eventHub = new Vue()
// ComponentA.vue 发布者
addTodo: () => {
eventHub.$emit('add-todo', { text: this.newTodoText })
this.newTodoText = ''
}
// ComponentB.vue 订阅者
created: () => {
eventHub.$on('add-todo', this.addTodo)
}
例3 模拟发布订阅模式:
// 模拟发布订阅模式-事件触发器
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)
}
// 发布事件的方法
$emit (eventType) {
if(this.subs[eventType]) {
const handlers = this.subs[eventType]
handlers.forEach(handler => {
handler()
});
}
}
}
// 测试
const em = new EventEmitter()
em.$on('change', () => {
console.log('change01')
})
em.$on('change', () => {
console.log('change02')
})
setTimeout(() => {
em.$emit('change')
em.$emit('click')
}, 2000)
3.2 观察者模式
发布者知道有哪些订阅者,每个订阅者提供一个update方法,当事件触发时,发布者将调用每个订阅者的update方法。
和发布订阅模式相比,观察者模式无事件中心,所以观察者模式的发布者和观察者之间存在依赖。
而发布订阅模式有事件中心,事件中心记录了哪些事件有哪些订阅者,解除了发布者和订阅者的依赖。
例1: 模拟观察者模式
// 发布者(依赖)
class Dep {
constructor() {
// 记录所有订阅者
this.subs = []; // subscriber
}
// 添加订阅者
addSub(sub) {
if(sub && sub.update) {
this.subs.push(sub)
}
}
// 发布通知
notify() {
this.subs.forEach(sub => {
sub.update()
})
}
}
// 订阅者(观察者)
class Wather {
constructor(name) {
this.name = name
}
update() {
console.log(`${this.name} got update message and do something!`)
}
}
// 测试
const dep = new Dep()
const wather1 = new Wather('wather1')
dep.addSub(wather1)
const wather2 = new Wather('wather2')
dep.addSub(wather2)
dep.notify()
本文 完。