前一段时间,尤雨溪公布了Vue 3.0 的核心部分代码。咱们趁着目前代码量还比较少,来赶忙学一下。vue
Vue3.0 仓库react
首先看一下 Vue3.0 的文件目录结构,全部的核心代码都在 packages 文件夹下:git
reactivity :Vue3.0的数据响应式系统,咱们确定都据说 Vue3 摒弃了 Object.defineProperty,基于 Proxy 实现了一套响应式系统,就是这个文件夹下。github
runtime-core、runtime-dom、runtime-test,这三个文件夹都是Vue 运行时相关的核心代码。bash
compiler-core、compiler-dom、compiler-sfc,这三个文件夹是 Vue 实现的编译器相关代码。数据结构
server-renderer 是服务端渲染部分。dom
vue 文件夹是整个项目打包构建以后的出口文件。函数
咱们首先来阅读 reactivity 响应式系统相关的代码。 响应式系统分别有几个文件:: ref 、reactive 、baseHandlers、collectionHandlers、computed 、effect 、lock 、operations。工具
其中lock
其中有两个控制锁的开关的方法,operations
里分别枚举了几种数据操做类型。ui
reactive
: 响应式系统的核心部分,Proxy 就在这里了。
ref
:ref 其实提供了一套 Ref
类型,它的主要做用是让JS的基础数据类型也能成为响应式数据被监测。
computed
: computed 不用多说,就是 Vue2 中咱们已经熟悉的计算属性了。
baseHandlers
、collectionHandlers
,收集依赖和触发监听方法的 Handlers
effect
:effect 里面包含了具体如何收集依赖和触发监听方法的处理逻辑。
今天主要分析的就是 reactive 文件,话很少说,咱们直接上代码:
首先文件的一开始分别引入了一下外部代码:
import { isObject, toRawType } from '@vue/shared'
复制代码
isObject
判断数据是不是对象,toRawType
获取数据类型。都是工具方法。
import {
mutableHandlers, // 可变数据的 Handlers
readonlyHandlers, // 只读数据的 Handlers
readonlyPropsHandlers // 只读 Props 的 Handlers
} from './baseHandlers'
import {
mutableCollectionHandlers, // 可变集合数据的 Handlers
readonlyCollectionHandlers // 只读集合数据的 Handlers
} from './collectionHandlers'
复制代码
上面引入了针对三种数据的 handlers 处理。
import { ReactiveEffect } from './effect'
复制代码
从 effect 文件引入了一种数据类型。
import { UnwrapRef, Ref } from './ref'
复制代码
从 ref 文件引入了两种数据类型。
import { makeMap } from '@vue/shared'
复制代码
生成 map 的工具方法。
export type Dep = Set<ReactiveEffect>
export type KeyToDepMap = Map<any, Dep>
export const targetMap = new WeakMap<any, KeyToDepMap>()
复制代码
定义了一个 targetMap 常量,为了减小内存开销,使用 WeakMap 数据类型,可是还不知道这个 targetMap 是用来作什么的。
const rawToReactive = new WeakMap<any, any>() // 原始数据 和 响应式数据的映射
const reactiveToRaw = new WeakMap<any, any>() // 响应式数据 和 原始数据的映射
const rawToReadonly = new WeakMap<any, any>() // 原始数据和只读的映射
const readonlyToRaw = new WeakMap<any, any>() // 只读数据和原始数据的映射
复制代码
这里定义了四个 WeakMap 的常量来进行数据的双向映射。
const readonlyValues = new WeakSet<any>() // 被用户标记为只读类型的数据
const nonReactiveValues = new WeakSet<any>() // 被用户标记为不能转换成响应式的的数据
// 几种集合数据类型
const collectionTypes = new Set<Function>([Set, Map, WeakMap, WeakSet])
// 是不是可监测类型数据
const isObservableType = /*#__PURE__*/ makeMap(
'Object,Array,Map,Set,WeakMap,WeakSet'
)
复制代码
除了上面定义的一些常量,还有一些工具方法,基本很简单易懂,咱们直接来看核心方法。 这里有三个方法,分别是 reactive
、readonly
、readonlyProps
方法:
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
// if trying to observe a readonly proxy, return the readonly version.
// 已经在只读map里,说明已是响应式数据,直接 return
if (readonlyToRaw.has(target)) {
return target
}
// target is explicitly marked as readonly by user
// 被用户标记为只读类型,调用只读方法
if (readonlyValues.has(target)) {
return readonly(target)
}
// 调用 建立响应式对象方法
return createReactiveObject(
target, // 目标对象
rawToReactive, // 原始数据 map
reactiveToRaw, // 响应式数据 map
mutableHandlers, // 可变数据的处理
mutableCollectionHandlers // 可变集合的处理
)
}
export function readonly<T extends object>(
target: T
): Readonly<UnwrapNestedRefs<T>> {
// value is a mutable observable, retrieve its original and return
// a readonly version.
// 若是响应式 map 里有目标对象,从 map 里取出
if (reactiveToRaw.has(target)) {
target = reactiveToRaw.get(target)
}
// 调用 建立响应式对象方法
return createReactiveObject(
target,
rawToReadonly,
readonlyToRaw,
readonlyHandlers,
readonlyCollectionHandlers
)
}
export function readonlyProps<T extends object>(
target: T
): Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]> }> {
return createReactiveObject(
target,
rawToReadonly,
readonlyToRaw,
readonlyPropsHandlers,
readonlyCollectionHandlers
)
}
复制代码
从以上能够看出,除了一些相应校验以外,主要调用了 createReactiveObject 方法。
function createReactiveObject(
target: unknown,
toProxy: WeakMap<any, any>,
toRaw: WeakMap<any, any>,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>
) {
// 判断 target 是不是对象
if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
// target already has corresponding Proxy
// 原始数据 已经有 对应的 响应式数据
// 直接返回 对应的 响应式数据
let observed = toProxy.get(target)
if (observed !== void 0) {
return observed
}
// target is already a Proxy
// 原始数据 已经在 toRaw 里,说明已是响应式数据
// 直接返回 target
if (toRaw.has(target)) {
return target
}
// only a whitelist of value types can be observed.
// 目标数据不能被 监听
if (!canObserve(target)) {
return target
}
// 根据数据是不是集合找寻对应的 handlers
const handlers = collectionTypes.has(target.constructor)
? collectionHandlers
: baseHandlers
// 使用 new Proxy 监听,返回一个响应式数据
observed = new Proxy(target, handlers)
// 设置原始数据与响应式数据的双向映射
toProxy.set(target, observed)
toRaw.set(observed, target)
// 若是 targetMap 里没有 入参的原始数据,那么 set 进 targetMap 里
if (!targetMap.has(target)) {
targetMap.set(target, new Map())
}
return observed
}
复制代码
咱们能够看到 createReactiveObject
方法来生成了一个响应式数据,可是具体的收集依赖和监听函数都在 handlers
里。但 handlers
里是怎么个操做逻辑呢,这个具体等以后的文章来分析。
另外 createReactiveObject
里用到了上面咱们不知因此的 targetMap
常量,那么再回头来看一下。
export type Dep = Set<ReactiveEffect>
export type KeyToDepMap = Map<any, Dep>
export const targetMap = new WeakMap<any, KeyToDepMap>()
复制代码
targetMap
的 key 就是咱们在 createReactiveObject
方法里传入的 target
,而 value 是一个 KeyToDepMap
类型的数据,是一个 any 类型的值 和 Dep
的映射。 Dep
又是一个 ReactiveEffect
类型的 Set 集合。
能够看出来 targetMap
是一个三维的数据结构。 后续若是看到 effect
部分代码的话,咱们会知道 KeyToDepMap
的 key,实际上是 target
对象的各个属性,而 Dep
固然就是存对应的监听函数了。
其实这部分的代码比较简单,主要是几个数据映射和一些校验判断,更复杂的逻辑仍是在 handlers
和 effect
。Vue3.0 彻底是使用 TS 编写,我也是初学 TS,因此读起来仍是有些吃力的,因此先解读这一部分的代码,后续再继续深刻。