Vue3.js“非原始值”响应式实现基本原理笔记(四)浅响应和深响应、只读和浅只读

如果您觉得这篇文章有帮助的话!给个点赞和评论支持下吧,感谢~

作者:前端小王hs

阿里云社区博客专家/清华大学出版社签约作者/csdn百万访问前端博主/B站千粉前端up主

此篇文章是博主于2022年学习《Vue.js设计与实现》时的笔记整理而来

书籍:《Vue.js设计与实现》 作者:霍春阳

本篇博文将在书第5.1节5.4节的基础上进一步总结所提到的基础概念,附加了测试的代码运行示例,方便正在学习Vue3想分析Vue3源码的朋友快速阅读

如有帮助,不胜荣幸

前文:
Vue3.js“非原始值”响应式实现基本原理笔记(一)
Vue3.js“非原始值”响应式实现基本原理笔记(二)
Vue3.js“非原始值”响应式实现基本原理笔记(三)

浅响应与深响应

假设传入一个嵌套着对象的对象

  • 浅响应:只有表层对象具有响应式
  • 深响应:递归地将对象的所有嵌套属性都转换为响应式的

Vue3.js“非原始值”响应式实现基本原理笔记(三)中第一次出现了reactive,其实就是对proxy对象的一个封装函数,现在来看一下这样做出现的问题以及继续完善的方法

其实读到这里,可以发现书的走向就是不断的提出问题并完善响应式的一个过程,对于响应式的问题,就是不能触发副作用函数

我们先来看如何实现深响应

来看书中的代码:

const obj = reactive({  
  foo: {  
    bar: 1  
  }  
});  
  
effect(() => {  
  console.log(obj.foo.bar);  
});  
  
obj.foo.bar = 2; // 修改值不会触发响应

来看一下effect()中的执行流程:

  1. 因为不是lazy,直接执行effectFn,进而执行匿名函数
  2. 读取obj.foo,调用trackobj.fooeffectFn进行关联
  3. 执行return Reflect.get(target, key, receiver)返回{bar:1}

所以整个过程bareffect不会有关系,那么也就不会触发响应了

如果需要深响应,只需将返回的{bar:1}再丢进reactive里即可,这里就需要做一个判断,Reflect.get返回的得是一个对象且不为null,最终代码如下:

function reactive(obj) {
    return new Proxy(obj, {
        get(target, key, receiver) {
            if (key === 'raw') {
                return target;
            }
            track(target, key);
            const res = Reflect.get(target, key, receiver);
            if (typeof res === 'object' && res !== null) {
                return reactive(res);
            }
            return res;
        }
        // 省略其他拦截函数
    });  
}

这样,深响应就实现了


但是有时候我们不需要所有的嵌套对象都实现响应,那么就催生了浅响应,也就是shallowReactive,shallow是浅的意思

实现的过程也非常简单,只需添加一个形参,用于告知函数是否需要进行深响应,反之则浅响应,代码如下:

// 默认为 false,即非浅响应  
function createReactive(obj, isShallow = false) {
    return new Proxy(obj, {
        get(target, key, receiver) {
            if (key === 'raw') {
                return target;
            }
            const res = Reflect.get(target, key, receiver);
            if (isShallow) {
                return res;
            }
            track(target, key);  
            if (typeof res === 'object' && res !== null) {  
                return reactive(res);
            }
            return res;
        }
        // 省略其他拦截函数
    });
}

最后,在createReactive外套上reactiveshallowReactive,就是我们在文档中看到的API了:

function reactive(obj) {
    return createReactive(obj);
}

function shallowReactive(obj) {
    return createReactive(obj, true);
}

只读和浅只读

只读的逻辑与浅响应相同,都是在新增形参在函数中进行判定,逻辑非常简单,代码如下:

// 增加第三个参数 isReadonly,代表是否只读,默认为 false,即非只读
function createReactive(obj, isShallow = false, isReadonly = false) {
    return new Proxy(obj, {
        // 拦截设置操作
        set(target, key, newVal, receiver) {
            // 如果是只读的,则打印警告信息并返回
            if (isReadonly) {
                console.warn(`属性 ${key} 是只读的`);
                return true;
            }
            // 省略其他
            return res;
        },
        deleteProperty(target, key) {
            // 如果是只读的,则打印警告信息并返回  
            if (isReadonly) {  
                console.warn(`属性 ${key} 是只读的`);  
                return true;  
            }  
            // 省略其他
            return res;  
        }  
        // 省略其他拦截函数  
    });
}

可以看到,如果是只读的,也就是isReadonlytrue,那么不管是修改还是删除,都会弹出警告

同理,由于是只读的,所以只读的对象也没有必要触发effect,代码如下:

get(target, key, receiver) {
  if (!isReadonly) {
    track(target, key)
  }
// 省略其他
}

readonly如下所示:

function readonly(obj) {  
    return createReactive(obj, false, true /* 只读 */);  
}

当然,现在只是浅只读,如果是const obj = readonly({ foo: { bar: 1 } }),在读取obj.foo.bar时仍然可以修改

因为Reflect.get(target, key, receiver)返回的对象又执行了reactive(res),所以还需进行一个判定,就是如果为只读,就进行再次调用readonly,而不是调用reactive,代码如下:

if (typeof res === 'object' && res !== null) {  
    // 如果数据为只读,则调用 readonly 对值进行包装  
    return isReadonly ? readonly(res) : reactive(res);  
}

最后,在createReactive外套上readonlyshallowReadonly,就是我们在文档中看到的API了:

function readonly(obj) {
    return createReactive(obj, false, true);
}

function shallowReadonly(obj) {
    return createReactive(obj, true, true);
}

总结

  1. 如何实现深响应和浅响应
  2. 如何实现只读和浅只读

相关推荐

  1. vue响应

    2024-07-14 12:48:07       50 阅读
  2. vue2vue 3响应原理

    2024-07-14 12:48:07       26 阅读
  3. Vue 3响应对象: refreactive

    2024-07-14 12:48:07       35 阅读
  4. vue响应原理

    2024-07-14 12:48:07       56 阅读
  5. vue2响应vue3响应

    2024-07-14 12:48:07       22 阅读
  6. Vue3响应原理

    2024-07-14 12:48:07       61 阅读
  7. Vue 3 中的响应原理

    2024-07-14 12:48:07       59 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-14 12:48:07       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-14 12:48:07       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-14 12:48:07       58 阅读
  4. Python语言-面向对象

    2024-07-14 12:48:07       69 阅读

热门阅读

  1. 【LLMs】大语言模型分类

    2024-07-14 12:48:07       25 阅读
  2. 北京工业大学学报

    2024-07-14 12:48:07       25 阅读
  3. FFmpeg学习(五)-- libswresample使用说明及函数介绍

    2024-07-14 12:48:07       23 阅读
  4. 【大学生前端期末作业】顶级茶叶网页

    2024-07-14 12:48:07       16 阅读
  5. Spring @Scheduled学习

    2024-07-14 12:48:07       16 阅读
  6. Xcode自动化测试全景:释放你的应用质量潜能

    2024-07-14 12:48:07       21 阅读
  7. 常用软件的docker compose安装

    2024-07-14 12:48:07       27 阅读
  8. Dubbo 的集群容错机制

    2024-07-14 12:48:07       19 阅读
  9. c++【入门】捡石头

    2024-07-14 12:48:07       20 阅读
  10. 详解 QAxObject

    2024-07-14 12:48:07       12 阅读
  11. windos安装qemu启动树莓派2b连接网卡和openssh-server

    2024-07-14 12:48:07       19 阅读