您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

vue3中的createApp怎么使用

2024/11/13 16:33:02发布17次查看
函数定义createapp函数定义在文件 packages/runtime-dom/src/index.ts中
export const createapp = ((...args) => { const app = ensurerenderer().createapp(...args) if (__dev__) { injectnativetagcheck(app) injectcompileroptionscheck(app) } const { mount } = app app.mount = (containerorselector: element | shadowroot | string): any => { const container = normalizecontainer(containerorselector) if (!container) return const component = app._component if (!isfunction(component) && !component.render && !component.template) { // __unsafe__ // reason: potential execution of js expressions in in-dom template. // the user must make sure the in-dom template is trusted. if it's // rendered by the server, the template should not contain any user data. component.template = container.innerhtml // 2.x compat check if (__compat__ && __dev__) { for (let i = 0; i < container.attributes.length; i++) { const attr = container.attributes[i] if (attr.name !== 'v-cloak' && /^(v-|:|@)/.test(attr.name)) { compatutils.warndeprecation( deprecationtypes.global_mount_container, null ) break } } } } // clear content before mounting container.innerhtml = '' const proxy = mount(container, false, container instanceof svgelement) if (container instanceof element) { container.removeattribute('v-cloak') container.setattribute('data-v-app', '') } return proxy } return app}) as createappfunction<element>
(1)首先创建app对象
(2)取出app对象中的mount方法,重写mount方法
首先调用 normalizecontainer 函数来获取container节点
清空container的innerhtml
调用原mount方法
ensurerenderer其函数定义为
function ensurerenderer() { return ( renderer || (renderer = createrenderer<node, element | shadowroot>(rendereroptions)) )}
ensurerenderer通过调用createrenderer,createrenderer函数定义在 packages/runtime-core/src/renderer.ts文件中
export function createrenderer< hostnode = renderernode, hostelement = rendererelement>(options: rendereroptions<hostnode, hostelement>) { return basecreaterenderer<hostnode, hostelement>(options)}
最终是调用basecreaterenderer,其最终返回
return { render, hydrate, createapp: createappapi(render, hydrate) }
createappapicreateappapi函数定义在 packages/runtime-core/src/apicreateapp.ts文件中,其返回函数
return function createapp(rootcomponent, rootprops = null)
mount其定义在packages/runtime-core/src/apicreateapp.ts文件
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 // hmr root reload if (__dev__) { context.reload = () => { render(clonevnode(vnode), rootcontainer, issvg) } } 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 if (__dev__ || __feature_prod_devtools__) { app._instance = vnode.component 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)\`` ) } }
(1)调用createvnode创建虚拟结点
(2)调用 render 函数来实现渲染vnode。render函数是basecreaterenderer 函数返回调用 createappapi 时传入的参数之一
// implementationfunction basecreaterenderer( options: rendereroptions, createhydrationfns?: typeof createhydrationfunctions): any { .... 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 } .....}
patch函数定义在 packages/runtime-core/src/renderer.ts文件中
const patch: patchfn = ( n1, n2, container, anchor = null, parentcomponent = null, parentsuspense = null, issvg = false, slotscopeids = null, optimized = __dev__ && ishmrupdating ? false : !!n2.dynamicchildren ) => { // patching & not same type, unmount old tree 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 } 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})`) } } // set ref if (ref != null && parentcomponent) { setref(ref, n1 && n1.ref, parentsuspense, n2 || n1, !n2) } }const patch: patchfn = ( n1, n2, container, anchor = null, parentcomponent = null, parentsuspense = null, issvg = false, slotscopeids = null, optimized = __dev__ && ishmrupdating ? false : !!n2.dynamicchildren ) => { // patching & not same type, unmount old tree 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 } 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})`) } } // set ref if (ref != null && parentcomponent) { setref(ref, n1 && n1.ref, parentsuspense, n2 || n1, !n2) } }
processcomponent函数定义在packages/runtime-core/src/renderer.ts文件中
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) } }
对比新旧虚拟结点n1和n2,如果n1为空,则挂载节点;否则,更新节点。此时n1为空,执行mountcomponent挂载结点
mountcomponent函数定义在packages/runtime-core/src/renderer.ts文件中
const mountcomponent: mountcomponentfn = ( initialvnode, container, anchor, parentcomponent, parentsuspense, issvg, optimized ) => { // 2.x compat may pre-creaate the component instance before actually // mounting const compatmountinstance = __compat__ && initialvnode.iscompatroot && initialvnode.component const instance: componentinternalinstance = compatmountinstance || (initialvnode.component = createcomponentinstance( initialvnode, parentcomponent, parentsuspense )) // inject renderer internals for keepalive if (iskeepalive(initialvnode)) { ;(instance.ctx as keepalivecontext).renderer = internals } // resolve props and slots for setup context if (!(__compat__ && compatmountinstance)) { setupcomponent(instance) } // setup() is async. this component relies on async logic to be resolved // before proceeding if (__feature_suspense__ && instance.asyncdep) { parentsuspense && parentsuspense.registerdep(instance, setuprendereffect) // give it a placeholder if this is not hydration // todo handle self-defined fallback if (!initialvnode.el) { const placeholder = (instance.subtree = createvnode(comment)) processcommentnode(null, placeholder, container!, anchor) } return } setuprendereffect( instance, initialvnode, container, anchor, parentsuspense, issvg, optimized ) }
其执行流程为
(1)首先调用createcomponentinstance创建组件的实例对象
(2)调用setupcomponent来初始化属性及slots属性
(3)调用设置和渲染有副作用的函数setuprendereffect,主要是执行reactiveeffect的run方法来执行componentupdatefn以及调度queuejob。
setuprendereffect函数定义在packages/runtime-core/src/renderer.ts文件中,关键的是当中定义的函数componentupdatefn
const componentupdatefn = () => { if (!instance.ismounted) { let vnodehook: vnodehook | null | undefined const { el, props } = initialvnode const { bm, m, parent } = instance effect.allowrecurse = false // beforemount hook if (bm) { invokearrayfns(bm) } // onvnodebeforemount if ((vnodehook = props && props.onvnodebeforemount)) { invokevnodehook(vnodehook, parent, initialvnode) } if ( __compat__ && iscompatenabled(deprecationtypes.instance_event_hooks, instance) ) { instance.emit('hook:beforemount') } effect.allowrecurse = true if (el && hydratenode) { // vnode has adopted host node - perform hydration instead of mount. const hydratesubtree = () => { instance.subtree = rendercomponentroot(instance) hydratenode!( el as node, instance.subtree, instance, parentsuspense, null ) } if (isasyncwrapper(initialvnode)) { (initialvnode.type as componentoptions).__asyncloader!().then( // note: we are moving the render call into an async callback, // which means it won't track dependencies - but it's ok because // a server-rendered async wrapper is already in resolved state // and it will never need to change. () => !instance.isunmounted && hydratesubtree() ) } else { hydratesubtree() } } else { const subtree = (instance.subtree = rendercomponentroot(instance)) patch( null, subtree, container, anchor, instance, parentsuspense, issvg ) initialvnode.el = subtree.el } // mounted hook if (m) { queuepostrendereffect(m, parentsuspense) } // onvnodemounted if ((vnodehook = props && props.onvnodemounted)) { const scopedinitialvnode = initialvnode queuepostrendereffect( () => invokevnodehook(vnodehook!, parent, scopedinitialvnode), parentsuspense ) } if ( __compat__ && iscompatenabled(deprecationtypes.instance_event_hooks, instance) ) { queuepostrendereffect( () => instance.emit('hook:mounted'), parentsuspense ) } // activated hook for keep-alive roots. // #1742 activated hook must be accessed after first render // since the hook may be injected by a child keep-alive if (initialvnode.shapeflag & shapeflags.component_should_keep_alive) { instance.a && queuepostrendereffect(instance.a, parentsuspense) if ( __compat__ && iscompatenabled(deprecationtypes.instance_event_hooks, instance) ) { queuepostrendereffect( () => instance.emit('hook:activated'), parentsuspense ) } } instance.ismounted = true // #2458: deference mount-only object parameters to prevent memleaks initialvnode = container = anchor = null as any } else { // updatecomponent // this is triggered by mutation of component's own state (next: null) // or parent calling processcomponent (next: vnode) let { next, bu, u, parent, vnode } = instance let originnext = next let vnodehook: vnodehook | null | undefined if (next) { next.el = vnode.el updatecomponentprerender(instance, next, optimized) } else { next = vnode } // disallow component effect recursion during pre-lifecycle hooks. effect.allowrecurse = false // beforeupdate hook if (bu) { invokearrayfns(bu) } // onvnodebeforeupdate if ((vnodehook = next.props && next.props.onvnodebeforeupdate)) { invokevnodehook(vnodehook, parent, next, vnode) } if ( __compat__ && iscompatenabled(deprecationtypes.instance_event_hooks, instance) ) { instance.emit('hook:beforeupdate') } effect.allowrecurse = true // render const nexttree = rendercomponentroot(instance) const prevtree = instance.subtree instance.subtree = nexttree patch( prevtree, nexttree, // parent may have changed if it's in a teleport hostparentnode(prevtree.el!)!, // anchor may have changed if it's in a fragment getnexthostnode(prevtree), instance, parentsuspense, issvg ) next.el = nexttree.el if (originnext === null) { // self-triggered update. in case of hoc, update parent component // vnode el. hoc is indicated by parent instance's subtree pointing // to child component's vnode updatehochostel(instance, nexttree.el) } // updated hook if (u) { queuepostrendereffect(u, parentsuspense) } // onvnodeupdated if ((vnodehook = next.props && next.props.onvnodeupdated)) { queuepostrendereffect( () => invokevnodehook(vnodehook!, parent, next!, vnode), parentsuspense ) } if ( __compat__ && iscompatenabled(deprecationtypes.instance_event_hooks, instance) ) { queuepostrendereffect( () => instance.emit('hook:updated'), parentsuspense ) } } }
当组件还未被挂载时,需要进行挂载,并且在挂载完成后将组件的 ismounted 属性设为 true,以示该组件已经被成功挂载。如果已经挂载,则更新组件。
递归调用 patch 函数挂载组件
processfragment函数定义在packages/runtime-core/src/renderer.ts文件中
const processfragment = ( n1: vnode | null, n2: vnode, container: rendererelement, anchor: renderernode | null, parentcomponent: componentinternalinstance | null, parentsuspense: suspenseboundary | null, issvg: boolean, slotscopeids: string[] | null, optimized: boolean ) => { const fragmentstartanchor = (n2.el = n1 ? n1.el : hostcreatetext(''))! const fragmentendanchor = (n2.anchor = n1 ? n1.anchor : hostcreatetext(''))! let { patchflag, dynamicchildren, slotscopeids: fragmentslotscopeids } = n2 if (dynamicchildren) { optimized = true } // check if this is a slot fragment with :slotted scope ids if (fragmentslotscopeids) { slotscopeids = slotscopeids ? slotscopeids.concat(fragmentslotscopeids) : fragmentslotscopeids } if (n1 == null) { hostinsert(fragmentstartanchor, container, anchor) hostinsert(fragmentendanchor, container, anchor) // a fragment can only have array children // since they are either generated by the compiler, or implicitly created // from arrays. mountchildren( n2.children as vnodearraychildren, container, fragmentendanchor, parentcomponent, parentsuspense, issvg, slotscopeids, optimized ) } else { if ( patchflag > 0 && patchflag & patchflags.stable_fragment && dynamicchildren && // #2715 the previous fragment could've been a bailed one as a result // of renderslot() with no valid children n1.dynamicchildren ) { // a stable fragment (template root or <template v-for>) doesn't need to // patch children order, but it may contain dynamicchildren. patchblockchildren( n1.dynamicchildren, dynamicchildren, container, parentcomponent, parentsuspense, issvg, slotscopeids ) if (__dev__ && parentcomponent && parentcomponent.type.__hmrid) { traversestaticchildren(n1, n2) } else if ( // #2080 if the stable fragment has a key, it's a <template v-for> that may // get moved around. make sure all root level vnodes inherit el. // #2134 or if it's a component root, it may also get moved around // as the component is being moved. n2.key != null || (parentcomponent && n2 === parentcomponent.subtree) ) { traversestaticchildren(n1, n2, true /* shallow */) } } else { // keyed / unkeyed, or manual fragments. // for keyed & unkeyed, since they are compiler generated from v-for, // each child is guaranteed to be a block so the fragment will never // have dynamicchildren. patchchildren( n1, n2, container, fragmentendanchor, parentcomponent, parentsuspense, issvg, slotscopeids, optimized ) } } }
(1)如果n1为空,则执行mountchildren
(2)否则执行patchchildren
mountchildren函数定义在packages/runtime-core/src/renderer.ts文件中
const mountchildren: mountchildrenfn = ( children, container, anchor, parentcomponent, parentsuspense, issvg, slotscopeids, optimized, start = 0 ) => { for (let i = start; i < children.length; i++) { const child = (children[i] = optimized ? cloneifmounted(children[i] as vnode) : normalizevnode(children[i])) patch( null, child, container, anchor, parentcomponent, parentsuspense, issvg, slotscopeids, optimized ) } }
遍历children,执行patch挂载所有子元素
processelement函数定义在packages/runtime-core/src/renderer.ts文件中
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 { patchelement( n1, n2, parentcomponent, parentsuspense, issvg, slotscopeids, optimized ) } }

(1)如果n1为空,则调用mountelement挂载元素
(2)如果n1不为空,则调用patchelement来更新元素
此时,执行挂载元素。
mountelement函数定义在packages/runtime-core/src/renderer.ts文件中
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 { patchelement( n1, n2, parentcomponent, parentsuspense, issvg, slotscopeids, optimized ) } }

(1)调用hostcreateelement根据类型和其他属性,创建dom元素节点
(2)判断子节点的类型,若子节点为纯文本,则直接处理纯文本hostsetelementtext,若子节点为数组,则调用 mountchildren 函数深度遍历处理子节点
(3)处理 props 属性,主要是调用hostpatchprop和setscopeid
(4)调用hostinsert将el挂载到 container 中
以上就是vue3中的createapp怎么使用的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product