vue3.0源码

397 阅读3分钟

对比vue2

  • 组件模块化相对vue2.0更加细,耦合度更低,无需全部vue.js引入才能使用
  • 减少了打包体积,许多方法未使用无需打包
  • 自定义渲染器能力增强,无需改源码才能扩展,vue3提供了扩展的api

image.png

命名式编程vs声明式编程

  • 命名式编程需要关注编程的中间逻辑过程
  • vue3声明式编程,底层已经将过程封装好了,只需要调取声明式编程的api

pnpm

image.png

reactive

使用proxy实现

image.png 缓存同一个对象

image.png 代理对象被再次代理 可以直接返回

image.png

image.png

const {effect,reactive}=VueReactivity
  • vue2.0 defineProperty只对定义好的属性进行劫持,需要对属性进行重写添加getter和setter,导致性能降低
  • 新增属性和删除属性无法监控变化,需要通过setset delete实现
  • 数组需要单独处理,不采用defineProperty进行劫持 Vue3.0使用proxy实现响应式,解决上述问题

image.png Reflect将this变成代理对象 receiver改变this指向

effect

effect根据状态变化重新执行,也可以嵌套执行

effect(()=>{})

image.png image.png

响应式

image.png 通过activeEffect导出到reactive,通过activeEffect和key关联,监听key的变化,促使effect执行run方法

  • 处理effect中嵌套问题

image.png 方案1:入栈出栈方式 方案2:记录每个effect父级

image.png 目前方式

image.png 响应式方法track

image.png key关联activeEffect

image.png trigger更新响应方法:促使effect中的函数重新获取

image.png 处理同一个effect情况

image.png

image.png effect更新

image.png effect分支切换:每次effect重新收集依赖 清理deps

image.png

源码

  1. 核心effect,react方法
  2. vue3默认所有对象都进行了proxy,嵌套的对象也会proxy,当然基于react衍生了其他api,例如只针对一层的proxy:shallowReactive
  3. proxy并没有重新定义属性,只是在修改属性的时候,调用了get set
  4. 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有问题

  1. 多个reactive代理同一个对象(配合weakMap)/proxy代理proxy后的对象(配合缓存变量判断) 进行了缓存
  2. effect针对嵌套effect流程进行堆栈的思路,记录当前执行activeEffect的父级effect,当当前effect执行完成后,提取父级effect继续执行
//嵌套结构
effect(()=>{
  effect(()=>{}) //记录父级 跳出effect再次调用父级effect
})
  1. 响应式流程
  • 先搞一个响应式对象new proxy
  • effect默认数据变化要能更新,我们先将正在执行的effect作为全局变量,渲染(取值),我们在get方法中进行依赖收集
  • weakmap(对象:map(属性:set(effect)))
  • 稍后用户发生数据变化,会通过对象属性查找对应的effect集合,找到effect全部执行
  1. 分支切换:每次执行effect都需要清理无用的effect