上一篇文章(https://blog.csdn.net/liubangbo/article/details/112792345)分析了渲染流程大体过程,这一篇文章分析一下创建VNode过程,以及渲染VNode过程。
分析之前还是先贴一下我们那个最简单的例子:
<html>
<head>
<style type="text/css">
</style>
</head>
<body>
<div id="app">
<div>hello {{state.msg}}</div>
</div>
<script src="../dist/vue.global.js"></script>
<script>
const {createApp, ref, reactive} = Vue
const app = createApp({
setup(){
const state = reactive({
msg: 'Vue3.0'
})
const num = ref(10)
return {state}
},
}, {style: {
color: 'red'
}}).mount('#app')
</script>
</body>
</html>
细心的读者会发现,例子有点变动。我们给createApp方法传入了第二个参数,通过运行可以看到渲染出了红色字体。我们从createApp返回的app对象的mount方法开始分析。mount方法代码如下:
mount(rootContainer: HostElement, isHydrate?: boolean): any {
//rootContainer参数就是要挂载的Dom节点(app节点)
if (!isMounted) {
//传给createVNode的参数rootComponent就是createApp第一个参数,rootProps参数就是createApp的第二个参数
const vnode = createVNode(
rootComponent as ConcreteComponent,
rootProps
)
// store app context on the root VNode.
// this will be set on the root instance on initial mount.
vnode.appContext = context
// HMR root reload
if (__DEV__) {
context.reload = () => {
render(cloneVNode(vnode), rootContainer)
}
}
if (isHydrate && hydrate) {
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
} else {
render(vnode, rootContainer)
}
isMounted = true
app._container = rootContainer
// for devtools and telemetry
;(rootContainer as any).__vue_app__ = app
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
devtoolsInitApp(app, version)
}
return vnode.component!.proxy
} else if (__DEV__) {
warn(
`App has already been mounted.\n` +
`If you want to remount the same app, move your app creation logic ` +
`into a factory function and create fresh app instances for each ` +
`mount - e.g. \`const createMyApp = () => createApp(App)\``
)
}
},
rootComponent 通过调试可以看到:
rootProps通过调试可以看到:
先看看createVNode 这个方法:
export const createVNode = (__DEV__
? createVNodeWithArgsTransform
: _createVNode) as typeof _createVNode
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 (!type || type === NULL_DYNAMIC_COMPONENT) {
if (__DEV__ && !type) {
warn(`Invalid vnode type when creating vnode: ${type}.`)
}
type = Comment
}
//....
// class & style normalization.
// 对props进行了处理
if (props) {
// for reactive or proxy objects, we need to clone it to enable mutation.
if (isProxy(props) || InternalObjectKey in props) {
props = extend({}, props)
}
//从props对象中解构出class,style属性进行处理
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
// 这里根据type类型 设置shapeFlag,就这个简单例子而言,shapeFlag是
// ShapeFlags.STATEFUL_COMPONENT
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
const vnode: VNode = {
__v_isVNode: true, //给vnode打上一个标记
[ReactiveFlags.SKIP]: true, //这里设置这个对象应该不是reactive的
type,
props,
key: props && normalizeKey(props), //props没有key属性返回null
ref: props && normalizeRef(props), //props没有ref属性返回null
scopeId: currentScopeId,
children: null,
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
}
//最后把创建的vnode对象返回
return vnode
创建vnode对象过程还是比较简单的,主要是处理了props,并且设置了shapeFlag,最后返回创建的vnode。接着往下走,
mount(rootContainer: HostElement, isHydrate?: boolean): any {
//rootContainer参数就是要挂载的Dom节点(app节点)
if (!isMounted) {
//传给createVNode的参数rootComponent就是createApp第一个参数,rootProps参数就是createApp的第二个参数
const vnode = createVNode(
rootComponent as ConcreteComponent,
rootProps
)
// store app context on the root VNode.
// this will be set on the root instance on initial mount.
//给vnode的appContext属性赋值为之前创建的context,而context.app就是最开始的时候
//创建的app对象,
vnode.appContext = context
// HMR root reload
if (__DEV__) {
context.reload = () => {
render(cloneVNode(vnode), rootContainer)
}
}
if (isHydrate && hydrate) {
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
} else {
//在这里开始进行渲染vnode
render(vnode, rootContainer)
}
isMounted = true
app._container = rootContainer
vnode.appContext = context 这行代码让vnode 拥有了 app对象,最后调用render函数进行vnode的渲染。我们下一篇文章继续分析vnode的patch过程。
如有需要可以关注作者公众号: