课程地址:【已完结】全网最详细Vue3源码解析!(一行行带你手写Vue3源码)
第二部分-实现响应式(2):(对应课程的第6-9节)
第6节:《实现proxy代理以及解决重复代理的问题》
1、在入口文件中暴露4个响应式API接口:
2、既然是从当前文件夹下的reactive中引入的这4个接口并暴露,那么接下来在当前文件夹下新建reactive.ts,并在其中编写代码:
定义并暴露了这4个方法,这4个方法都接收一个target入参,然后在函数内部又返回了一个createReactiveObj函数,此函数是实现响应式的核心方法,它接收三个参数:target(传入的参数)、isReadonly(是否只读)、baseHanlers(实现响应式的配置对象)
3、接下来编写createReactiveObj函数,这个方法是实现响应式的核心方法,首先需要判断数据是否对象类型,先在shared文件夹下定义一个公共方法 isObject:
4、由于在shared里面添加了公共方法,需要运行npm run build 进行打包才能将shared下的代码也进行打包(因为我们前面配置的npm run dev 只会对reactivity下的内容进行打包):
5、打包完成之后会发现packages/shared下已经出现了打包生成的文件,其中已经有了我们刚刚写的isObject方法:
6、打包之后运行一下 yarn install,重新安装一下,然后会发现 node_modules/@vue/shared 下会出现刚刚打包的 isObject 相关代码:
小总结:在shared里新增公共方法后,需要运行 npm run build 进行打包,将代码打包到packages下,然后需要运行 yarn install 重新安装,将代码同步到node_modules/@vue/shared下,之后才可以引用其中的方法。
7、引入isObject公共方法:
8、处理一个问题:假如一个已经被reactive处理过的数据,再次传递给了reactive,此时需要判断一下,如果这已经是一个被reactive处理过的数据,则直接返回处理后的数据,无需再次重复处理。实现方式为:用一个 weakMap 数据结构将reactive处理过的数据记录下来,在处理之前,先来判断一下这个存储被处理过的数据的结构中是否有这个数据,有则直接返回,无则进行处理并记录到weakMap数据集中。
第7节:《实现代理proxy中的get》
1、将4个响应式API对应的hander移动到一个单独的文件 baseHandlers.ts 中,并在reactive.ts中导入和使用:
2、在 baseHandlers.ts文件中定义并导出这4个handlers对象,由于4个handler都会包含一个get方法和一个set方法,所以仍然采用柯里化的形式,用一个createGetter函数,来统一处理相同的逻辑,通过传入不同的参数控制不同的逻辑:
3、编写createGetter公共方法:这个方法返回一个get函数,其参数为target(传递给4个响应式API的需要处理的对象数据)、key(读取的对象属性)以及receiver。在这个函数中,首先通过Reflect取到目标对象的属性值,然后根据不同参数进行判断:如果不是只读,那么通过effect进行收集依赖,相当于vue2中的watcher;如果是浅的,则直接返回属性值,因为Proxy默认只会对目标对象的第一层实现代理,并不会自动去处理嵌套的更深层级。最后将属性值返回,代码如下:
补充:Proxy的handler对象中get函数接收到的第三个参数receiver,经测试就是生成的Proxy对象:
4、通过Proxy对对象实现代理,默认只会对对象第一层进行代理;对比vue2,初始时如果遇到对象属性值又是对象,则会跑递归,给所有嵌套属性都实现watcher,即数据的“监听”
5、处理对象的属性值又是一个对象的情况:判断其属性值是对象类型,则递归调用响应式方法,如果是只读的,则继续调用readonly;如果不是只读,则继续调用reactive。
注意:这里的面试点:懒代理。这里不像vue2初始时就直接遍历跑递归,将对象的所有嵌套属性全部层层递归实现watcher,vue3的这种处理方式称为“懒代理”,只在用到了这个属性时(模板读取这个属性值时),才会去判断该属性值是否对象,如是才会去将该属性值对应的对象处理为Proxy响应式对象;如果没有用到这个属性,则不会去实现这个属性值对应的对象的响应式处理。这样的处理方式大大提高了性能。
vue3相对vue2实现响应式有什么不同,对性能提升有什么帮助?首先,vue3通过proxy实现对目标对象的代理,从而实现响应式。在通过proxy对目标对象代理的过程中,默认只会对对象第一层数据进行代理,如果不会读取或使用嵌套的对象属性值,则不会对嵌套的对象属性值进行代理,只有当使用嵌套的对象属性值时,才会对其进行代理。这样的处理方式大大提高了性能。
第8节:《实现响应式》
1、实现Proxy的handler中的set方法:
由于reodonly与shallowReadonly不能设置值(视频课程里应该是没考虑shallowReadonly的浅层次只读),所以在这两个方法对应的handler中的set方法里,直接打印一个提示信息:设置值失败
2、在packages文件夹下新建examples,新建1.reactive.html文件,引入打包后的reactivity文件,测试刚刚实现的reactive方法:
测试readonly方法:
测试 shallowReactive :
3、实现 reactive 与 shallowReactive 中Proxy对象handler中的set方法编写,统一用一个createSetter方法来处理:
第9节:《回顾上节课的知识点》
完善笔记:
优化代码,对象合拼: