痛定思痛,总要静一静,用抹布抹一抹陈旧模糊的记忆。
虽然截止至2023年12月31日,vue2.x版本已停止维护,但是vue2中的很多东西还是值得去思考的。
最经典的面试题莫过于:简述下Vue2的响应式原理~🤦♂️
what ?
什么是变化侦测?
说白话,就是界面视图发生改变时,需要有个人告诉我视图变了。
例:元宵节夜晚,当你一路向北行进时,背后方向忽然放起了烟花秀。迎面而来的一位美女,告诉了你。你转身回望赞叹了一句:"好漂亮~"
变化的事件:烟花秀
侦测者:美女
How?
如何追踪变化?
那么绕不开的一个方法必然就是 Object.defineproperty() ,建议通读其对象属性以便加深理解与感悟。
通过对其封装,得到如下函数,其作用是定义一个响应式数据。即在其中进行变化追踪,封装的目的在于仅仅传递入参即可。
js
function defineReactive(data,key,val){ // 封装函数
Object.defineProperty(data,key,{
enumerable:true,
configurable:true,
get:function(){ // 获取(读)数据的值
return val;
},
set:function(newVal){ // 设置数据的值
if(val === newVal){
return;
}
val = newVal;
}
})
}
关注点:
- 每当从data的key中读取数据是,get函数被触发
- 每当往data的key中设置数据时,set函数被触发
封装的目的:收集依赖
🤕 依赖是啥?---> 变化的信息(比如:之前提到的烟花秀)
一句话总结:先收集依赖,在getter中收集依赖,在setter中触发依赖
ps【通读 **Object.defineproperty()**后很好理解】:
getter---> get()函数对应的属性
setter---> set()函数对应的属性
存放依赖
⛏ 为啥存?---> 计算机处理数据,在输入后,需要存储才能进行后续操作。
这里定义了一个Dep类来收集、删除依赖或向依赖发送通知。
有时候,为了让自己开心,Dep可以是任何我喜欢的名字(比如:BBQ🤪)。
js
export default class Dep {
constructor() {
this.subs = []
}
addSub(sub) {
this.subs.push(sub)
}
removeSub(sub) {
remove(this.subs, sub)
}
depend() {
if(window.target) {
this.addSub(windwo.target)
}
}
notify() {
const subs = this.subs.slice()
for(let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
function remove (arr, item) {
if (arr.length) {
const index = arr.indexOf(item)
if(index > -1) {
return arr.splice(index, 1)
}
}
}
既然有地方存依赖了,那么响应式函数就可以改造了
js
function defineReactive (data, key, val) {
let dep = new Dep() // 修改
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function () {
dep.depend() // 修改
return val
},
set: function () {
if(val === newVal) {
return
}
val = newVal
dep.notify() // 修改
}
})
}
什么是watcher?
Watcher是一个中介的角色,数据变化时通知它,他负责通知其他地方。(比如:之前例子中的美女😁)
美女告诉了我背后有烟花秀,所以我才知道,并发出了赞美。
想要侦测所有
美女告诉我有烟花秀,仅仅是一件事。如果想知道更多的事应该怎么办?
答:住院
你一旦有什么事,护士会立马通知你~🤣
此时,则需要一个新的类 Observer,来实现将一个正常的Object转换成被侦测的Object。
解决方案是递归实现,此处不再展开。
关于Object的问题
如上所述,递归将一个正常的Object转换成被侦测的Object。然而,仅仅涵盖了Object本身已有的属性。
故无法追踪新增属性和删除属性
总结
🤕🤕🤕 欢迎讨论交流~