This article mainly understands the vue3 component mounting process based on the vue3 source code (the flow chart is attached at the end), and explains how the vue component is .vue
single-file component step by step into the real DOM and rendered to the page based on personal reading the source code. on.
Example description
Let's take a look at the writing of vue3 first by following the simple code as an example.
App.vue
<template> <h1>Hello <span class="blue">{{ name }}</span></h1> </template> <script> import { defineComponent } from 'vue'; export default defineComponent({ setup() { return { name: 'world' } } }) </script> <style scoped> .blue { color: blue; } </style>
index.js
import { createApp } from "vue"; import App from "./App.vue"; const root = document.getElementById('app'); console.log(App); createApp(App).mount(root);
index.html
<body> <div id="app"></div> <script src="/bundle.js"></script> <-- index.js经过打包 变成 bundle.js是经过打包的js --> </body>
Through these three documents vue3 can put App.vue
content to render the page, we take a look at rendering results, as shown below:
Look at the above example: we can know that the App component is passed
createApp(App)
method in vue, and then the mount(root)
method is called to mount it on root
. There will be some questions here ❓
- What does the App component look like after being compiled by Vue?
- What kind of processing goes through the createApp(App) function and then returns to the mount method?
- How does the mount method mount App components to root?
- ...
Let’s take a look at the first question. The code above prints console.log(App)
App
look specifically at 060adabb06b681 after being compiled to get the following object:
Among them, setup
setup function defined by our component, and the function code of
render
const _hoisted_1 = /*#__PURE__*/createTextVNode("Hello "); // 静态代码提升
const _hoisted_2 = { class: "blue" }; // 静态代码提升
const render = /*#__PURE__*/_withId((_ctx, _cache, $props, $setup, $data, $options) => {
return (openBlock(), createBlock("h1", null, [
_hoisted_1,
createVNode("span", _hoisted_2, toDisplayString(_ctx.name), 1 /* TEXT */)
]))
});
It is not difficult to see here that after the App.vue
file is compiled, a render
function is obtained. For details, please see online compilation . Of course, there is also the css
code, the compiled code is as follows:
var css_248z = "\n.blue[data-v-7ba5bd90] {\r\n color: blue;\n}\r\n";
styleInject(css_248z);
styleInject
method is to create a style
tag, style
tag is the content of css
, and then insert it into the head tag, the simple code is as follows,
function styleInject(css, ref) {
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
style.appendChild(document.createTextNode(css));
head.appendChild(style);
}
Rendering process
From the above example, we can see that, in fact, Vue is a single-file component that is compiled and then mounted on the page. The CSS style is inserted into the head. The general flow chart is as follows:
.vue
file in the first part is compiled into the render function, the detailed process and details are many, so I won’t go into details here. This article focuses on how the components are mounted on the page. First, we look createApp(App).mount(root);
this line of code inside createApp
is how to generate and return mount
of.
app object generation
// 简化后的代码
const createApp = ((...args) => {
const app = ensureRenderer().createApp(...args);
const { mount } = app; // 先保存app.mount方法
// 重写app.mount方法
app.mount = (containerOrSelector) => {
// 省略一些代码
};
return app;
});
createApp
function does not look at the details, jump to the last line of code, return to see a app
objects, app
which has a app.mount
function, can outside so createApp().mount()
call.
The details are also very simple. createApp
uses ensureRenderer()
delay the creation of the renderer, and executes createApp(...args)
to return a app
object. The object contains the mount
function. Through slice programming, app.mount
is rewritten, and the app object is finally returned.
Now the question comes to how app
is generated, what is in the app
object, it depends ensureRenderer
the function 060adabb06b894
const forcePatchProp = (_, key) => key === 'value';
const patchProp = () => {
// 省略
};
const nodeOps = {
// 插入标签
insert: (child, parent, anchor) => {
parent.insertBefore(child, anchor || null);
},
// 移除标签
remove: child => {
const parent = child.parentNode;
if (parent) {
parent.removeChild(child);
}
},
// 创建标签
createElement: (tag, isSVG, is, props) => {
const el = doc.createElement(tag);
// 省略了svg标签创建代码
return el;
},
// 创建文本标签
createText: text => doc.createTextNode(text),
// ...还有很多
}
const rendererOptions = extend({ patchProp, forcePatchProp }, nodeOps);
function ensureRenderer() {
return renderer || (renderer = createRenderer(rendererOptions)); // 创建渲染器
}
This function is very simple, that is, directly return createRenderer(rendererOptions)
create a renderer function.
It can be seen from this that if we don't use the renderer related to vue, we won't call ensureRenderer. When only the responsive package is used, these codes will be tree-shaking.
The parameter rendererOptions
here needs to be explained. These are related to dom
, and check operation of 060adabb06b91c, and the explanations are in the comments.
Continue to go deep into createRenderer
, from the above code const app = ensureRenderer().createApp(...args);
can know that createRendere
will return an object, the object will have the createApp
attribute, the following is the createRenderer
function
// options: dom操作相关的api
function createRenderer(options) {
return baseCreateRenderer(options);
}
function baseCreateRenderer(options, createHydrationFns) {
const patch = () => {
// 省略
};
const processText = () => {
// 省略
};
const render = (vnode, container, isSVG) => {
// 省略
}
// 此处省略很多代码
return {
render,
hydrate,
createApp: createAppAPI(render, hydrate)
};
}
See the above code, know ensureRenderer();
in is to call createAppAPI(render, hydrate)
object returned createApp
function, this time came createAppAPI(render, hydrate)
up.
let uid$1 = 0;
function createAppAPI(render, hydrate) {
return function createApp(rootComponent, rootProps = null) {
const context = createAppContext(); // 创建app上下文对象
let isMounted = false; // 挂载标识
const app = (context.app = {
_uid: uid$1++,
_component: rootComponent, // 传入的<App />组件
// 省略了一些代码
use(plugin, ...options) {}, // 使用插件相关,省略
mixin(mixin) {}, // mixin 相关,省略
component(name, component) {}, // 组件挂载, 省略
directive(name, directive) {}, // 指令挂载
mount(rootContainer, isHydrate, isSVG) {
if (!isMounted) {
// 创建vnode
const vnode = createVNode(rootComponent, rootProps);
vnode.appContext = context;
// 省略了一些代码
// 渲染vnode
render(vnode, rootContainer, isSVG);
isMounted = true;
app._container = rootContainer;
rootContainer.__vue_app__ = app;
return vnode.component.proxy;
}
},
// 省略了一些代码
});
return app;
};
}
The createApp function above returns an app object. There are component-related attributes in the object, including plugin-related, mixin-related, component mounting, and command mounting to the context. Another thing worth noting is the mount function, and the beginning createApp(App).mount(root);
is different. Remember that the above is rewritten. The mount
function will be called in the rewrite. The code is as follows
const createApp = ((...args) => {
const app = ensureRenderer().createApp(...args);
const { mount } = app; // 先缓存mount
app.mount = (containerOrSelector) => {
// 获取到容器节点
const container = normalizeContainer(containerOrSelector);
if (!container) return;
const component = app._component;
if (!isFunction(component) && !component.render && !component.template) {
// 传入的App组件,如果不是函数组件,组件没有render 组件没有template,就用容器的innerHTML
component.template = container.innerHTML;
}
// 清空容器的内容
container.innerHTML = '';
// 把组件挂载到容器上
const proxy = mount(container, false, container instanceof SVGElement);
return proxy;
};
return app;
});
The above one rewrites the mount
function, which does some things,
- Process the incoming container and generate nodes;
- Determine whether the incoming component is a function component, whether there is a
render
function in the component, whether there is atemplate
attribute in the component, if not, use the container'sinnerHTML
as the component'stemplate
; - Empty the contents of the container;
- Run the cached
mount
function to implement mounting components;
The above is the general operating process of createApp(App).mount(root);
But here only know how to generate app
, the render
function is how to generate vnode
of?, vnode
is how to mount the page? Let's continue to see what is done in the mount
Mount component mounting process
From the above, we will finally call mount
to mount the component to the page. Let's focus on what the createApp
function does in the mount
function?
function createAppAPI(render, hydrate) {
return function createApp(rootComponent, rootProps = null) {
const context = createAppContext(); // 创建app上下文对象
let isMounted = false; // 挂载标识
const app = (context.app = {
// 省略
mount(rootContainer, isHydrate, isSVG) {
if (!isMounted) {
// 创建vnode
const vnode = createVNode(rootComponent, rootProps);
vnode.appContext = context;
// 省略了一些代码
// 渲染vnode
render(vnode, rootContainer, isSVG);
isMounted = true;
app._container = rootContainer;
rootContainer.__vue_app__ = app;
return vnode.component.proxy;
}
},
// 省略了一些代码
});
return app;
};
}
In the mount function, the main thing is:
- Create a vnode through
const vnode = createVNode(rootComponent, rootProps);
- In
vnode
mounted onappContext
. render(vnode, rootContainer, isSVG);
passesvnode
androotContainer
(container nodes) as parameters to therender
function for execution.- Set the mount flag
isMounted = true;
. - ...And so on other attributes are mounted.
vnode
the process of creating _createVNode
will eventually be called rootComponent
(which is our compiled object) will be passed in vnode
object will be generated.
const createVNodeWithArgsTransform = (...args) => {
return _createVNode(...(args));
};
function _createVNode(type, props = null, children = null, patchFlag = 0, dynamicProps = null, isBlockNode = false) {
// 省略
// 将vnode类型信息编码
const shapeFlag = isString(type)
? 1 /* ELEMENT */
: isSuspense(type)
? 128 /* SUSPENSE */
: isTeleport(type)
? 64 /* TELEPORT */
: isObject(type)
? 4 /* STATEFUL_COMPONENT */
: isFunction(type)
? 2 /* FUNCTIONAL_COMPONENT */
: 0;
const vnode = {
__v_isVNode: true,
["__v_skip" /* SKIP */]: true,
type,
props,
key: props && normalizeKey(props),
ref: props && normalizeRef(props),
scopeId: currentScopeId,
slotScopeIds: null,
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
};
normalizeChildren(vnode, children);
// 省略
if (!isBlockNode && currentBlock && (patchFlag > 0 || shapeFlag & 6 /* COMPONENT */) && patchFlag !== 32) {
currentBlock.push(vnode);
}
return vnode;
}
const createVNode = createVNodeWithArgsTransform;
The above process, need to pay attention rootComponent
, is compiled above us App
, rootComponent
roughly the following format (not sure you can look back at it.)
rootComponent = {
render() {},
setup() {}
}
The process of creating vnode
- First judge
shapeFlag
, wheretype == rootComponent
is an object, you know that at this timeshapeFlag = 4
- Create a
vnode
object, wheretype == rootComponent
- Then
normalizeChildren(vnode, children)
, there is nochildren
, skip - Return
vnode
You can get a vnode
object by creating createVNode
, and then use this vnode
to render render(vnode, rootContainer, isSVG);
const render = (vnode, container, isSVG) => {
// vnode是空的
if (vnode == null) {
if (container._vnode) {
// 卸载老vnode
unmount(container._vnode, null, null, true);
}
} else {
// container._vnode 一开始是没有的,所以n1 = null
patch(container._vnode || null, vnode, container, null, null, null, isSVG);
}
flushPostFlushCbs();
container._vnode = vnode; // 节点上挂载老的vnode
};
As you can see, render
function first determines whether the incoming vnode
is null
. If it is null
and the container node mounts the old vnode
vnode
needs to be uninstalled, because the new vnode
is gone, if not null
, Execute the patch
function. The process is very simple. Look directly at the patch
function below:
const patch = (n1, n2, container, anchor = null, parentComponent = null, parentSuspense = null, isSVG = false, slotScopeIds = null, optimized = false) => {
// 省略
const { type, ref, shapeFlag } = n2;
switch (type) {
case Text:
processText(n1, n2, container, anchor);
break;
case Comment:
processCommentNode(n1, n2, container, anchor);
break;
case Static:
if (n1 == null) {
mountStaticNode(n2, container, anchor, isSVG);
} else {
patchStaticNode(n1, n2, container, isSVG);
}
break;
case Fragment:
processFragment(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
break;
default:
if (shapeFlag & 1 /* ELEMENT */) {
processElement(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
} else if (shapeFlag & 6 /* COMPONENT */) {
processComponent(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
} else if (shapeFlag & 64 /* TELEPORT */) {
type.process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized, internals);
} else if (shapeFlag & 128 /* SUSPENSE */) {
type.process(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized, internals);
} else {
warn('Invalid VNode type:', type, `(${typeof type})`);
}
}
// set ref
if (ref != null && parentComponent) {
setRef(ref, n1 && n1.ref, parentSuspense, n2);
}
};
patch
execution process:
- First look at our parameters n1 = null (old vnode), n2 = vnode (new vnode), container = root, it was the old vnode at the beginning;
- Get the type and shapeFlag of n2, at this time our
type = { render, setup }
, shapeFlag = 4 - After switch ... case judgment, we will come
else if (shapeFlag & 6 /* COMPONENT */)
this branch, because4 & 6 = 4
; processComponent(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
it over to 060adabb06bf32 to process our components.
Let's take a look directly at the function processComponent
const processComponent = (n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized) => {
n2.slotScopeIds = slotScopeIds;
if (n1 == null) {
// keep-alive组件处理
if (n2.shapeFlag & 512 /* COMPONENT_KEPT_ALIVE */) {
parentComponent.ctx.activate(n2, container, anchor, isSVG, optimized);
} else {
mountComponent(n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized);
}
} else {
updateComponent(n1, n2, optimized);
}
};
The processComponent
function has two main functions
- When n1 == null, call function
mountComponent
to mount the component - When n1 is not null and there are new and old vnodes, call
updateComponent
to update the components
We are talking about the mounting process here, just look at mountComponent
const mountComponent = (initialVNode, container, anchor, parentComponent, parentSuspense, isSVG, optimized) => {
// 第一步
const instance = (initialVNode.component = createComponentInstance(initialVNode, parentComponent, parentSuspense));
// 省略
// 第二步
setupComponent(instance);
// 省略
// 第三步
setupRenderEffect(instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized);
};
After removing some irrelevant code, we see that mountComponent
is actually very simple. It creates a component instance
, and then calls two functions setupComponent
and setupRenderEffect
.
- setupComponent: The main function is to do some component initialization work or something
- setupRenderEffect: It is equivalent to the rendering watcher of vue2
1、createComponentInstance
But let's first look at how the component creates an instance createComponentInstance
function createAppContext() {
return {
app: null,
config: {}, // 省略
mixins: [],
components: {},
directives: {},
provides: Object.create(null)
};
}
const emptyAppContext = createAppContext();
let uid$2 = 0;
function createComponentInstance(vnode, parent, suspense) {
const type = vnode.type;
// inherit parent app context - or - if root, adopt from root vnode
const appContext = (parent ? parent.appContext : vnode.appContext) || emptyAppContext;
const instance = {
uid: uid$2++,
vnode,
type,
parent,
appContext,
root: null,
subTree: null,
update: null,
render: null
withProxy: null,
components: null,
// props default value
propsDefaults: EMPTY_OBJ,
// state
ctx: EMPTY_OBJ,
data: EMPTY_OBJ,
props: EMPTY_OBJ,
attrs: EMPTY_OBJ,
// 省略
};
{
instance.ctx = createRenderContext(instance); // 生成一个对象 { $el, $data, $props, .... }
}
instance.root = parent ? parent.root : instance;
instance.emit = emit.bind(null, instance);
return instance;
}
Don't let so many properties be createComponentInstance
. In fact, 060adabb06c25b is to initialize a instance
object, and then return this instance
. It's that simple.
instance.ctx = createRenderContext(instance); There are many initialization properties in this object. Many properties are mounted on instance.ctx through createRenderContext. They are all our common $el, $data, $props, $attr, $emit,..., this has nothing to do with our initial rendering, don't be optimistic about it.
2、setupComponent
The next step is to put the generated instance
object into the setupComponent
function as a parameter to run.
function setupComponent(instance) {
const { props, children } = instance.vnode;
const isStateful = isStatefulComponent(instance); // instance.vnode.shapeFlag & 4
// 省略
const setupResult = isStateful
? setupStatefulComponent(instance)
: undefined;
return setupResult;
}
Look at setupComponent
does is to determine whether our vnode.shapeFlag
is a state component. From the above, we know that our vnode.shapeFlag == 4
, so the next step will call setupStatefulComponent(instance)
, then return the value setupResult, and finally return. Have a look at setupStatefulComponent
function setupStatefulComponent(instance, isSSR) {
const Component = instance.type;
// 省略
const { setup } = Component;
if (setup) {
// 省略
// 调用setup
const setupResult = callWithErrorHandling(setup, instance, 0, [shallowReadonly(instance.props), setupContext]);
// 省略
handleSetupResult(instance, setupResult, isSSR);
} else {
finishComponentSetup(instance, isSSR);
}
}
function callWithErrorHandling(fn, instance, type, args) {
let res;
try {
res = args ? fn(...args) : fn(); // 调用传进来的setup函数
} catch (err) {
handleError(err, instance, type);
}
return res;
}
function handleSetupResult(instance, setupResult, isSSR) {
// 省略
instance.setupState = proxyRefs(setupResult); // 相当于代理挂载操作 instance.setupState = setupResult
{
exposeSetupStateOnRenderContext(instance); // 相当于代理挂载操作 instance.ctx = setupResult
}
finishComponentSetup(instance, isSSR);
}
setupStatefulComponent
function is to call the custom setup function of our component and return a setupResult object. According to what is written above, the setup returns an object:
setupResult = {
name: 'world'
}
Then when I run handleSetupResult
, I see that there is actually no work in it, so I call finishComponentSetup(instance, isSSR);
function finishComponentSetup(instance, isSSR) {
const Component = instance.type;
if (!instance.render) {
instance.render = (Component.render || NOOP);
}
// support for 2.x options
{
currentInstance = instance;
pauseTracking();
applyOptions(instance, Component);
resetTracking();
currentInstance = null;
}
}
So far all the setupComponent
processes have been completed, that is, call the setup
function, and then mount many attribute agents in instance
Including the important instance.ctx
, they all represent setupResult
. Let's look at the third step below:
3、setupRenderEffect
const setupRenderEffect = (instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized) => {
// create reactive effect for rendering
instance.update = effect(function componentEffect() {
if (!instance.isMounted) {
let vnodeHook;
const { el, props } = initialVNode;
const subTree = (instance.subTree = renderComponentRoot(instance));
// 省略
patch(null, subTree, container, anchor, instance, parentSuspense, isSVG);
initialVNode.el = subTree.el;
// 省略
instance.isMounted = true;
// 生路
} else {
// 更新过程
}
}, createDevEffectOptions(instance) );
};
The function in effect in setupRenderEffect will be executed once. Then go to the function inside.
- First judge whether it has been mounted
instance.isMounted
If it has not been mounted, perform the mount operation, and if it has been mounted, perform the update operation. - Generate subTree by
renderComponentRoot
- Call path to mount recursively
- Update the instance.isMounted logo
Generate subTree
renderComponentRoot
generates the subTree, which is actually the render
function App
function renderComponentRoot(instance) {
const { type: Component, vnode, proxy, withProxy, props, propsOptions: [propsOptions], slots, attrs, emit, render, renderCache, data, setupState, ctx } = instance;
let result;
try {
let fallthroughAttrs;
if (vnode.shapeFlag & 4) {
// 拿到instance.proxy
const proxyToUse = withProxy || proxy;
// 调用install.render函数,并改变this的指向
result = normalizeVNode(render.call(proxyToUse, proxyToUse, renderCache, props, setupState, data, ctx));
} else {
// 函数组件挂载
}
// 省略
} catch (err) {
blockStack.length = 0;
handleError(err, instance, 1 /* RENDER_FUNCTION */);
result = createVNode(Comment);
}
setCurrentRenderingInstance(prev);
return result;
}
renderComponentRoot
main function is to call install.render
function, and change this
pointing to instance.proxy
. We know from the above,
- instance.proxy has a data proxy, that is, access instance.proxy.name ==='world'
result = normalizeVNode(render.call(proxyToUse, proxyToUse, renderCache, props, setupState, data, ctx));
render
function here is the redner function compiled by our app.const _hoisted_1 = /*#__PURE__*/createTextVNode("Hello "); // 静态代码提升 const _hoisted_2 = { class: "blue" }; // 静态代码提升 const render = /*#__PURE__*/_withId((_ctx, _cache, $props, $setup, $data, $options) => { return (openBlock(), createBlock("h1", null, [ _hoisted_1, createVNode("span", _hoisted_2, toDisplayString(_ctx.name), 1 /* TEXT */) ])) });
Then execute the render function, and pass in the ctx, props, setupState and other parameters of the instance. Let's take a look at the execution of the render function is to execute an openBlock() first
const blockStack = []; // block栈
let currentBlock = null; // 当前的block
function openBlock(disableTracking = false) {
blockStack.push((currentBlock = disableTracking ? null : []));
}
function closeBlock() {
blockStack.pop();
currentBlock = blockStack[blockStack.length - 1] || null;
}
As can be seen from the above, to execute a openBlock()
is to create a new data, assign it to currentBlock
, and then push
to blockStack
, so the current
blockStack = [[]]
currentBlock = []
Then the receipt first executes createVNode("span", _hoisted_2, toDisplayString(_ctx.name), 1)
, which is the vnode that creates the span node. toDisplayString(_ctx.name)
takes the value of toDisplayString(_ctx.name) === 'world'
. As mentioned above on createVNode, the object created by createVNode is probably
// 详细代码看前文有写
function createVNode () {
const vnode = {
appContext: null,
children: "world",
props: {class: "blue"},
type: "span",
// 省略很多
}
// 这里有判断,如果是动态block就push
// currentBlock.push(vnode);
}
// 执行过后
// currentBlock = [span-vnode]
// blockStack = [[span-vnode]]
Then execute createBlock("h1", null, [_hoisted_1, span-vnode])
function createBlock(type, props, children, patchFlag, dynamicProps) {
const vnode = createVNode(type, props, children, patchFlag, dynamicProps, true);
// 保存动态的block,下次更新只更新这个,
vnode.dynamicChildren = currentBlock || EMPTY_ARR;
// 清空当前的block
closeBlock();
// 如果currentBlock还有的话就继续push到currentBlock
if (currentBlock) {
currentBlock.push(vnode);
}
return vnode;
}
createBlock also calls createVNode to generate a h1 vnode, and then execute vnode.dynamicChildren = currentBlock, clear the block and return to the vnode. It is roughly as follows
vnode = {
"type": "h1",
"children": [
{
"children": "Hello ",
"shapeFlag": 8,
"patchFlag": 0,
},
{
"type": "span",
"props": {
"class": "blue"
},
"children": "world",
"shapeFlag": 9,
"patchFlag": 1,
}
],
"shapeFlag": 17,
"patchFlag": 0,
"dynamicChildren": [
{
"type": "span",
"props": {
"class": "blue"
},
"children": "world",
"shapeFlag": 9,
"patchFlag": 1,
}
],
"appContext": null
}
patch subTree
Then call patch(null, subTree, container, anchor, instance, parentSuspense, isSVG);
, and then to patch, which will type==='h1', and call the patch in it.
const { type, ref, shapeFlag } = n2;
if (shapeFlag & 1 /* ELEMENT */) {
processElement(n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
}
// ---
const processElement = (n1, n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized) => {
isSVG = isSVG || n2.type === 'svg';
if (n1 == null) {
mountElement(n2, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
} else {
patchElement(n1, n2, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized);
}
};
Calling processElement will call mountElement to mount
const mountElement = (vnode, container, anchor, parentComponent, parentSuspense, isSVG, slotScopeIds, optimized) => {
let el;
let vnodeHook;
const { type, props, shapeFlag, transition, patchFlag, dirs } = vnode;
{
// 创建h1元素
el = vnode.el = hostCreateElement(vnode.type, isSVG, props && props.is, props);
// chilren是文本的
if (shapeFlag & 8) {
hostSetElementText(el, vnode.children);
} else if (shapeFlag & 16) { // chilren是数组的
// 递归挂载children
mountChildren(vnode.children, el, null, parentComponent, parentSuspense, isSVG && type !== 'foreignObject', slotScopeIds, optimized || !!vnode.dynamicChildren);
}
// 省略
// 把创建的h1挂载到root上
hostInsert(el, container, anchor);
};
mountElemen
t Mount the process, display the creation element, and then determine whether the child element is an array or a text. If the child is a text, create the text directly and insert it into the element. If it is an array, call the mountChildren function.
const mountChildren = (children, container, anchor, parentComponent, parentSuspense, isSVG, optimized, slotScopeIds, start = 0) => {
for (let i = start; i < children.length; i++) {
patch(null, child, container, anchor, parentComponent, parentSuspense, isSVG, optimized, slotScopeIds);
}
};
mountChildren
function, all the children are looped, and then patch
called one by one to insert.
Finally, insert the tree node el generated after the path
hostInsert(el, container, anchor);
insert into the root. Then it is rendered to the page.
Vue3 component mounting process
Criticisms and corrections are welcome for bad writing
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。