Vue2响应式原理
1.数据劫持
Vue官网原话: 你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。
// 数据劫持
const data = {
name: "",
message: ""
}
// 通过 keys 获取 data 多个键,实现对 data 多个键值变化的监听
Object.keys(data).forEach(key => {
let value = data[key]
// 该方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
Object.defineProperty(data, key, {
// 设定某属性的值的方法
set(newValue) {
value = newValue
console.log("监听到了变化" + newValue);
},
// 获取某个特定属性的值的方法
get() {
return value
}
})
})
2.设计模式
我刚开始理解这些观察者模式和订阅/发布者模式时也是非常的混乱,完全不懂在讲什么,在网上搜索了别人写的博客也才慢慢了解一点,推荐看一下这个博客,看完会对理解这两种模式有帮助。
另外推荐一下coderwhy老师的响应式原理视频
2.1观察者模式
参考了小浪努力学前端-手写一个简易vue响应式带你了解响应式原理-的文章
// 观察者模式
// 目标类
class Subject {
constructor() {
// 用来储存观察者的列表
this.observerLists = []
}
// 添加观察者
addObs(obs) {
// 判断是否存在观察者和观察者是否有 update 方法
if (obs && obs.update) {
this.observerLists.push(obs)
}
}
// 通知观察者
notify() {
this.observerLists.forEach(obs => {
obs.update()
})
}
// 清空观察者
clearObs() {
this.observerLists = []
}
}
// 观察者类
class Observer {
constructor(name) {
this.name = name
}
// 观察更新
update() {
console.log(this.name + "更新了");
}
}
// 实例化一个目标对象
const sub = new Subject()
// 实例化两个观察者对象
const obs1 = new Observer('张三')
const obs2 = new Observer('李四')
// 给目标对象添加观察者
sub.addObs(obs1)
sub.addObs(obs2)
// 通知观察者
sub.notify()
在控制台调用目标对象的 notify(),会对张三和李四进行通知:
1.将上边数据劫持代码改为如下:
Object.defineProperty(data, key, {
// 设定某属性的值的方法
set(newValue) {
value = newValue
console.log(`监听到了 ${key} 变化`);
// 数据劫持监听到 data 某个键值改变时,通知观察者更新
// ↓
sub.notify()
},
// 获取某个特定属性的值的方法
get() {
return value
}
})
2.控制台修改 data 键值时:
Vue实例执行的具体过程可以看一下coderwhy老师画的图解,图片需要结合老师的讲解更容易理解:
2.2订阅/发布者模式
class Event {
constructor() {
// 事件中心,即调度中心
this.subs = {}
}
$on(eventType, fn) {
// 参数为事件名称和回调函数
// 判断事件总线是否存在该事件
if(!this.subs[eventType]) {
this.subs[eventType] = []
}
// 把事件回调函数保存到事件名称列表中
this.subs[eventType].push(fn)
}
$emit(eventType, payload) {
// 参数为事件名称和有效负载
// 判断事件总线里是否有该事件
if(this.subs[eventType]) {
// 遍历该事件的回调函数,并执行
this.subs[eventType].forEach((fn) => {
fn(payload)
})
}
}
}
const e = new Event()
// 注册事件
e.$on('click', () => {
console.log("clike1");
})
e.$on('click', () => {
console.log("clike2");
})
e.$on('sum', (numlist) => {
const total = numlist.reduce((x, y) => x + y, 0)
console.log(total);
})
// e.$emit('click')
// click1
// click2
// e.$emit('sum', [1,2,3,4,5])
// 15