往期内容:
《Vue进阶教程》第六课:computed()函数详解(上)
《Vue进阶教程》第七课:computed()函数详解(下)
经过前面的努力, 我们初步了解最基本的响应式原理, 并且实现了一个Demo
接下来, 我们要一步步完善响应式系统~😀
1 实现一对多
所谓一对多
: 一个属性对应多个副作用函数
🤔思考
如果一个属性存在多个与之对应的副作用函数
理论上当属性改变时, 属性关联的每一个副作用函数都应该重新执行
1) 手动方式
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">xiaopang</div>
<script>
function isObject(value) {
return typeof value === 'object' && value !== null
}
/**
* 创建响应式数据
* @param [object]: 普通对象
* @return [Proxy]: 代理对象
*/
function reactive(data) {
if (!isObject(data)) return
return new Proxy(data, {
get(target, key) {
return target[key]
},
set(target, key, value) {
target[key] = value
effect() // 调用副作用函数, 调用effect函数会重新获取新的数据
effect1()
return true
},
})
}
// 定义一个响应式数据 : 触发者
const state = reactive({ name: 'xiaopang' })
// 定义一个副作用函数 : 响应者
function effect() {
console.log('effect被执行了...')
app.innerHTML = state.name
}
function effect1() {
console.log('effect1被执行了...')
state.name
}
// 当state.name改变时, 重新执行对应副作用函数effect
setTimeout(() => {
state.name = 'xxp'
}, 1000)
</script>
</body>
</html>
很显然, 我们可以通过手动的方式, 依次执行每一个关联的副作用函数.
2) 自动方式
我们可以创建一个存储所有副作用函数的空间, 这个空间叫做副作用桶
首先想到的是数组, 数组的每个元素都是一个副作用函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">xiaopang</div>
<script>
// 定义一个副作用函数桶, 存放所有的副作用函数. 每个元素都是一个副作用函数
const bucket = [] // 新增
function isObject(value) {
return typeof value === 'object' && value !== null
}
/**
* 创建响应式数据
* @param [object]: 普通对象
* @return [Proxy]: 代理对象
*/
function reactive(data) {
if (!isObject(data)) return
return new Proxy(data, {
get(target, key) {
return target[key]
},
set(target, key, value) {
target[key] = value
// 从副作用函数桶中依次取出每一个元素(副作用函数)执行
bucket.forEach((fn) => fn()) // 修改
return true
},
})
}
// 定义一个响应式数据 : 触发者
const state = reactive({ name: 'xiaopang' })
// 定义一个副作用函数 : 响应者
function effect() {
console.log('effect被执行了...')
app.innerHTML = state.name
}
bucket.push(effect)
bucket.push(effect)
function effect1() {
console.log('effect1被执行了...')
state.name
}
bucket.push(effect1)
// 当state.name改变时, 重新执行对应副作用函数effect
setTimeout(() => {
state.name = 'xxp'
}, 1000)
</script>
</body>
</html>
但是数组元素是也可重复的, 为了效率, 我们还要去重, 而Set
集合是默认去重的
因此, 考虑用Set
实现, Set集合中存放的每个元素也是副作用函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">xiaopang</div>
<script>
// 定义一个副作用函数桶, 存放所有的副作用函数. 每个元素都是一个副作用函数
const bucket = new Set() // 修改
function isObject(value) {
return typeof value === 'object' && value !== null
}
/**
* 创建响应式数据
* @param [object]: 普通对象
* @return [Proxy]: 代理对象
*/
function reactive(data) {
if (!isObject(data)) return
return new Proxy(data, {
get(target, key) {
return target[key]
},
set(target, key, value) {
target[key] = value
// 从副作用函数桶中依次取出每一个元素(副作用函数)执行
bucket.forEach((fn) => fn())
return true
},
})
}
// 定义一个响应式数据 : 触发者
const state = reactive({ name: 'xiaopang' })
// 定义一个副作用函数 : 响应者
function effect() {
console.log('effect被执行了...')
app.innerHTML = state.name
}
bucket.add(effect)
function effect1() {
console.log('effect1被执行了...')
state.name
}
bucket.add(effect1)
const effect2 = () => {
console.log(state.name)
}
bucket.add(effect2)
// 当state.name改变时, 重新执行对应副作用函数effect
setTimeout(() => {
state.name = 'xxp'
}, 1000)
</script>
</body>
</html>