theme: fancy
目录:
- createApp - vue3源码解读 - 掘金 (juejin.cn))
我们接着上一篇文章来讲,createApp
构建之后,我们进行mount
挂载,在其中执行了两个重要的操作,第一是createVNode
,第二是render
,今天我们来学习下createVNode
中究竟发生了什么。
一.执行createVNode
此方法在mount
中调用,其中传入两个参数,一个是rootComponent
,也就是我们的根组件App.vue
,另一个是rootProps
根参数,初始值为null,我们了解了它的调用之后,继续往下看,看下它是如何定义的。
//源码路径 core/packages/runtime-core/src/apiCreateApp.ts
const vnode = createVNode(
rootComponent as ConcreteComponent,
rootProps
)
二. createVNode的定义
1. createVNode
通过源码我们可以看到,createVNode
有两种形式,在开发环境
下是createVNodeWithArgsTransform
,而在生产环境
下是_createVNode
,好的,我们继续往下看。
//源码路径 core/packages/runtime-core/src/vnode.ts
export const createVNode = (
__DEV__ ? createVNodeWithArgsTransform : _createVNode
) as typeof _createVNode
2. createVNodeWithArgsTransform
我们可以看到,该方法最后还是调用了createVNode
方法,因此我们直接来看该方法的实现。
//源码路径 core/packages/runtime-core/src/vnode.ts
const createVNodeWithArgsTransform = (
...args: Parameters<typeof _createVNode>
): VNode => {
return _createVNode(
...(vnodeArgsTransformer
? vnodeArgsTransformer(args, currentRenderingInstance)
: args)
)
}
3. _createVNode
该函数中初始化了一些数据,并且对传入的组件做了一些处理,如果传入的组件已经是一个虚拟dom
,则直接对其克隆并返回,同时也对异步组件做了兼容处理,同时确定了shapeFlag
属性,该属性标识了该VNode
的类型(ELEMENT | SUSPENSE | TELEPORT),最后调用了createBaseVNode
方法,我们接着往下看。
function _createVNode(
type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
props: (Data & VNodeProps) | null = null,
children: unknown = null,
patchFlag: number = 0,
dynamicProps: string[] | null = null,
isBlockNode = false
): VNode {
if (isVNode(type)) {
const cloned = cloneVNode(type, props, true /* mergeRef: true */)
if (children) {
normalizeChildren(cloned, children)
}
return cloned
}
// class component normalization.
if (isClassComponent(type)) {
type = type.__vccOpts
}
// 2.x async/functional component compat
if (__COMPAT__) {
type = convertLegacyComponent(type, currentRenderingInstance)
}
// class & style normalization.
if (props) {
// for reactive or proxy objects, we need to clone it to enable mutation.
props = guardReactiveProps(props)!
let { class: klass, style } = props
if (klass && !isString(klass)) {
props.class = normalizeClass(klass)
}
if (isObject(style)) {
// reactive state objects need to be cloned since they are likely to be
// mutated
if (isProxy(style) && !isArray(style)) {
style = extend({}, style)
}
props.style = normalizeStyle(style)
}
}
// encode the vnode type information into a bitmap
const shapeFlag = isString(type)
? ShapeFlags.ELEMENT
: __FEATURE_SUSPENSE__ && isSuspense(type)
? ShapeFlags.SUSPENSE
: isTeleport(type)
? ShapeFlags.TELEPORT
: isObject(type)
? ShapeFlags.STATEFUL_COMPONENT
: isFunction(type)
? ShapeFlags.FUNCTIONAL_COMPONENT
: 0
return createBaseVNode(
type,
props,
children,
patchFlag,
dynamicProps,
shapeFlag,
isBlockNode,
true
)
}
4. createBaseVNode
在这个方法里真正的生成了VNode
,对其属性做了初始化,同时对 props
做了规范化处理,将VNode
返回。
function createBaseVNode(
type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
props: (Data & VNodeProps) | null = null,
children: unknown = null,
patchFlag = 0,
dynamicProps: string[] | null = null,
shapeFlag = type === Fragment ? 0 : ShapeFlags.ELEMENT,
isBlockNode = false,
needFullChildrenNormalization = false
) {
// 初始化 vnode
const vnode = {
__v_isVNode: true,
__v_skip: true,
type,
props,
key: props && normalizeKey(props),
ref: props && normalizeRef(props),
scopeId: currentScopeId,
slotScopeIds: null,
children,
component: null,
suspense: null,
ssContent: null,
ssFallback: null,
dirs: null,
transition: null,
el: null,
anchor: null,
target: null,
targetAnchor: null,
staticCount: 0,
shapeFlag,
patchFlag,
dynamicProps,
dynamicChildren: null,
appContext: null
} as VNode
if (needFullChildrenNormalization) {
normalizeChildren(vnode, children)
// normalize suspense children
if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
;(type as typeof SuspenseImpl).normalize(vnode)
}
} else if (children) {
vnode.shapeFlag |= isString(children)
? ShapeFlags.TEXT_CHILDREN
: ShapeFlags.ARRAY_CHILDREN
}
if (
isBlockTreeEnabled > 0 &&
!isBlockNode &&
// has current parent block
currentBlock &&
(vnode.patchFlag > 0 || shapeFlag & ShapeFlags.COMPONENT) &&
vnode.patchFlag !== PatchFlags.HYDRATE_EVENTS
) {
currentBlock.push(vnode)
}
return vnode
}
三. 总结
让我们来回顾下构建VNode
的流程:
c
r
e
a
t
e
V
N
o
d
e
−
>
_
c
r
e
a
t
e
V
N
o
d
e
−
>
c
r
e
a
t
e
B
a
s
e
V
N
o
d
e
−
>
V
N
o
d
e
createVNode -> \_createVNode -> createBaseVNode -> VNode
createVNode−>_createVNode−>createBaseVNode−>VNode