阅读 242

Vue 3 源码解析 reactive (pre-alpha)

10.5号凌晨,发布了vue 3的pre-alpha源码,出于我对Vue的热爱,迫不及待的就从github上拉了源码,想看看Vue 3 更新了哪些有趣的变化~~

Vue最让人感兴趣的自然是它的响应式拉~ Vue 3改变了Vue 2采用Object.defineProperty的方法去而采用Proxy的监听模式

Proxy的官方定义是Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)

Proxy的语法为:

let p = new Proxy(target, handler);复制代码

target参数用于监听的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至可以是另一个代理)

handler则是一个对象,其实最常用的则是set和get方法

由target的定义就可以看出,proxy解决了Vue 2中不能监听数组改变的缺点

注:Vue 2中之所以可以监听到数组改变的原因是因为对数组方法进行了重新的包装,这七个方法分别为push, pop,shift,unshift,splice,reverse,sort

Vue 3将Observer重命名为reactive

在用法上,建议reactive监听复杂类型的数据,而ref采用简单类型的数据

为什么要这么说呢?是因为ref源码中有一个convert函数,它会先判断value是否为一个Object,如果为一个Object的情况下会调用reactive方法

源码如下:

const convert = (val: any): any => (isObject(val) ? reactive(val) : val)复制代码

简单说了下reactive和ref的一点不同(ref我会在下一节文章中讲解~),接下来还是要说reactive的实现拉~(点题^∀^)

reactive中会先判断是否为readonly,如果在readonly的情况下,就返回readonly的target

if (readonlyToRaw.has(target)) {   
 return target  
}
if (readonlyValues.has(target)) { 
   return readonly(target)
}
return createReactiveObject(  
   target,   
   rawToReactive,
   reactiveToRaw,
   mutableHandlers,
   mutableCollectionHandlers
)复制代码

最后调用了一个createReactiveObject方法,reactive的核心就是这个拉~,终于接触到了核心,让我们来看看~~

我将createReactiveObject的源码来进行分开讲解

if (!isObject(target)) {
  if (__DEV__) {
    console.warn(`value cannot be made reactive: ${String(target)}`)  
    }  
    return target
}复制代码

首先isObject就是判断当前的target是否为一个object类型,源码如下:

export const isObject = (val: any): val is Record<any, any> =>  
    val !== null && typeof val === 'object'复制代码

__DEV__是一个Boolean类型的全局变量,所以第一段代码的意思就是当target不是对象的时候会出现“不能响应target值”的警告( ớ ₃ờ)

let observed = toProxy.get(target)
if (observed !== void 0) {
  return observed
}复制代码

toProxy是一个WeakMap<any,any>类型,来看一下WeakMap接口的源码~

interface WeakMap<K extends object, V> {   
   delete(key: K): boolean;   
   get(key: K): V | undefined;
   has(key: K): boolean;
   set(key: K, value: V): this;
}复制代码

所以当target如果有相应的代理了就会返回target而不再执行下去

注:void 运算符对给定的表达式进行求值,然后返回undefined,所以void 0undefined

if (toRaw.has(target)) {   
    return target
}复制代码

toRaw的类型和toProxy一样,也是WeakMap,这段代码即判断target已经是proxy的情况下会返回target

即:toProxy和toRaw我们可以通过他们找到代理过的数据是否存在(或通过代理数据找到原始数据)

接下来就是判断target是否可以监听

 if (!canObserve(target)) {
   return target
 }复制代码

canObserve(target)的target类型必须是Object|Array|Map|Set|WeakMap|WeakSet

处理完了target,接下来就是handler拉 ≖‿≖✧

const handlers = collectionTypes.has(target.constructor)    
    ? collectionHandlers
    : baseHandlers复制代码

const collectionTypes: Set<any> = new Set([Set, Map, WeakMap, WeakSet])复制代码

可以从collectionTypes中看出,如果target.constructor是collectionTypes中声明的类型就执行collectionHandlers,否则就是baseHandlers

在处理完target和handler之后,就可以创建Proxy对象拉(Proxy介绍看本文开头)~(ง •̀_•́)ง

observed = new Proxy(target, handlers)
toProxy.set(target, observed)
toRaw.set(observed, target)
if (!targetMap.has(target)) {
  targetMap.set(target, new Map())
}
return observed复制代码

最终用Proxy构造一个Proxy对象,返回observed对象,这时候,vue的响应式核心部分就差不多结束了~~

国庆结束拉,接下来回学校做毕设上课~,Vue 3的源码刚发布,复杂度不是太高非常方便阅读,所以接下来会继续看Vue源码~

本文到这里就结束拉~,如果哪里写的不好,麻烦大家帮我指出~,一起学习,谢谢~