实现响应式系统--解决非响应属性更改造成的多余更新。

58 阅读2分钟

**开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 15 天,点击查看活动详情 响应式数据的基本实现

实现响应式系统--解决非响应属性更改造成的多余更新。

问题 改变obj中非响应式内容时候obj.other = 'hello vue'执行了副用用函数

obj.other = 'hello vue' 改变的时候,我们用到的只有text 所以text变化才更改,否则造成了过度渲染性能浪费,上一节中我们遇到了,非响应式数据改变结果 副用用函数也执行了。

如何实现

因为这个问题跟obj中的key有关,很容易联想到我们存的时候把响应式的key和副作用函数关联起来不就可以了吗 即

key: effectFunc 

最终数据格式即如下

{target1: { key: {function}} }}

所以在obj.text的时候我们拦截get方法,构建上面格式的数据结构

let keyMap = effectFuncList.get(target)
if (!keyMap) effectFuncList.set(target, (keyMap = new Map()))
let deps = keyMap.get(key)
if (!deps) keyMap.set(key, (deps = new Set()))
 // 收集依赖方法(副作用函数)
deps.add(activeEffect)

注意前两层是map最后方法的对象是set。set和map的区别是set中的值不重复且只存key不保存value的对象。

// html.js


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>响应式数据</title>
</head>

<body>

</body>
// 引入我们写的js代码
<script type="text/javascript" src="./index2.js"></script>
</html>

//index2.js


// 用来保存响应式函数
let activeEffect
function effect(fn) {
    activeEffect = fn
    fn()
}


//---------响应式数据相关------------
let effectFuncList = new WeakMap()
const data = { text: 'hello world' }
// vue3 用的Proxy
const obj = new Proxy(data, {
    get(target, key) {
        if (!activeEffect) return target[key]
        // 是不是根据key来保存就能够保证唯一呢。
        let keyMap = effectFuncList.get(target)
        if (!keyMap) effectFuncList.set(target, (keyMap = new Map()))
        let deps = keyMap.get(key)
        if (!deps) keyMap.set(key, (deps = new Set()))
        // 收集依赖方法(副作用函数)
        deps.add(activeEffect)
        return target[key]
    },
   set(target, key, newValue) {
        target[key] = newValue
        // 根据target获取以key为键的集合
        const keyMap = effectFuncList.get(target)
        if (!keyMap) return

        // 根据target获取以key为键的集合
        const effectsSet = keyMap.get(key)
        if (!effectsSet) return
        // 执行副作用函数
        effectsSet && effectsSet.forEach(fn => fn())
    }
})

//--------------------------



// 调用effect函数将,第一个参数即改变innerText的函数即副作用函数
effect(() => {
    console.warn('副用用函数执行====')
    document.body.innerText = obj.text
})


// 2秒后改变obj.other的值
setTimeout(() => {
    obj.other = 'hello vue'
}, 2000);


最终发现控制台 console.warn('副用用函数执行====')只打印了一次

扩展:

  1. 必须用Map来保存吗,可以用Object吗,Object和map有什么区别?

这里只能用map,因为例子里面有大量修改删除等操作,并且target是对象,对象作为key只能是map。对于二者区别可以参考

  1. 为什么target对象的时候用的是WeakMap可以用map吗两者有什么区别?

因为当obj对象销毁的时候,weakmap可以方便回收。

完成。

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 15 天,点击查看活动详情 响应式数据的基本实现