APP实例
首先,我们先了解一下Vue实例(App对象)所包含的属性以及方法.
控制台输出的createApp()创建的Vue实例对象

CreateApp
CreateApp是Vue3的实例创建方法, 方法定义在runtime-dom/src/index.ts
方法主要调用流程如下:
- 调用
ensureRenderer方法来创建渲染器(单例)
创建渲染器.runtime-core/src/renderer.ts的baseCreateRenderer方法baseCreateRenderer方法主要定义了补丁(patch)方法以及一些vNode操作方法
- 补丁方法
- patch
- patchChildren
- patchBlockChildren
- vNode操作
- unmount
- move
- remove
- mountComponent
- mountChildren
- getNextHostNode
- options(包含一些dom操作方法的对象)
- render(渲染方法, 会传给
**createAppApi**)
- 调用
createAppAPI中的createApp方法来创建Vue实例对象
实则调用runtime-core/src/apiCreateApp.ts中的createAppAPI的返回值createApp方法来创建实例对象
该方法主要内容是
- 初始化Vue实例对象(定义对象以及一些操作方法)
- 初始化安装的插件
- 初始化挂载标识(
isMounted=false)

- 重写第二步创建好的Vue实例对象的
mount方法 - 调用
normalizeContainer方法来标准化容器 - 如果组件对象没有定义
render函数和template模板,则取容器的innerHTML作为组件模板内容 - 清空容器内容(
innerHTML = '') - 调用原始
mount方法作为方法返回值 - 移除容器
v-cloak属性 - 为容器设置
data-v-app属性, 表明元素是一个Vue实例容器 - Vue实例创建完成, 等待调用
mount方法进行挂载
Mount(原始版本)
调用完createApp创建Vue实例后最终需要挂载到指定元素上, 调用mount方法并传入目标容器来进行挂载操作.
- 创建VNode对象
- 将app对象存储在vNode对象上
- 进行渲染(调用
render) - 设置挂载标识(
isMounted)为true - 缓存根节点容器(
rootContainer) - 通过
__vue_app__属性为devtools暴露vue实例
Render
首先, 我们来看一下render方法的定义
const render = (vnode, container, isSVG) => {
// 如果vnode为空, 则表明元素被清空
if (vnode == null) {
// 如果容器之前挂载过vNode,则卸载之前挂载的vNode
if (container._vnode) {
unmount(container._vnode, null, null, true);
}
}
else {
// 如果有vNode, 即容器内元素变化,打补丁(patch 方法由ensureRenderer定义)
patch(container._vnode || null, vnode, container, null, null, null, isSVG);
}
// 刷新后置任务
flushPostFlushCbs();
// 更新vNode绑定
container._vnode = vnode;
};
render方法接收三个参数(vnode, container, isSVG)
- vNode: 待渲染的vNode
- container: 渲染容器
- isSVG: 是否是SVG
- 判断是否有需要渲染的vNode,
- 如果没有, 则卸载之前容器挂载过的vNode(如果存在的话)
- 如果有, 则调用补丁方法来为容器更新节点
- 冲洗后置任务队列(
flushPostFlushCbs) - 为容器更新
_vnode属性
Patch
补丁方法
- 判断是否是相同节点, 如果相同则直接退出
- 如果有容器有旧节点, 并且新节点与旧节点类型不同, 则将锚点设置为旧节点的下一个节点并卸载旧节点
- 判断新节点类型, 根据不同的节点类型来进行不同处理
- Text:
- Comment:
- Static:
- Fragment:
- Others:
- Element:
- Component
- 继承slotScopeIds
- 如果原容器没有节点
- 如果不是keepAlive, 则挂载节点(mountComponent)
- 如果是keepAlive, 则
activate
- 原容器有节点, 则更新组件(
updateComponent)
- Teleport:
- Suspense:
- 如果有
ref则调用setRef来设置ref
mountComponent
挂载组件方法, 方法大致流程以及操作如下
- 创建组件实例
- 如果是keepAlive组件则更新renderer
- 设置组件(setupComponent)
- 设置
props,attrs - 设置
slots - 如果是有状态组件,且有组件有
**setup**方法, 则执行**setup**
- 设置
- 设置渲染器影响(
setupRenderEffect)- 定义组件更新方法
- 为渲染过程创建响应式影响对象
createComponentInstance
创建组件实例方法, 组件实例的定义如下
const instance: ComponentInternalInstance = {
uid: uid++,
vnode,
type,
parent,
appContext,
root: null!, // to be immediately set
next: null,
subTree: null!, // will be set synchronously right after creation
update: null!, // will be set synchronously right after creation
scope: new EffectScope(true /* detached */),
render: null,
proxy: null,
exposed: null,
exposeProxy: null,
withProxy: null,
provides: parent ? parent.provides : Object.create(appContext.provides),
accessCache: null!,
renderCache: [],
// local resovled assets
components: null,
directives: null,
// resolved props and emits options
propsOptions: normalizePropsOptions(type, appContext),
emitsOptions: normalizeEmitsOptions(type, appContext),
// emit
emit: null!, // to be set immediately
emitted: null,
// props default value
propsDefaults: EMPTY_OBJ,
// inheritAttrs
inheritAttrs: type.inheritAttrs,
// state
ctx: EMPTY_OBJ,
data: EMPTY_OBJ,
props: EMPTY_OBJ,
attrs: EMPTY_OBJ,
slots: EMPTY_OBJ,
refs: EMPTY_OBJ,
setupState: EMPTY_OBJ,
setupContext: null,
// suspense related
suspense,
suspenseId: suspense ? suspense.pendingId : 0,
asyncDep: null,
asyncResolved: false,
// lifecycle hooks
// not using enums here because it results in computed properties
isMounted: false,
isUnmounted: false,
isDeactivated: false,
bc: null,
c: null,
bm: null,
m: null,
bu: null,
u: null,
um: null,
bum: null,
da: null,
a: null,
rtg: null,
rtc: null,
ec: null,
sp: null
}
该方法主要定义上面👆这个对象,对以下属性赋值并将实例对象(instance)返回
- ctx:
{ _: instance } - root: 有父组件则使用父组件的
root, 否则使用自己(instance) - emit: emit方法
响应式
createReactiveObject
packages/reactivity/src/reactive.ts
只有以下类型对象可以进行转化为响应式对象(Proxy)
- Object
- Array
- Map
- Set
- WeakMap
- WeakSet
以下类型对象无法进行转化
- 有真值的
__v_skip属性 - 有真值的
__v_raw属性 - 有真值的
__v_isReactive属性 - 有真值的
__v_isReadonly属性
- 如果有对应缓存则直接返回缓存proxy对象
- 根据对象是
集合(collection)还是普通对象(common)来设置不同的代理处理方法 - 以原始对象为key,代理对象为value放入代理表(
proxyMap)进行缓存
SetupComponent
方法定位: packages/runtime-core/src/component.ts
该方法用于初始化组件设置
initProps: 初始化组件的props和attrs
initSlots: 初始化组件的插槽
如果是有状态的组件, 则调用setupStatefulComponent
setupStatefulComponent
方法定位: packages/runtime-core/src/component.ts
初始化有状态组件
- 初始化渲染函数代理属性访问缓存(
accessCache) - 创建一个不被观测的组件实例上下文(
instance.ctx)代理对象 - 如果组件有配置
setup- 初始化setup上下文
- 设置当前实例(开启影响范围)
- 暂停跟踪
- 调用实例
setup方法 - 恢复跟踪
- 取消当前实例设置
- 处理setup结果(handleSetupResule)
- 没有配置
setup就结束组件配置(调用finishComponentSetup来进行模板编译)
handleSetupResult
finishComponentSetup
1.如果组件实例没定义render
获取组件模板(template)以及编译器配置, 调用编译方法(compile)将编译完成的对象作为组件的render方法
- 构建实例的代理对象
- 应用组件选项applyOptions
compileToFunction
方法位置: packages/vue/src/index.ts的compileToFunction
方法接收两个参数template(模板)与options(编译器选项)
- 以模板为key查找缓存
- 如果模板字符串以
#开头,则作为ID选择器进行元素查找- 如果找到则使用元素的
innerHTML作为模板 - 没找到则使用空字符串作为模板
- 如果找到则使用元素的
- 调用编译方法(compile)来编译模板字符串
compile
方法位置: packages/compiler-dom/src/index.ts
方法返回实质调用baseCompile
baseCompile
方法位置: packages/compiler-core/src/compile.ts
- 调用baseParse将字符串模板转化为AST(抽象语法树)
-
baseParse
方法位置: packages/compiler-core/src/parse.ts
字符串模板解析方法
- 创建解析环境(
createParserContext) - 获取解析开始位置(
getCursor) - 创建根节点(
createRoot)
parseChildren
applyOptions
文件位置: packages/runtime-core/src/componentOptions.ts
组件选项初始化顺序:
props(在该方法外就早已完成初始化, 待查明具体方法)injectmethodsdata(直到它依赖this访问)computedwatch(直到它依赖this访问)- 解析合并组件选项
- 检查是否有重复定义的属性
- 如果有方法(
methods)定义, 则遍历方法为每个方法更新this指向(使用bind绑定当前组件代理对象) - 如果有
data方法定义, 使用call(publicThis, publicThis)来绑定到当前实例代理对象上?TODO - 将
data调用reactive转化为响应式对象并赋值给组件实例 - 如果有计算属性(
computed)
如果是方法, 则更新this指向 - 如果有
watch定义, 则调用createWatcher来创建watcher