Vue响应式系统的简单实现
一、什么是副作用函数和响应式数据?
副作用函数: 可以产生副作用的函数,那么什么是副作用呢?举个栗子:
let count = 1
function effect() {
count++
}
function fn() {
if (count == 1) {
console.log('执行1')
} else {
console.log('执行2')
}
}
effect()
fn()
当执行effect函数时会修改全局变量count的值,count值的修改就是effect函数执行带来的副作用,由于count变量被effect、fn函数所共享,effect函数修改了count值后会对fn函数产生影响,即effect函数的执行对fn函数产生了副作用。
响应式数据: 当数据发生变化时会触发相关副作用函数自动执行,我们将这样的数据称为响应式数据。比如下面的例子,一开始obj.title的值为 首页
,而后修改obj.title的值为详情页
,当修改了obj.title的值之后若effect函数自动执行,则称obj为响应式数据。
const obj = { title: '首页' }
function effect() {
document.body.innerText = obj.title
}
obj.title = '详情页'
二、响应式数据的简单实现
首先我们要清楚对象操作的基本语意:
- “读取” 操作又称之为
get
- “设置” 操作又称之为
set
我们可以通过Proxy
代理目标对象的读取和设置操作,读取和设置操作的语意比较宽泛,在这里只介绍比较简单的get
和set
,我们来看下面这个例子:
// 桶,用于存储代理对象和副作用函数之间的对应关系
const bucket = new Set()
// 用于存储临时的副作用函数
const activateEffect = null
const data = {
num: 1
}
function effect(fn) {
activateEffect = fn
fn()
}
function track() {
if (!activateEffect) {
return
}
bucket.add(activateEffect)
}
function trigger() {
bucket.forEach(fn => fn())
}
const proxy = new Proxy(data, {
get(target, key, receiver) {
track()
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
trigger
return Reflect.set(target, key, value, receiver)
}
})
effect(() => console.log(proxy.num))
proxy.num++
上述例子中实现了一个简单的响应式功能,当读取proxy代理对象的属性时,代理对象中的get方法会收集副作用函数,当设置proxy对象的属性时set方法中的trigger函数会触发副作用函数的执行。但例子中的响应式系统还存在缺陷,作者还会不断完善。