Vue 3.0 source code learning 1 --- rendering process of components

chonglingliu 2021-08-08 17:58:38
vue source code learning rendering


Vue Easy to use , Just need a simple documentation to start developing . Although I have always used Vue 2.0 Project development experience , Before, I only knew a little about the core code logic , I haven't fully read Vue 2.0 Source code .Vue 3.0 After the release, I also have some Vue 3.0 Project experience , Follow the vine and touch the melon to learn Vue 3.0 Source code , Learn coding skills from experts , In order to be more comfortable in the project .

because Vue 3.0 Used TypeScript Refactored , So before reading this series, you need to know TypeScript Understand the basic grammar , In addition, you need to understand recursive calls and function coritization .

Vue 3.0 The series is expected to have 30 The length of the article is about , Each article will only focus on the knowledge points involved , This analysis will be clearer , Otherwise, it will be very chaotic .

If you don't want to see the complex analysis process , You can directly look at the final picture summary .

We often use the following code :

import { createApp } from "vue";
import App from "./App.vue";
createApp(App).mount("#app");
 Copy code 

App It's just one. Vue Components , The browser does not recognize this component , I don't know how to render this component , that Vue How will App The component is rendered as real DOM Well ? Let's study this article Vue Convert components into real DOM The rendering process .

Application initialization

createApp Entry function

export const createApp = ((...args) => {
// 1.
const app = ensureRenderer().createApp(...args)
// 2.
const { mount } = app
app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
// Omit ...
}
return app
}) as CreateAppFunction<Element>
 Copy code 

createApp The entry function mainly does two things :

  • Use ensureRenderer().createApp() establish app object
  • rewrite app Of mount Method .
establish app object
Create a render object

The renderer is a platform with core rendering logic JS object .Vue It can be used as cross platform rendering , So it doesn't have to be DOM Rendering of .

use ensureRenderer() Create a renderer object :

// Renderer object
let renderer: Renderer<Element | ShadowRoot> | HydrationRenderer
function ensureRenderer() {
return (
renderer ||
(renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions))
)
}
 Copy code 

Let's first judge whether there is a renderer , No need to create a renderer , It belongs to a delay creation method , Create renderers only when needed .

When the renderer is initialized, a rendering configuration parameter will be passed in ---rendererOptions, It defines the attribute processing method , DOM Operation method, etc .

export interface RendererOptions<
HostNode = RendererNode,
HostElement = RendererElement
> {
// Handle Prop,attributes etc.
patchProp(
el: HostElement,
key: string,
prevValue: any,
nextValue: any,
isSVG?: boolean,
prevChildren?: VNode<HostNode, HostElement>[],
parentComponent?: ComponentInternalInstance | null,
parentSuspense?: SuspenseBoundary | null,
unmountChildren?: UnmountChildrenFn
): void
// Insert node
insert(el: HostNode, parent: HostElement, anchor?: HostNode | null): void
// Omit ...
}
 Copy code 

Although developers do not need to operate directly DOM, But you can guess that all the components will be converted into DOM. This configuration parameter of the renderer contains direct operations DOM Methods , Therefore, it is a very critical configuration .

createRenderer Method internal direct call baseCreateRenderer Method :

export function createRenderer<
HostNode = RendererNode,
HostElement = RendererElement
>(options: RendererOptions<HostNode, HostElement>) {
return baseCreateRenderer<HostNode, HostElement>(options)
}
 Copy code 

baseCreateRenderer The code of the method is as follows :

function baseCreateRenderer(
options: RendererOptions,
createHydrationFns?: typeof createHydrationFunctions
): any {
// 1.
const {
insert: hostInsert,
remove: hostRemove,
patchProp: hostPatchProp,
createElement: hostCreateElement,
createText: hostCreateText,
createComment: hostCreateComment,
setText: hostSetText,
setElementText: hostSetElementText,
parentNode: hostParentNode,
nextSibling: hostNextSibling,
setScopeId: hostSetScopeId = NOOP,
cloneNode: hostCloneNode,
insertStaticContent: hostInsertStaticContent
} = options
// 2.
const patch: PatchFn = (
n1,
n2,
...
) => {}
const processComponent = (
n1: VNode | null,
n2: VNode,
...
) => {}
// Omit many methods ...
const render: RootRenderFunction = (vnode, container, isSVG) => {
if (vnode == null) {
if (container._vnode) {
unmount(container._vnode, null, null, true)
}
} else {
patch(container._vnode || null, vnode, container, null, null, null, isSVG)
}
flushPostFlushCbs()
container._vnode = vnode
}
// 3.
return {
render,
hydrate,
createApp: createAppAPI(render, hydrate)
}
}
 Copy code 
  1. First deconstruct the incoming RendererOptions object options, Then modify the operation DOM Parameter name of the method ;
  2. Many rendering related methods are defined , The most important one is render Method .render Called an important patch Method ,patch Method will call other methods , For example, components deal with related processComponent Method . If a method needs to be operated DOM, That would call RendererOptions object options The method in .
  3. Finally, it returns a containing render and createApp Object of method .hydrate by undefined.

above createApp The value of is createAppAPI Function return value of , What does it do ?

export function createAppAPI<HostElement>(
render: RootRenderFunction,
hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
// 1.
return function createApp(rootComponent, rootProps = null) {
// 2.
if (rootProps != null && !isObject(rootProps)) {
__DEV__ && warn(`root props passed to app.mount() must be an object.`)
rootProps = null
}
// 2.
const context = createAppContext()
// 3.
const installedPlugins = new Set()
// 4.
let isMounted = false
// 5.
const app: App = (context.app = {
_uid: uid++,
_component: rootComponent as ConcreteComponent,
_props: rootProps,
_container: null,
_context: context,
_instance: null,
version,
get config() {
return context.config
},
set config(v) {
},
use(plugin: Plugin, ...options: any[]) {
if (installedPlugins.has(plugin)) {
__DEV__ && warn(`Plugin has already been applied to target app.`)
} else if (plugin && isFunction(plugin.install)) {
installedPlugins.add(plugin)
plugin.install(app, ...options)
} else if (isFunction(plugin)) {
installedPlugins.add(plugin)
plugin(app, ...options)
}
return app
},
mixin(mixin: ComponentOptions) {
if (__FEATURE_OPTIONS_API__) {
if (!context.mixins.includes(mixin)) {
context.mixins.push(mixin)
}
return app
},
component(name: string, component?: Component): any {
if (!component) {
return context.components[name]
}
context.components[name] = component
return app
},
directive(name: string, directive?: Directive) {
if (!directive) {
return context.directives[name] as any
}
context.directives[name] = directive
return app
},
mount(
rootContainer: HostElement,
isHydrate?: boolean,
isSVG?: boolean
): any {
if (!isMounted) {
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
if (isHydrate && hydrate) {
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
} else {
render(vnode, rootContainer, isSVG)
}
isMounted = true
app._container = rootContainer
// for devtools and telemetry
;(rootContainer as any).__vue_app__ = app
return vnode.component!.proxy
}
},
unmount() {
if (isMounted) {
render(null, app._container)
delete app._container.__vue_app__
}
},
provide(key, value) {
context.provides[key as string] = value
return app
}
})
if (__COMPAT__) {
installAppCompatProperties(app, context, render)
}
return app
}
}
 Copy code 
  1. createAppAPI The result of the implementation of createApp Method , Method returns a App object ;
  • Be careful : Don't confuse Vue In the framework App And developer defined App, Developer defined App In fact, it is the incoming parameter of the function rootComponent This root component , rootProps It is related to the root component passed in by the developer props.
  1. Check props Legitimacy --- If not for null, It must be the object ;
  2. Create a AppContext object context, It contains a app Attribute points to App object ,plugin,provide,directive and component And so on are mounted on this object ;
  3. installedPlugins Used to store installed Plugin;
  4. isMounted Set as false, Mark as unmounted ;
  5. It generates a app object , It contains some properties :_component Defined for developers App The root component ,_props Related to the root component passed in for the developer props,_context As defined above AppContext object context. It also contains some methods ,use How to install plug-ins ,mixin Mixing method ,component Define component methods globally ,directive Command method ,mount Mounting method ,unmount Uninstall method ,provide How to share data .
  • Here we can see Vue 3.0 A major change is that these methods have changed from before Vue 2.0 Of The global method Turned into app Object methods .
  • One of the important ways is mount Mounting method , Specific functions will be introduced later . This method holds render rendering method , So call mount Method does not need to pass the renderer , This is a The function is coriolized An important skill of .
rewrite mount Method

We go back to createApp Entry function , The last section analyzed const app = ensureRenderer().createApp(...args) The implementation details of this line of code , Let's analyze the next process :

export const createApp = ((...args) => {
const app = ensureRenderer().createApp(...args)
// 1.
const { mount } = app
app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
// 2.
const container = normalizeContainer(containerOrSelector)
if (!container) return
// 3.
const component = app._component
// 4.
container.innerHTML = ''
// 5.
const proxy = mount(container, false, container instanceof SVGElement)
if (container instanceof Element) {
// 6.
container.removeAttribute('v-cloak')
// 7.
container.setAttribute('data-v-app', '')
}
return proxy
}
return app
}) as CreateAppFunction<Element>
 Copy code 
  1. Deconstructed app Medium mount Method , Then rewrite app Medium mount Method ;
  2. Standardized containers , If the container is a string , It will call document.querySelector(container) Find the corresponding DOM node , This is what we can bring in "#app" As a container ;
  3. take app._component Assign a value to component object , This object is actually provided by the developer App The root component ;
  4. Clear the contents of the container , That is, if the container has child nodes, it will be cleared ;
  5. Call... In the framework App Of course mount Method , namely createAppAPI Methods app Object's mount, Mount , This process is described in detail below ;
  • Take a look at mount Method call :mount(container, true, container instanceof SVGElement), The next one is containers , The second parameter is true, The third parameter is false.
  1. Remove the of the container v-cloak Attribute, This property can be associated with {display:none} Solve the page flicker problem when the network is slow ;
  2. Container plus data-v-app Attribute, This property has no practical effect , It's just a sign ;

We are here App Of mount Method :

mount(
rootContainer: HostElement,
isHydrate?: boolean,
isSVG?: boolean
): any {
if (!isMounted) {
// 1.
const vnode = createVNode(
rootComponent as ConcreteComponent,
rootProps
)
// 2.
vnode.appContext = context
// 3.
render(vnode, rootContainer, isSVG)
// 4.
isMounted = true
// 5.
app._container = rootContainer
return vnode.component!.proxy
}
}
 Copy code 
  1. First of all, according to the rootComponent and rootProps Create the corresponding VNode object vnode;
  2. to vnode Of appContext Assign to create app Is initialized context, This context As mentioned above , It can be hung in plug-ins and other contents , There are also app Attribute points to app;
  3. Rendering vnode, This will be highlighted below , Don't go deep for a while ;
  4. Mark as mounted ;
  5. to app Of _container Assign to parent container ;

App Of mount There are two important logic in the process : establish VNode Of createVNode And rendering VNode Of render(vnode, rootContainer, isSVG), Next, let's introduce them .

establish VNode

VNode Is the front-end development description DOM Of JS object , It can describe different types of nodes , It can be a component node , It can also be an ordinary element node , There are many other types of nodes .DOM It's a tree structure ,VNode It's also a tree structure .

VNode and Flutter Medium Widget similar , Just a description tree of node information .Flutter The real rendering tree in is RenderObject Trees , and Vue The rendering tree in front-end development is DOM Trees .

Flutter The cross platform logic is the rendering logic, which varies according to different platforms ,Vue be based on VNode It's also cross platform , for example Weex and uniapp Is the use Vue Realize multi platform development .

createVNode

createVNode The inside points to _createVNode function :

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 {
// 1.
if (isVNode(type)) {
const cloned = cloneVNode(type, props, true /* mergeRef: true */)
if (children) {
normalizeChildren(cloned, children)
}
return cloned
}
if (props) {
// 2.
props = guardReactiveProps(props)!
// 3.
let { class: klass, style } = props
if (klass && !isString(klass)) {
props.class = normalizeClass(klass)
}
// 4.
if (isObject(style)) {
if (isProxy(style) && !isArray(style)) {
style = extend({}, style)
}
props.style = normalizeStyle(style)
}
}
// 5.
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
// 6.
return createBaseVNode(
type,
props,
children,
patchFlag,
dynamicProps,
shapeFlag,
isBlockNode,
true
)
}
 Copy code 
  1. If the incoming type The parameter itself is VNode, Then make a copy and standardize the child nodes and return ;
  2. If there is props Parameters are standardized , If it's a responsive object, copy it , Otherwise, we will not deal with it ; Responsive object replication is to avoid other side effects caused by modifying responsive data ;
  3. Yes class Standardize , If it is a string, it will directly return the corresponding original value , If it is an array, standardize each element of the array , If it is an object, get the attribute value of the object as true Then separate with a space ; Reference documents
  4. Yes style Standardize , If it is a string or object, return the original value directly , If it's data, it's for each element in the array key and value Combine into style object ; Reference documents ;
  5. according to type The type of is encoded as ShapeFlags, If the incoming is Object, It's encoded as ShapeFlags.STATEFUL_COMPONENT;
  6. Last call createBaseVNode Make a real creation 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
) {
// 1.
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) {
// 2.
normalizeChildren(vnode, children)
} else if (children) {
vnode.shapeFlag |= isString(children)
? ShapeFlags.TEXT_CHILDREN
: ShapeFlags.ARRAY_CHILDREN
}
return vnode
}
 Copy code 
  1. Generate vnode object , Contains type and props Parameters ;
  2. Standardize child nodes --- Assign child nodes to vnode Object's children attribute , According to the of child nodes ShapeFlags Before modifying points VNode Of ShapeFlags;

Rendering VNode

So let's see mount Methods render(vnode, rootContainer, isSVG) The logic of :

 const render: RootRenderFunction = (vnode, container, isSVG) => {
// 1.
if (vnode == null) {
if (container._vnode) {
unmount(container._vnode, null, null, true)
}
} else {
// 2.
patch(container._vnode || null, vnode, container, null, null, null, isSVG)
}
flushPostFlushCbs()
// 3.
container._vnode = vnode
}
 Copy code 
  1. If vnode by null, Unload the parent container _vnode object ;
  2. If vnode Not for null, Call patch Method , for the first time container._vnode by null,vnode For developers App Generated VNode,container by #appDOM Elements ;
  3. Of the parent container _vnode Set to vnode;

Mount and update VNode

 // 1.
const patch: PatchFn = (
n1,
n2,
container,
anchor = null,
parentComponent = null,
parentSuspense = null,
isSVG = false,
slotScopeIds = null,
optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren
) => {
// 2.
if (n1 === n2) {
return
}
// 3.
if (n1 && !isSameVNodeType(n1, n2)) {
anchor = getNextHostNode(n1)
unmount(n1, parentComponent, parentSuspense, true)
n1 = null
}
if (n2.patchFlag === PatchFlags.BAIL) {
optimized = false
n2.dynamicChildren = null
}
// 4.
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 if (__DEV__) {
patchStaticNode(n1, n2, container, isSVG)
}
break
case Fragment:
processFragment(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
break
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
processElement(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
} else if (shapeFlag & ShapeFlags.COMPONENT) {
processComponent(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
} else if (shapeFlag & ShapeFlags.TELEPORT) {
;(type as typeof TeleportImpl).process(
n1 as TeleportVNode,
n2 as TeleportVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized,
internals
)
} else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
;(type as typeof SuspenseImpl).process(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized,
internals
)
} else if (__DEV__) {
warn('Invalid VNode type:', type, `(${typeof type})`)
}
}
if (ref != null && parentComponent) {
setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2)
}
}
 Copy code 
  1. patch The first argument to the method is old VNode, The second parameter is new VNode, The third parameter is the parent node DOM Elements ;
  2. If new and old VNode If it is the same object, there is no need to operate , Go straight back to ;
  3. If new and old VNode Of VNodeType atypism , Just uninstall the old VNode, Put the old VNode empty , Mount the new VNode;
  4. According to the new VNode Of type and shapeFlag, If it is a component, enter processComponent Method , If the normal node enters processElement Method ;

processComponent Processing components

 const processComponent = (
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
slotScopeIds: string[] | null,
optimized: boolean
) => {
n2.slotScopeIds = slotScopeIds
if (n1 == null) {
if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
;(parentComponent!.ctx as KeepAliveContext).activate(
n2,
container,
anchor,
isSVG,
optimized
)
} else {
mountComponent(
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
)
}
} else {
updateComponent(n1, n2, optimized)
}
}
 Copy code 

If the old VNode by null, It's not keep-alive Components , Call mountComponent Method to mount components .

mountComponent Mount components

 const mountComponent: MountComponentFn = (
initialVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
) => {
// 1.
const compatMountInstance =
__COMPAT__ && initialVNode.isCompatRoot && initialVNode.component
const instance: ComponentInternalInstance =
compatMountInstance ||
(initialVNode.component = createComponentInstance(
initialVNode,
parentComponent,
parentSuspense
))
// 2.
if (!(__COMPAT__ && compatMountInstance)) {
setupComponent(instance)
}
// 3.
setupRenderEffect(
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
)
}
 Copy code 
  1. Create a component instance ComponentInternalInstance object compatMountInstance, We see that the created instance receives vnode And the parent container node DOM Elements , Other properties are initialized by default ;
  2. Setting component instances is mainly from the held vnode In order to get props and slot And other properties, and then set the corresponding properties , In addition, if the developer uses it in the component setup Method , So this one setup Methods are also called , Hold the properties and methods ;
  3. According to the component instance compatMountInstance,vnode And the parent container node DOM Element to create a rendering function with side effects ;

setupRenderEffect - Create a rendering function with side effects

 const setupRenderEffect: SetupRenderEffectFn = (
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
) => {
// 1.
const componentUpdateFn = () => {
if (!instance.isMounted) {
const subTree = (instance.subTree = renderComponentRoot(instance))
patch(
null,
subTree,
container,
anchor,
instance,
parentSuspense,
isSVG
)
initialVNode.el = subTree.el
}
}
// 2.
const effect = new ReactiveEffect(
componentUpdateFn,
() => queueJob(instance.update),
instance.scope // track it in component's effect scope
)
// 3.
const update = (instance.update = effect.run.bind(effect) as SchedulerJob)
update.id = instance.uid
// allowRecurse
// #1801, #2043 component render effects should allow recursive updates
effect.allowRecurse = update.allowRecurse = true
// 4.
update()
}
 Copy code 
  1. Created an initial or updated rendering componentUpdateFn Method , In fact, they all call patch Method , The difference is that the first parameter of the first rendering is null, When updating rendering, the first parameter is old VNode;
  • componentUpdateFn In the patch One feature of the method is that it is passed instance, Namely the ComponentInternalInstance Object is passed in as a parameter patch Method .
  1. Created a ReactiveEffect object effect, The first parameter of this object fn It's a function , When effect call update Method fn Function call for ;
  2. take effect Of run The function is assigned to instance Of update attribute , And give update Mark a id;
  3. perform update(), Which is execution componentUpdateFn Method ,componentUpdateFn Method will call patch Method , Recursion .

patch In the end, it must deal with ordinary elements VNode, So next, let's look at the common elements VNode How is it handled .

processElement Handle common element nodes

 const processElement = (
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
slotScopeIds: string[] | null,
optimized: boolean
) => {
isSVG = isSVG || (n2.type as string) === 'svg'
if (n1 == null) {
mountElement(
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
} else {
}
}
 Copy code 

processElement stay n1 by null It's time to enter mountElement Mount element method .

mountElement Mount element node

const mountElement = (
vnode: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
slotScopeIds: string[] | null,
optimized: boolean
) => {
let el: RendererElement
let vnodeHook: VNodeHook | undefined | null
const { type, props, shapeFlag, transition, patchFlag, dirs } = vnode
// 1.
el = vnode.el = hostCreateElement(
vnode.type as string,
isSVG,
props && props.is,
props
)
// 2.
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
hostSetElementText(el, vnode.children as string)
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren(
vnode.children as VNodeArrayChildren,
el,
null,
parentComponent,
parentSuspense,
isSVG && type !== 'foreignObject',
slotScopeIds,
optimized
)
}
if (dirs) {
invokeDirectiveHook(vnode, null, parentComponent, 'created')
}
// props
if (props) {
for (const key in props) {
if (key !== 'value' && !isReservedProp(key)) {
hostPatchProp(
el,
key,
null,
props[key],
isSVG,
vnode.children as VNode[],
parentComponent,
parentSuspense,
unmountChildren
)
}
}
}
// 4.
hostInsert(el, container, anchor)
}
 Copy code 
  1. adopt hostCreateElement establish DOM Element nodes , We introduced hostCreateElement Is the configuration parameter passed in when creating the renderer , The essence is to call doc.createElement(tag, is ? { is } : undefined);
  2. Process child nodes : If the child node is text , Finally, it actually calls el.textContent = text; If the child node is an array , call mountChildren Method , Each child node of the array calls patch Mount child nodes ;
  3. Judgment if any props, adopt hostPatchProp How to give this DOM Node settings related class,style,event Equal attribute .
  4. Will create the DOM The elements are attached to container On .

We've seen... First path Depth-first traversal , Then mount the created DOM Elements , That is, mount DOM The element starts from the child node of the tree and then mounts step by step until the root node , Finally, the whole is mounted to #app On this element . So far the whole DOM The rendering is complete .

summary

createApp

createApp

mount

mount

版权声明
本文为[chonglingliu]所创,转载请带上原文链接,感谢
https://qdmana.com/2021/08/20210808175447996d.html

  1. HTML + CSS + JavaScript to achieve cool Fireworks (cloud like particle text 3D opening)
  2. HTML + CSS + JavaScript realizes 520 advertising love tree (including music), which is necessary for programmers to express themselves
  3. Solve the problem of Web front-end deployment server (it can be deployed online without a server)
  4. HTML + CSS + JS make wedding countdown web page template (520 / Tanabata Valentine's Day / programmer advertisement)
  5. What else can driverless minibus do besides "Park connection"?
  6. Cloud native leads the era of all cloud development
  7. NRM mirror source management tool
  8. Bring it to you, flex Jiugong
  9. Lolstyle UI component development practice (II) -- button group component
  10. Deconstruction assignment in ES6
  11. Luo 2 peerless Tang clan was officially launched. The official gave a key point, and the broadcast time was implied
  12. 20初识前端HTML(1)
  13. 当新零售遇上 Serverless
  14. 20 initial knowledge of front-end HTML (1)
  15. When new retail meets serverless
  16. [golang] - go into go language lesson 5 type conversion
  17. [golang] - go into go language lesson 6 conditional expression
  18. HTML5(八)——SVG 之 path 详解
  19. HTML5 (8) -- detailed explanation of SVG path
  20. 需要开通VIP以后页面内容才能复制怎么办?控制台禁用javascript即可
  21. Web前端|CSS入门教程(超详细的CSS使用讲解,适合前端初学者)
  22. 实践积累 —— 用Vue3简单写一个单行横向滚动组件
  23. Serverless 全能选手,再下一城
  24. What if you need to open a VIP to copy the page content? Just disable JavaScript on the console
  25. Web front end | CSS introductory tutorial (super detailed CSS explanation, suitable for front-end beginners)
  26. Practice accumulation - write a single line horizontal scroll component simply with vue3
  27. Dili Reba is thin again. She looks elegant and high in a strapless hollow skirt, and her "palm waist" is beautiful to a new height
  28. Serverless all-round player, next city
  29. The difference between MySQL semi synchronous replication and lossless semi synchronous replication
  30. Vue表单设计器的终极解决方案
  31. The ultimate solution for Vue form designer
  32. Nginx从理论到实践超详细笔记
  33. Yu Shuxin's red backless swimsuit is split to the waist and tail, with a concave convex figure and excessive color matching, and his face is white to dazzling
  34. Nginx ultra detailed notes from theory to practice
  35. 【动画消消乐|CSS】086.炫酷水波浪Loading过渡动画
  36. typecho全站启用https
  37. CCTV has another popular employee. The off-site interpretation is very professional, and the appearance ability is no less than that of Wang Bingbing
  38. [animation Xiaole | CSS] 086. Cool water wave loading transition animation
  39. Enable HTTPS in Typecho
  40. 50天用JavaScript完成50个web项目,我学到了什么?
  41. 根据JavaScript中原生的XMLHttpRequest实现jQuery的Ajax
  42. What have I learned from completing 50 web projects with JavaScript in 50 days?
  43. "My neighbor doesn't grow up" has hit the whole network. There are countless horse music circles, and actor Zhou Xiaochuan has successfully made a circle
  44. 根据JavaScript中原生的XMLHttpRequest实现jQuery的Ajax
  45. Implement the Ajax of jQuery according to the native XMLHttpRequest in JavaScript
  46. Implement the Ajax of jQuery according to the native XMLHttpRequest in JavaScript
  47. 30 + women still wear less T-shirts and jeans. If they wear them like stars, they will lose weight
  48. 数栈技术分享前端篇:TS,看你哪里逃~
  49. Several stack technology sharing front end: TS, see where you escape~
  50. 舍弃Kong和Nginx,Apache APISIX 在趣链科技 BaaS 平台的落地实践
  51. Abandon the landing practice of Kong and nginx, Apache apisik on the baas platform of fun chain technology
  52. 浪迹天涯king教你用elementui做复杂的表格,去处理报表数据(合并表头,合并表体行和列)
  53. 前端HTML两万字图文大总结,快来看看你会多少!【️熬夜整理&建议收藏️】
  54. Wandering around the world king teaches you to use elementui to make complex tables and process report data (merge header, merge table body rows and columns)
  55. 路由刷新数据丢失 - vuex数据读取的问题
  56. Front end HTML 20000 word graphic summary, come and see how much you can【 Stay up late to sort out & suggestions]
  57. Route refresh data loss - vuex data reading problem
  58. Systemctl系统启动Nginx服务脚本
  59. Systemctl system startup nginx service script
  60. sleepless