Vue-next源码浅析

目前版本处于Pre-Alpha阶段,基本架构功能已完成,主要包括CompilerRuntime两部分。

第一眼看上去最显著变化有两个,一个就是代码使用TypeScript编写,另外一个就是Modular architecture(模块化架构?暂时这么翻译),内部功能分解为单独的包,隔离复杂性,提高了可维护性。这样的架构划分React早已使用,算是React又领先一步。

接下来从入口文件开始分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* packages/vue/src/index.ts */
// 入口文件
// 包括运行时,编译和运行时编译模板功能
import { compile, CompilerOptions } from '@vue/compiler-dom'
import * as runtimeDom from '@vue/runtime-dom'
import { registerRuntimeCompiler, RenderFunction } from '@vue/runtime-dom'
function compileToFunction(
template: string,
options?: CompilerOptions
): RenderFunction {
const { code } = compile(template, {
hoistStatic: true,
...options
})
return new Function('Vue', code)(runtimeDom) as RenderFunction
}
// 注册compile,支持运行时编译
registerRuntimeCompiler(compileToFunction)
export { compileToFunction as compile }
export * from '@vue/runtime-dom'

从代码可以看出依赖两个核心模块,compiler-domruntime-dom,分别提供了编译功能和运行时功能。先从runtime-dom开始,对运行时实现进行分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* packages/runtime-dom/src/index.ts */
// 基于Browser DOM的运行时
import { createRenderer } from '@vue/runtime-core'
import { nodeOps } from './nodeOps'
import { patchProp } from './patchProp'
const { render, createApp } = createRenderer<Node, Element>({
patchProp,
...nodeOps
})
export { render, createApp }
// 导出运行时核心功能
export * from '@vue/runtime-core'
export interface ComponentPublicInstance {
$el: Element
}

该模块是Browser的运行时实现,提供了基于DOM的各种节点操作,调用createRenderer完成render初始化。该方法由runtime-core模块提供,封装了节点diff整个过程(核心方法patch),同时还提供了支持组件运行时的各类API。

下面是patch函数的实现,截取了部分代码实现,主要为了缕清整体设计思路。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/* packages/runtime-core/src/createRenderer.ts */
// 根据平台自定义render实现
// 实现了补丁函数,用于进行节点diff操作
export function createRenderer<
HostNode extends object = any,
HostElement extends HostNode = any
>(
options: RendererOptions<HostNode, HostElement>
): {
render: RootRenderFunction<HostNode, HostElement>
createApp: () => App<HostElement>
} {
type HostVNode = VNode<HostNode, HostElement>
type HostVNodeChildren = VNodeChildren<HostNode, HostElement>
type HostSuspenseBoundary = SuspenseBoundary<HostNode, HostElement>
const {
insert: hostInsert,
remove: hostRemove,
patchProp: hostPatchProp,
createElement: hostCreateElement
// ...
} = options
// 补丁函数
function patch(
n1: HostVNode | null, // null means this is a mount
n2: HostVNode,
container: HostElement,
anchor: HostNode | null = null,
parentComponent: ComponentInternalInstance | null = null,
parentSuspense: HostSuspenseBoundary | null = null,
isSVG: boolean = false,
optimized: boolean = false
) {
// ...
processComponent(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
)
// ...
}
// ...
// 处理自定义组件
function processComponent(
n1: HostVNode | null,
n2: HostVNode,
container: HostElement,
anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: HostSuspenseBoundary | null,
isSVG: boolean,
optimized: boolean
) {
if (n1 == null) {
// 挂载组件
mountComponent(
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG
)
} else {
// 更新组件
const instance = (n2.component = n1.component)!
// ...
instance.update()
// ...
}
}
function mountComponent(
initialVNode: HostVNode,
container: HostElement,
anchor: HostNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: HostSuspenseBoundary | null,
isSVG: boolean
) {
const instance: ComponentInternalInstance = (initialVNode.component = createComponentInstance(
initialVNode,
parentComponent
))
// ...
// setup stateful logic
if (initialVNode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
setupStatefulComponent(instance, parentSuspense)
}
// ...
// 创建redner effect,响应数据更新
setupRenderEffect(
instance,
parentSuspense,
initialVNode,
container,
anchor,
isSVG
)
}
// render effect
function setupRenderEffect(
instance: ComponentInternalInstance,
parentSuspense: HostSuspenseBoundary | null,
initialVNode: HostVNode,
container: HostElement,
anchor: HostNode | null,
isSVG: boolean
) {
// create reactive effect for rendering
// 相当于2.x render watch
let mounted = false
instance.update = effect(function componentEffect() {
if (!mounted) {
// 调用组件render函数,返回VNode
const subTree = (instance.subTree = renderComponentRoot(instance))
// 调用patch方法
patch(null, subTree, container, anchor, instance, parentSuspense, isSVG)
// ...
mounted = true
} else {
// updateComponent
const { next } = instance
const prevTree = instance.subTree
const nextTree = (instance.subTree = renderComponentRoot(instance))
// ...
patch(
prevTree,
nextTree,
// parent may have changed if it's in a portal
hostParentNode(prevTree.el as HostNode) as HostElement,
// anchor may have changed if it's in a fragment
getNextHostNode(prevTree),
instance,
parentSuspense,
isSVG
)
// ...
}
}, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions)
}
// ...
// 根节点render函数
function render(vnode: HostVNode | null, rawContainer: HostElement | string) {
let container: any = rawContainer
if (vnode == null) {
if (container._vnode) {
unmount(container._vnode, null, null, true)
}
} else {
// 调用patch方法,挂载根节点
patch(container._vnode || null, vnode, container)
}
//
flushPostFlushCbs()
container._vnode = vnode
}
return {
render,
createApp: createAppAPI(render) // 创建
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* packages/runtime-core/src/apiApp.ts */
export function createAppAPI<HostNode, HostElement>(
render: RootRenderFunction<HostNode, HostElement>
): () => App<HostElement> {
return function createApp(): App {
const context = createAppContext()
let isMounted = false
const app: App = {
mount(
rootComponent: Component,
rootContainer: string | HostElement,
rootProps?: Data
): any {
// ...
const vnode = createVNode(rootComponent, rootProps)
vnode.appContext = context
// 调用根节点render函数
render(vnode, rootContainer)
return vnode.component!.renderProxy
}
}
return app
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/* packages/runtime-core/src/component.ts */
// 创建带状态的响应式组件
export function setupStatefulComponent(
instance: ComponentInternalInstance,
parentSuspense: SuspenseBoundary | null
) {
const Component = instance.type as ComponentOptions
// 1. create render proxy
instance.renderProxy = new Proxy(instance, PublicInstanceProxyHandlers)
// 2. create props proxy
// the propsProxy is a reactive AND readonly proxy to the actual props.
// it will be updated in resolveProps() on updates before render
const propsProxy = (instance.propsProxy = readonly(instance.props))
// 3. call setup()
const { setup } = Component
if (setup) {
// ...
const setupResult = callWithErrorHandling(
setup,
instance,
ErrorCodes.SETUP_FUNCTION,
[propsProxy, setupContext]
)
if (
setupResult &&
isFunction(setupResult.then) &&
isFunction(setupResult.catch)
) {
// ...
instance.asyncDep = setupResult
return
} else {
handleSetupResult(instance, setupResult, parentSuspense)
}
} else {
finishComponentSetup(instance, parentSuspense)
}
}
// 设置实例render
export function handleSetupResult(
instance: ComponentInternalInstance,
setupResult: unknown,
parentSuspense: SuspenseBoundary | null
) {
if (isFunction(setupResult)) {
// setup returned an inline render function
instance.render = setupResult as RenderFunction
} else if (isObject(setupResult)) {
// setup returned bindings.
// assuming a render function compiled from template is present.
instance.renderContext = reactive(setupResult)
}
// ...
finishComponentSetup(instance, parentSuspense)
}
function finishComponentSetup(
instance: ComponentInternalInstance,
parentSuspense: SuspenseBoundary | null
) {
const Component = instance.type as ComponentOptions
if (!instance.render) {
if (Component.template && !Component.render) {
// 支持运行时编译
Component.render = compile(Component.template, {
onError(err) {}
})
}
instance.render = (Component.render || NOOP) as RenderFunction
}
// support for 2.x options
if (__FEATURE_OPTIONS__) {
// ...
// 支持2.x参数组件,调用生命周期、响应式数据处理等等
applyOptions(instance, Component)
// ...
}
}

整个组件的核心更新过程就到此结束,在patch的实现过程中,提到了两个新的方法,reactiveeffect,这就带出了Vue的另一个核心功能,响应式实现。

响应式的实现在reactivity包下,相比2.x的实现,Watch,Dep,Observer的组合实现,3.0在代码结构上更加简练,核心包括reactiveeffect。代码部分,不再使用defineProperty,改为Proxy,除此之外还使用了很多ES6的特性,例如Map,WeakMap等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/* packages/reactivity/src/reactive.ts */
export function reactive(target: object) {
// ...
return createReactiveObject(
target,
rawToReactive,
reactiveToRaw,
mutableHandlers,
mutableCollectionHandlers
)
}
// 响应式的实现
function createReactiveObject(
target: any,
toProxy: WeakMap<any, any>,
toRaw: WeakMap<any, any>,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>
) {
// ...
// 判断对象类型,Set, Map, WeakMap, WeakSet 还是普通对象
const handlers = collectionTypes.has(target.constructor)
? collectionHandlers
: baseHandlers
observed = new Proxy(target, handlers) // 创建代理值
toProxy.set(target, observed)
toRaw.set(observed, target) // 设置代理对象和原始值关系
if (!targetMap.has(target)) {
targetMap.set(target, new Map()) // 设置target -> key -> dep关系
}
return observed // 返回代理对象
}

下面只沿着普通对象类型继续分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/* packages/reactivity/src/baseHandlers.ts */
// 设置get处理函数,绑定依赖
function createGetter(isReadonly: boolean) {
return function get(target: any, key: string | symbol, receiver: any) {
// ...
const res = Reflect.get(target, key, receiver) // 默认复制行为
track(target, OperationTypes.GET, key) // 搜集target-key-dep依赖关系,相当于2.x dep.update()
return isObject(res)
? isReadonly
?
readonly(res)
: reactive(res)
: res
}
}
// 设置set处理函数,触发更新
function set(
target: any,
key: string | symbol,
value: any,
receiver: any
): boolean {
value = toRaw(value) //
const hadKey = hasOwn(target, key)
const oldValue = target[key]
if (isRef(oldValue) && !isRef(value)) {
oldValue.value = value
return true
}
const result = Reflect.set(target, key, value, receiver) // 调用原始赋值操作
// don't trigger if target is something up in the prototype chain of original
if (target === toRaw(receiver)) {
// ...
trigger(target, OperationTypes.ADD, key) // 触发更新,相当于2.x dep.notify()
}
return result
}
function deleteProperty(target: any, key: string | symbol): boolean {
// ...
}
function has(target: any, key: string | symbol): boolean {
// ...
}
function ownKeys(target: any): (string | number | symbol)[] {
// ...
}
export const mutableHandlers: ProxyHandler<any> = {
get: createGetter(false),
set,
deleteProperty,
has,
ownKeys
}

到这里,reactivityProxy的部分就结束了,里面两个核心方法,tracktrigger,这两个方法由effect提供

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/* packages/reactivity/src/effect.ts */
// 相当于2.x dep.depend
export function track(
target: any,
type: OperationTypes,
key?: string | symbol
) {
const effect = activeReactiveEffectStack[activeReactiveEffectStack.length - 1] // 存放响应回调,相当于2.x Dep.target
if (effect) {
let depsMap = targetMap.get(target) // key-dep依赖
if (depsMap === void 0) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key!)
if (dep === void 0) {
depsMap.set(key!, (dep = new Set()))
}
if (!dep.has(effect)) {
dep.add(effect) // 添加依赖
effect.deps.push(dep)
}
}
}
// 相当于2.x dep.notify
export function trigger(
target: any,
type: OperationTypes,
key?: string | symbol,
extraInfo?: any
) {
const depsMap = targetMap.get(target) // 获取target-key-dep关系
const effects = new Set<ReactiveEffect>()
const computedRunners = new Set<ReactiveEffect>()
// ...
addRunners(effects, computedRunners, depsMap.get(key)) // 获取key-dep依赖,加入队列
// ...
const run = (effect: ReactiveEffect) => {
scheduleRun(effect, target, type, key, extraInfo)
}
// Important: computed effects must be run first so that computed getters
// can be invalidated before any normal effects that depend on them are run.
computedRunners.forEach(run)
effects.forEach(run)
}
function addRunners(
effects: Set<ReactiveEffect>,
computedRunners: Set<ReactiveEffect>,
effectsToAdd: Set<ReactiveEffect> | undefined
) {
// ...
effectsToAdd.forEach(effect => {
if (effect.computed) {
computedRunners.add(effect)
} else {
effects.add(effect)
}
})
}
function scheduleRun(
effect: ReactiveEffect,
target: any,
type: OperationTypes,
key: string | symbol | undefined,
extraInfo: any
) {
// ...
if (effect.scheduler !== void 0) {
// 按照一定顺序执行
effect.scheduler(effect)
} else {
// 直接执行
effect()
}
}

track方法用来搜集依赖,相当于2.x的Depdepend方法,通过维护一个Map对象,存放属性和回调的关系。trigger方法相当于2.x的Depnotify方法,负责触发回调。

另外一个方法effect,相当于2.x的watcher,提供了回调函数注册功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* packages/reactivity/src/effect.ts */
export function effect(
fn: Function,
options: ReactiveEffectOptions = EMPTY_OBJ
): ReactiveEffect {
// ...
const effect = createReactiveEffect(fn, options)
// ...
return effect
}
// 创建effect
function createReactiveEffect(
fn: Function,
options: ReactiveEffectOptions
): ReactiveEffect {
const effect = function effect(...args): any {
return run(effect as ReactiveEffect, fn, args) // 调用
} as ReactiveEffect
// ...
effect.raw = fn // 注册响应回调
effect.deps = []
// ...
return effect
}
// 调用effect,绑定依赖
function run(effect: ReactiveEffect, fn: Function, args: any[]): any {
// ...
try {
activeReactiveEffectStack.push(effect) // 入栈,利用栈,保证响应回调执行顺序
return fn(...args) // 执行回调
} finally {
activeReactiveEffectStack.pop() // 出栈
}
// ...
}

未完待续…