1. 响应式原理及实现
本文github地址:JavaScript_Interview_Everything大前端知识体系与面试宝典,从前端到后端,全栈工程师,六边形战士
1.1. 何为响应式?
如果我们有一个对象:
const obj = {
name: 'flten',
age: 16,
}
有一些函数使用了这个对象中的数据:
function fn1(){
console.log(obj.name)
}
function fn2(){
console.log(obj.name)
}
如何在obj对象的属性name
发生改变时,让fn1和fn2函数自动重新触发更新数据呢?
能够在数据更新时通知到所有使用到它的函数,使其重新触发,这就是响应式。
1.2. 如何区分需要响应式和非响应式
但是有些函数是使用了响应式的数据,而有些则没有,那我们如何判断呢?最简单的方式就是把所有使用到了某个数据的所有函数都保存在一个数组中,数据发生改变时直接全部遍历函数重新执行就可以了。
// 01.js
const reactiveArr = [];
const obj = {
name: 'flten',
age: 16,
}
function fn1(){
console.log(obj.name)
}
reactiveArr.push(fn1)
function fn2(){
console.log(obj.name)
}
reactiveArr.push(fn2)
reactiveArr.forEach(fn => {
fn()
})
等一下,难道每次我们都要手动push
吗?重复的操作当然是可以封装在一个函数里了
//02.js
const reactiveArr = [];
function addReactive(fn){
reactiveArr.push(fn)
}
const obj = {
name: 'flten',
age: 16,
}
function fn1(){
console.log(obj.name)
}
addReactive(fn1)
function fn2(){
console.log(obj.name)
}
addReactive(fn2)
reactiveArr.forEach(fn=>{
fn()
})
看起来还不错,但是这里我们只是收集了使用了name
属性的函数,如果我们还要收集age
属性的函数呢?这时我们就需要另外一个数组和另外一个将函数添加到数组中的方法
//03.js
const reactiveNameArr = [];
const reactiveAgeArr = [];
function addNameReactive(fn){
reactiveNameArr.push(fn)
}
function addAgeReactive(fn){
reactiveAgeArr.push(fn)
}
const obj = {
name: 'flten',
age: 16,
}
// name 属性的使用收集
function fn1(){
console.log(obj.name)
}
addNameReactive(fn1)
function fn2(){
console.log(obj.name)
}
addNameReactive(fn2)
reactiveNameArr.forEach(fn=>{
fn()
})
// age属性的使用收集
function fn3(){
console.log(obj.age)
}
addAgeReactive(fn3)
function fn4(){
console.log(obj.age)
}
addAgeReactive(fn4)
reactiveAgeArr.forEach(fn=>{
fn()
})
但是这样太麻烦了,每一个数据都要另外新建一个数组和添加函数吗?什么方式可以封装这些重复操作呢?它可以自动生成一个数组和对应的函数操作呢?当然是类了,我们可以通过定义一个类,为每个响应式数据都实例化一个对象,这样就把这些操作封装起来不需要每次都手动重建了。
1.3. 依赖收集
我们将上面的操作封装为一个类:
//04.js
class Depend{
constructor(){
this.reactiveArr = []
}
addDepend(fn){
this.reactiveArr.push(fn)
}
notify(){
this.reactiveArr.forEach(fn=>fn())
}
}
const obj = {
name: 'flten',
age: 16,
}
const dependName = new Depend();
const dependAge = new Depend();
// name 属性的使用收集
function fn1(){
console.log(obj.name)
}
dependName.