对比vue2
- 组件模块化相对vue2.0更加细,耦合度更低,无需全部vue.js引入才能使用
- 减少了打包体积,许多方法未使用无需打包
- 自定义渲染器能力增强,无需改源码才能扩展,vue3提供了扩展的api
命名式编程vs声明式编程
- 命名式编程需要关注编程的中间逻辑过程
- vue3声明式编程,底层已经将过程封装好了,只需要调取声明式编程的api
pnpm
reactive
使用proxy实现
缓存同一个对象
代理对象被再次代理 可以直接返回
const {effect,reactive}=VueReactivity
- vue2.0 defineProperty只对定义好的属性进行劫持,需要对属性进行重写添加getter和setter,导致性能降低
- 新增属性和删除属性无法监控变化,需要通过delete实现
- 数组需要单独处理,不采用defineProperty进行劫持 Vue3.0使用proxy实现响应式,解决上述问题
Reflect将this变成代理对象
receiver改变this指向
effect
effect根据状态变化重新执行,也可以嵌套执行
effect(()=>{})
响应式
通过activeEffect导出到reactive,通过activeEffect和key关联,监听key的变化,促使effect执行run方法
- 处理effect中嵌套问题
方案1:入栈出栈方式
方案2:记录每个effect父级
目前方式
响应式方法track
key关联activeEffect
trigger更新响应方法:促使effect中的函数重新获取
处理同一个effect情况
effect更新
effect分支切换:每次effect重新收集依赖
清理deps
源码
- 核心effect,react方法
- vue3默认所有对象都进行了proxy,嵌套的对象也会proxy,当然基于react衍生了其他api,例如只针对一层的proxy:shallowReactive
- proxy并没有重新定义属性,只是在修改属性的时候,调用了get set
- proxy中使用Reflect进行两个取值,改变调用取值时候的this指向,防止不更新
类似这种
let target={
name:'11',
get alias(){
return this.name
}
}
....
proxy
....
get(target,key,receiver){
return Reflect.get(target,key,receiver)
}
两次取值可以监听name变化时候,alias也能更新;如果不提去两次,this指向是target,非proxy无法触发get set,提取name有问题
- 多个reactive代理同一个对象(配合weakMap)/proxy代理proxy后的对象(配合缓存变量判断) 进行了缓存
- effect针对嵌套effect流程进行堆栈的思路,记录当前执行activeEffect的父级effect,当当前effect执行完成后,提取父级effect继续执行
//嵌套结构
effect(()=>{
effect(()=>{}) //记录父级 跳出effect再次调用父级effect
})
- 响应式流程
- 先搞一个响应式对象new proxy
- effect默认数据变化要能更新,我们先将正在执行的effect作为全局变量,渲染(取值),我们在get方法中进行依赖收集
- weakmap(对象:map(属性:set(effect)))
- 稍后用户发生数据变化,会通过对象属性查找对应的effect集合,找到effect全部执行
- 分支切换:每次执行effect都需要清理无用的effect