renderer.ts 77 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824
  1. import {
  2. Comment,
  3. Fragment,
  4. Static,
  5. Text,
  6. type VNode,
  7. type VNodeArrayChildren,
  8. type VNodeHook,
  9. type VNodeProps,
  10. VaporSlot,
  11. cloneIfMounted,
  12. cloneVNode,
  13. createVNode,
  14. invokeVNodeHook,
  15. isSameVNodeType,
  16. normalizeVNode,
  17. } from './vnode'
  18. import {
  19. type ComponentInternalInstance,
  20. type ComponentOptions,
  21. type ConcreteComponent,
  22. type Data,
  23. type GenericComponentInstance,
  24. type LifecycleHook,
  25. createComponentInstance,
  26. getComponentPublicInstance,
  27. setupComponent,
  28. } from './component'
  29. import {
  30. filterSingleRoot,
  31. renderComponentRoot,
  32. shouldUpdateComponent,
  33. updateHOCHostEl,
  34. } from './componentRenderUtils'
  35. import {
  36. EMPTY_ARR,
  37. EMPTY_OBJ,
  38. NOOP,
  39. PatchFlags,
  40. ShapeFlags,
  41. def,
  42. getGlobalThis,
  43. getSequence,
  44. invokeArrayFns,
  45. isArray,
  46. isReservedProp,
  47. } from '@vue/shared'
  48. import {
  49. type SchedulerJob,
  50. SchedulerJobFlags,
  51. type SchedulerJobs,
  52. flushOnAppMount,
  53. flushPreFlushCbs,
  54. queueJob,
  55. queuePostFlushCb,
  56. } from './scheduler'
  57. import {
  58. EffectFlags,
  59. ReactiveEffect,
  60. setActiveSub,
  61. setCurrentScope,
  62. } from '@vue/reactivity'
  63. import { updateProps } from './componentProps'
  64. import { updateSlots } from './componentSlots'
  65. import { popWarningContext, pushWarningContext, warn } from './warning'
  66. import {
  67. type AppMountFn,
  68. type AppUnmountFn,
  69. type CreateAppFunction,
  70. createAppAPI,
  71. } from './apiCreateApp'
  72. import { setRef } from './rendererTemplateRef'
  73. import {
  74. type SuspenseBoundary,
  75. type SuspenseImpl,
  76. isSuspense,
  77. queueEffectWithSuspense,
  78. } from './components/Suspense'
  79. import {
  80. TeleportEndKey,
  81. type TeleportImpl,
  82. type TeleportVNode,
  83. } from './components/Teleport'
  84. import { type KeepAliveContext, isKeepAlive } from './components/KeepAlive'
  85. import { isHmrUpdating, registerHMR, unregisterHMR } from './hmr'
  86. import { type RootHydrateFunction, createHydrationFunctions } from './hydration'
  87. import { invokeDirectiveHook } from './directives'
  88. import { endMeasure, startMeasure } from './profiling'
  89. import {
  90. devtoolsComponentAdded,
  91. devtoolsComponentRemoved,
  92. devtoolsComponentUpdated,
  93. setDevtoolsHook,
  94. } from './devtools'
  95. import { initFeatureFlags } from './featureFlags'
  96. import { isAsyncWrapper } from './apiAsyncComponent'
  97. import { isCompatEnabled } from './compat/compatConfig'
  98. import { DeprecationTypes } from './compat/compatConfig'
  99. import type { VaporInteropInterface } from './apiCreateApp'
  100. import { type TransitionHooks, leaveCbKey } from './components/BaseTransition'
  101. import type { VueElement } from '@vue/runtime-dom'
  102. export interface Renderer<HostElement = RendererElement> {
  103. render: RootRenderFunction<HostElement>
  104. createApp: CreateAppFunction<HostElement>
  105. internals: RendererInternals
  106. }
  107. export interface HydrationRenderer extends Renderer<Element | ShadowRoot> {
  108. hydrate: RootHydrateFunction
  109. hydrateNode: ReturnType<typeof createHydrationFunctions>[1]
  110. }
  111. export type ElementNamespace = 'svg' | 'mathml' | undefined
  112. export type RootRenderFunction<HostElement = RendererElement> = (
  113. vnode: VNode | null,
  114. container: HostElement,
  115. namespace?: ElementNamespace,
  116. ) => void
  117. export interface RendererOptions<
  118. HostNode = RendererNode,
  119. HostElement = RendererElement,
  120. > {
  121. patchProp(
  122. el: HostElement,
  123. key: string,
  124. prevValue: any,
  125. nextValue: any,
  126. namespace?: ElementNamespace,
  127. parentComponent?: ComponentInternalInstance | null,
  128. ): void
  129. insert(el: HostNode, parent: HostElement, anchor?: HostNode | null): void
  130. remove(el: HostNode): void
  131. createElement(
  132. type: string,
  133. namespace?: ElementNamespace,
  134. isCustomizedBuiltIn?: string,
  135. vnodeProps?: (VNodeProps & { [key: string]: any }) | null,
  136. ): HostElement
  137. createText(text: string): HostNode
  138. createComment(text: string): HostNode
  139. setText(node: HostNode, text: string): void
  140. setElementText(node: HostElement, text: string): void
  141. parentNode(node: HostNode): HostElement | null
  142. nextSibling(node: HostNode): HostNode | null
  143. querySelector?(selector: string): HostElement | null
  144. setScopeId?(el: HostElement, id: string): void
  145. cloneNode?(node: HostNode): HostNode
  146. insertStaticContent?(
  147. content: string,
  148. parent: HostElement,
  149. anchor: HostNode | null,
  150. namespace: ElementNamespace,
  151. start?: HostNode | null,
  152. end?: HostNode | null,
  153. ): [HostNode, HostNode]
  154. }
  155. // Renderer Node can technically be any object in the context of core renderer
  156. // logic - they are never directly operated on and always passed to the node op
  157. // functions provided via options, so the internal constraint is really just
  158. // a generic object.
  159. export interface RendererNode {
  160. [key: string | symbol]: any
  161. }
  162. export interface RendererElement extends RendererNode {}
  163. // An object exposing the internals of a renderer, passed to tree-shakeable
  164. // features so that they can be decoupled from this file. Keys are shortened
  165. // to optimize bundle size.
  166. export interface RendererInternals<
  167. HostNode = RendererNode,
  168. HostElement = RendererElement,
  169. > {
  170. p: PatchFn
  171. um: UnmountFn
  172. r: RemoveFn
  173. m: MoveFn
  174. mt: MountComponentFn
  175. umt: UnmountComponentFn
  176. mc: MountChildrenFn
  177. pc: PatchChildrenFn
  178. pbc: PatchBlockChildrenFn
  179. n: NextFn
  180. o: RendererOptions<HostNode, HostElement>
  181. }
  182. // These functions are created inside a closure and therefore their types cannot
  183. // be directly exported. In order to avoid maintaining function signatures in
  184. // two places, we declare them once here and use them inside the closure.
  185. type PatchFn = (
  186. n1: VNode | null, // null means this is a mount
  187. n2: VNode,
  188. container: RendererElement,
  189. anchor?: RendererNode | null,
  190. parentComponent?: ComponentInternalInstance | null,
  191. parentSuspense?: SuspenseBoundary | null,
  192. namespace?: ElementNamespace,
  193. slotScopeIds?: string[] | null,
  194. optimized?: boolean,
  195. ) => void
  196. type MountChildrenFn = (
  197. children: VNodeArrayChildren,
  198. container: RendererElement,
  199. anchor: RendererNode | null,
  200. parentComponent: ComponentInternalInstance | null,
  201. parentSuspense: SuspenseBoundary | null,
  202. namespace: ElementNamespace,
  203. slotScopeIds: string[] | null,
  204. optimized: boolean,
  205. start?: number,
  206. ) => void
  207. type PatchChildrenFn = (
  208. n1: VNode | null,
  209. n2: VNode,
  210. container: RendererElement,
  211. anchor: RendererNode | null,
  212. parentComponent: ComponentInternalInstance | null,
  213. parentSuspense: SuspenseBoundary | null,
  214. namespace: ElementNamespace,
  215. slotScopeIds: string[] | null,
  216. optimized: boolean,
  217. ) => void
  218. type PatchBlockChildrenFn = (
  219. oldChildren: VNode[],
  220. newChildren: VNode[],
  221. fallbackContainer: RendererElement,
  222. parentComponent: ComponentInternalInstance | null,
  223. parentSuspense: SuspenseBoundary | null,
  224. namespace: ElementNamespace,
  225. slotScopeIds: string[] | null,
  226. ) => void
  227. type MoveFn = (
  228. vnode: VNode,
  229. container: RendererElement,
  230. anchor: RendererNode | null,
  231. type: MoveType,
  232. parentComponent: ComponentInternalInstance | null,
  233. parentSuspense?: SuspenseBoundary | null,
  234. ) => void
  235. type NextFn = (vnode: VNode) => RendererNode | null
  236. type UnmountFn = (
  237. vnode: VNode,
  238. parentComponent: ComponentInternalInstance | null,
  239. parentSuspense: SuspenseBoundary | null,
  240. doRemove?: boolean,
  241. optimized?: boolean,
  242. ) => void
  243. type RemoveFn = (vnode: VNode) => void
  244. type UnmountChildrenFn = (
  245. children: VNode[],
  246. parentComponent: ComponentInternalInstance | null,
  247. parentSuspense: SuspenseBoundary | null,
  248. doRemove?: boolean,
  249. optimized?: boolean,
  250. start?: number,
  251. ) => void
  252. export type MountComponentFn = (
  253. initialVNode: VNode,
  254. container: RendererElement,
  255. anchor: RendererNode | null,
  256. parentComponent: ComponentInternalInstance | null,
  257. parentSuspense: SuspenseBoundary | null,
  258. namespace: ElementNamespace,
  259. optimized: boolean,
  260. ) => void
  261. export type UnmountComponentFn = (
  262. instance: ComponentInternalInstance,
  263. parentSuspense: SuspenseBoundary | null,
  264. doRemove?: boolean,
  265. ) => void
  266. type ProcessTextOrCommentFn = (
  267. n1: VNode | null,
  268. n2: VNode,
  269. container: RendererElement,
  270. anchor: RendererNode | null,
  271. ) => void
  272. export type SetupRenderEffectFn = (
  273. instance: ComponentInternalInstance,
  274. initialVNode: VNode,
  275. container: RendererElement,
  276. anchor: RendererNode | null,
  277. parentSuspense: SuspenseBoundary | null,
  278. namespace: ElementNamespace,
  279. optimized: boolean,
  280. ) => void
  281. export enum MoveType {
  282. ENTER,
  283. LEAVE,
  284. REORDER,
  285. }
  286. export const queuePostRenderEffect: (
  287. fn: SchedulerJobs,
  288. id: number | undefined,
  289. suspense: SuspenseBoundary | null,
  290. ) => void = __FEATURE_SUSPENSE__
  291. ? __TEST__
  292. ? // vitest can't seem to handle eager circular dependency
  293. (
  294. fn: Function | Function[],
  295. id: number | undefined,
  296. suspense: SuspenseBoundary | null,
  297. ) => queueEffectWithSuspense(fn, id, suspense)
  298. : queueEffectWithSuspense
  299. : queuePostFlushCb
  300. /**
  301. * The createRenderer function accepts two generic arguments:
  302. * HostNode and HostElement, corresponding to Node and Element types in the
  303. * host environment. For example, for runtime-dom, HostNode would be the DOM
  304. * `Node` interface and HostElement would be the DOM `Element` interface.
  305. *
  306. * Custom renderers can pass in the platform specific types like this:
  307. *
  308. * ``` js
  309. * const { render, createApp } = createRenderer<Node, Element>({
  310. * patchProp,
  311. * ...nodeOps
  312. * })
  313. * ```
  314. */
  315. export function createRenderer<
  316. HostNode = RendererNode,
  317. HostElement = RendererElement,
  318. >(options: RendererOptions<HostNode, HostElement>): Renderer<HostElement> {
  319. return baseCreateRenderer<HostNode, HostElement>(options)
  320. }
  321. // Separate API for creating hydration-enabled renderer.
  322. // Hydration logic is only used when calling this function, making it
  323. // tree-shakable.
  324. export function createHydrationRenderer(
  325. options: RendererOptions<Node, Element>,
  326. ): HydrationRenderer {
  327. return baseCreateRenderer(options, createHydrationFunctions)
  328. }
  329. // overload 1: no hydration
  330. function baseCreateRenderer<
  331. HostNode = RendererNode,
  332. HostElement = RendererElement,
  333. >(options: RendererOptions<HostNode, HostElement>): Renderer<HostElement>
  334. // overload 2: with hydration
  335. function baseCreateRenderer(
  336. options: RendererOptions<Node, Element>,
  337. createHydrationFns: typeof createHydrationFunctions,
  338. ): HydrationRenderer
  339. // implementation
  340. function baseCreateRenderer(
  341. options: RendererOptions,
  342. createHydrationFns?: typeof createHydrationFunctions,
  343. ): any {
  344. // compile-time feature flags check
  345. if (__ESM_BUNDLER__ && !__TEST__) {
  346. initFeatureFlags()
  347. }
  348. const target = getGlobalThis()
  349. target.__VUE__ = true
  350. if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
  351. setDevtoolsHook(target.__VUE_DEVTOOLS_GLOBAL_HOOK__, target)
  352. }
  353. const {
  354. insert: hostInsert,
  355. remove: hostRemove,
  356. patchProp: hostPatchProp,
  357. createElement: hostCreateElement,
  358. createText: hostCreateText,
  359. createComment: hostCreateComment,
  360. setText: hostSetText,
  361. setElementText: hostSetElementText,
  362. parentNode: hostParentNode,
  363. nextSibling: hostNextSibling,
  364. setScopeId: hostSetScopeId = NOOP,
  365. insertStaticContent: hostInsertStaticContent,
  366. } = options
  367. // Note: functions inside this closure should use `const xxx = () => {}`
  368. // style in order to prevent being inlined by minifiers.
  369. const patch: PatchFn = (
  370. n1,
  371. n2,
  372. container,
  373. anchor = null,
  374. parentComponent = null,
  375. parentSuspense = null,
  376. namespace = undefined,
  377. slotScopeIds = null,
  378. optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren,
  379. ) => {
  380. if (n1 === n2) {
  381. return
  382. }
  383. // patching & not same type, unmount old tree
  384. if (n1 && !isSameVNodeType(n1, n2)) {
  385. anchor = getNextHostNode(n1)
  386. unmount(n1, parentComponent, parentSuspense, true)
  387. n1 = null
  388. }
  389. if (n2.patchFlag === PatchFlags.BAIL) {
  390. optimized = false
  391. n2.dynamicChildren = null
  392. }
  393. const { type, ref, shapeFlag } = n2
  394. switch (type) {
  395. case Text:
  396. processText(n1, n2, container, anchor)
  397. break
  398. case Comment:
  399. processCommentNode(n1, n2, container, anchor)
  400. break
  401. case Static:
  402. if (n1 == null) {
  403. mountStaticNode(n2, container, anchor, namespace)
  404. } else if (__DEV__) {
  405. patchStaticNode(n1, n2, container, namespace)
  406. }
  407. break
  408. case Fragment:
  409. processFragment(
  410. n1,
  411. n2,
  412. container,
  413. anchor,
  414. parentComponent,
  415. parentSuspense,
  416. namespace,
  417. slotScopeIds,
  418. optimized,
  419. )
  420. break
  421. case VaporSlot:
  422. getVaporInterface(parentComponent, n2).slot(n1, n2, container, anchor)
  423. break
  424. default:
  425. if (shapeFlag & ShapeFlags.ELEMENT) {
  426. processElement(
  427. n1,
  428. n2,
  429. container,
  430. anchor,
  431. parentComponent,
  432. parentSuspense,
  433. namespace,
  434. slotScopeIds,
  435. optimized,
  436. )
  437. } else if (shapeFlag & ShapeFlags.COMPONENT) {
  438. processComponent(
  439. n1,
  440. n2,
  441. container,
  442. anchor,
  443. parentComponent,
  444. parentSuspense,
  445. namespace,
  446. slotScopeIds,
  447. optimized,
  448. )
  449. } else if (shapeFlag & ShapeFlags.TELEPORT) {
  450. ;(type as typeof TeleportImpl).process(
  451. n1 as TeleportVNode,
  452. n2 as TeleportVNode,
  453. container,
  454. anchor,
  455. parentComponent,
  456. parentSuspense,
  457. namespace,
  458. slotScopeIds,
  459. optimized,
  460. internals,
  461. )
  462. } else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
  463. ;(type as typeof SuspenseImpl).process(
  464. n1,
  465. n2,
  466. container,
  467. anchor,
  468. parentComponent,
  469. parentSuspense,
  470. namespace,
  471. slotScopeIds,
  472. optimized,
  473. internals,
  474. )
  475. } else if (__DEV__) {
  476. warn('Invalid VNode type:', type, `(${typeof type})`)
  477. }
  478. }
  479. // set ref
  480. if (ref != null && parentComponent) {
  481. setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2)
  482. } else if (ref == null && n1 && n1.ref != null) {
  483. setRef(n1.ref, null, parentSuspense, n1, true)
  484. }
  485. }
  486. const processText: ProcessTextOrCommentFn = (n1, n2, container, anchor) => {
  487. if (n1 == null) {
  488. hostInsert(
  489. (n2.el = hostCreateText(n2.children as string)),
  490. container,
  491. anchor,
  492. )
  493. } else {
  494. const el = (n2.el = n1.el!)
  495. if (n2.children !== n1.children) {
  496. hostSetText(el, n2.children as string)
  497. }
  498. }
  499. }
  500. const processCommentNode: ProcessTextOrCommentFn = (
  501. n1,
  502. n2,
  503. container,
  504. anchor,
  505. ) => {
  506. if (n1 == null) {
  507. hostInsert(
  508. (n2.el = hostCreateComment((n2.children as string) || '')),
  509. container,
  510. anchor,
  511. )
  512. } else {
  513. // there's no support for dynamic comments
  514. n2.el = n1.el
  515. }
  516. }
  517. const mountStaticNode = (
  518. n2: VNode,
  519. container: RendererElement,
  520. anchor: RendererNode | null,
  521. namespace: ElementNamespace,
  522. ) => {
  523. // static nodes are only present when used with compiler-dom/runtime-dom
  524. // which guarantees presence of hostInsertStaticContent.
  525. ;[n2.el, n2.anchor] = hostInsertStaticContent!(
  526. n2.children as string,
  527. container,
  528. anchor,
  529. namespace,
  530. n2.el,
  531. n2.anchor,
  532. )
  533. }
  534. /**
  535. * Dev / HMR only
  536. */
  537. const patchStaticNode = (
  538. n1: VNode,
  539. n2: VNode,
  540. container: RendererElement,
  541. namespace: ElementNamespace,
  542. ) => {
  543. // static nodes are only patched during dev for HMR
  544. if (n2.children !== n1.children) {
  545. const anchor = hostNextSibling(n1.anchor!)
  546. // remove existing
  547. removeStaticNode(n1)
  548. // insert new
  549. ;[n2.el, n2.anchor] = hostInsertStaticContent!(
  550. n2.children as string,
  551. container,
  552. anchor,
  553. namespace,
  554. )
  555. } else {
  556. n2.el = n1.el
  557. n2.anchor = n1.anchor
  558. }
  559. }
  560. const moveStaticNode = (
  561. { el, anchor }: VNode,
  562. container: RendererElement,
  563. nextSibling: RendererNode | null,
  564. ) => {
  565. let next
  566. while (el && el !== anchor) {
  567. next = hostNextSibling(el)
  568. hostInsert(el, container, nextSibling)
  569. el = next
  570. }
  571. hostInsert(anchor!, container, nextSibling)
  572. }
  573. const removeStaticNode = ({ el, anchor }: VNode) => {
  574. let next
  575. while (el && el !== anchor) {
  576. next = hostNextSibling(el)
  577. hostRemove(el)
  578. el = next
  579. }
  580. hostRemove(anchor!)
  581. }
  582. const processElement = (
  583. n1: VNode | null,
  584. n2: VNode,
  585. container: RendererElement,
  586. anchor: RendererNode | null,
  587. parentComponent: ComponentInternalInstance | null,
  588. parentSuspense: SuspenseBoundary | null,
  589. namespace: ElementNamespace,
  590. slotScopeIds: string[] | null,
  591. optimized: boolean,
  592. ) => {
  593. if (n2.type === 'svg') {
  594. namespace = 'svg'
  595. } else if (n2.type === 'math') {
  596. namespace = 'mathml'
  597. }
  598. if (n1 == null) {
  599. mountElement(
  600. n2,
  601. container,
  602. anchor,
  603. parentComponent,
  604. parentSuspense,
  605. namespace,
  606. slotScopeIds,
  607. optimized,
  608. )
  609. } else {
  610. patchElement(
  611. n1,
  612. n2,
  613. parentComponent,
  614. parentSuspense,
  615. namespace,
  616. slotScopeIds,
  617. optimized,
  618. )
  619. }
  620. }
  621. const mountElement = (
  622. vnode: VNode,
  623. container: RendererElement,
  624. anchor: RendererNode | null,
  625. parentComponent: ComponentInternalInstance | null,
  626. parentSuspense: SuspenseBoundary | null,
  627. namespace: ElementNamespace,
  628. slotScopeIds: string[] | null,
  629. optimized: boolean,
  630. ) => {
  631. let el: RendererElement
  632. let vnodeHook: VNodeHook | undefined | null
  633. const { props, shapeFlag, transition, dirs } = vnode
  634. el = vnode.el = hostCreateElement(
  635. vnode.type as string,
  636. namespace,
  637. props && props.is,
  638. props,
  639. )
  640. // mount children first, since some props may rely on child content
  641. // being already rendered, e.g. `<select value>`
  642. if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
  643. hostSetElementText(el, vnode.children as string)
  644. } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
  645. mountChildren(
  646. vnode.children as VNodeArrayChildren,
  647. el,
  648. null,
  649. parentComponent,
  650. parentSuspense,
  651. resolveChildrenNamespace(vnode, namespace),
  652. slotScopeIds,
  653. optimized,
  654. )
  655. }
  656. if (dirs) {
  657. invokeDirectiveHook(vnode, null, parentComponent, 'created')
  658. }
  659. // scopeId
  660. setScopeId(el, vnode, vnode.scopeId, slotScopeIds, parentComponent)
  661. // props
  662. if (props) {
  663. for (const key in props) {
  664. if (key !== 'value' && !isReservedProp(key)) {
  665. hostPatchProp(el, key, null, props[key], namespace, parentComponent)
  666. }
  667. }
  668. /**
  669. * Special case for setting value on DOM elements:
  670. * - it can be order-sensitive (e.g. should be set *after* min/max, #2325, #4024)
  671. * - it needs to be forced (#1471)
  672. * #2353 proposes adding another renderer option to configure this, but
  673. * the properties affects are so finite it is worth special casing it
  674. * here to reduce the complexity. (Special casing it also should not
  675. * affect non-DOM renderers)
  676. */
  677. if ('value' in props) {
  678. hostPatchProp(el, 'value', null, props.value, namespace)
  679. }
  680. if ((vnodeHook = props.onVnodeBeforeMount)) {
  681. invokeVNodeHook(vnodeHook, parentComponent, vnode)
  682. }
  683. }
  684. if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
  685. def(el, '__vnode', vnode, true)
  686. def(el, '__vueParentComponent', parentComponent, true)
  687. }
  688. if (dirs) {
  689. invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount')
  690. }
  691. // #1583 For inside suspense + suspense not resolved case, enter hook should call when suspense resolved
  692. // #1689 For inside suspense + suspense resolved case, just call it
  693. if (transition) {
  694. performTransitionEnter(
  695. el,
  696. transition,
  697. () => hostInsert(el, container, anchor),
  698. parentSuspense,
  699. )
  700. } else {
  701. hostInsert(el, container, anchor)
  702. }
  703. if ((vnodeHook = props && props.onVnodeMounted) || dirs) {
  704. queuePostRenderEffect(
  705. () => {
  706. vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
  707. dirs && invokeDirectiveHook(vnode, null, parentComponent, 'mounted')
  708. },
  709. undefined,
  710. parentSuspense,
  711. )
  712. }
  713. }
  714. const setScopeId = (
  715. el: RendererElement,
  716. vnode: VNode,
  717. scopeId: string | null,
  718. slotScopeIds: string[] | null,
  719. parentComponent: GenericComponentInstance | null,
  720. ) => {
  721. if (scopeId) {
  722. hostSetScopeId(el, scopeId)
  723. }
  724. if (slotScopeIds) {
  725. for (let i = 0; i < slotScopeIds.length; i++) {
  726. hostSetScopeId(el, slotScopeIds[i])
  727. }
  728. }
  729. const inheritedScopeIds = getInheritedScopeIds(vnode, parentComponent)
  730. for (let i = 0; i < inheritedScopeIds.length; i++) {
  731. hostSetScopeId(el, inheritedScopeIds[i])
  732. }
  733. }
  734. const mountChildren: MountChildrenFn = (
  735. children,
  736. container,
  737. anchor,
  738. parentComponent,
  739. parentSuspense,
  740. namespace: ElementNamespace,
  741. slotScopeIds,
  742. optimized,
  743. start = 0,
  744. ) => {
  745. for (let i = start; i < children.length; i++) {
  746. const child = (children[i] = optimized
  747. ? cloneIfMounted(children[i] as VNode)
  748. : normalizeVNode(children[i]))
  749. patch(
  750. null,
  751. child,
  752. container,
  753. anchor,
  754. parentComponent,
  755. parentSuspense,
  756. namespace,
  757. slotScopeIds,
  758. optimized,
  759. )
  760. }
  761. }
  762. const patchElement = (
  763. n1: VNode,
  764. n2: VNode,
  765. parentComponent: ComponentInternalInstance | null,
  766. parentSuspense: SuspenseBoundary | null,
  767. namespace: ElementNamespace,
  768. slotScopeIds: string[] | null,
  769. optimized: boolean,
  770. ) => {
  771. const el = (n2.el = n1.el!)
  772. if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
  773. el.__vnode = n2
  774. }
  775. let { patchFlag, dynamicChildren, dirs } = n2
  776. // #1426 take the old vnode's patch flag into account since user may clone a
  777. // compiler-generated vnode, which de-opts to FULL_PROPS
  778. patchFlag |= n1.patchFlag & PatchFlags.FULL_PROPS
  779. const oldProps = n1.props || EMPTY_OBJ
  780. const newProps = n2.props || EMPTY_OBJ
  781. let vnodeHook: VNodeHook | undefined | null
  782. // disable recurse in beforeUpdate hooks
  783. parentComponent && toggleRecurse(parentComponent, false)
  784. if ((vnodeHook = newProps.onVnodeBeforeUpdate)) {
  785. invokeVNodeHook(vnodeHook, parentComponent, n2, n1)
  786. }
  787. if (dirs) {
  788. invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate')
  789. }
  790. parentComponent && toggleRecurse(parentComponent, true)
  791. if (__DEV__ && isHmrUpdating) {
  792. // HMR updated, force full diff
  793. patchFlag = 0
  794. optimized = false
  795. dynamicChildren = null
  796. }
  797. // #9135 innerHTML / textContent unset needs to happen before possible
  798. // new children mount
  799. if (
  800. (oldProps.innerHTML && newProps.innerHTML == null) ||
  801. (oldProps.textContent && newProps.textContent == null)
  802. ) {
  803. hostSetElementText(el, '')
  804. }
  805. if (dynamicChildren) {
  806. patchBlockChildren(
  807. n1.dynamicChildren!,
  808. dynamicChildren,
  809. el,
  810. parentComponent,
  811. parentSuspense,
  812. resolveChildrenNamespace(n2, namespace),
  813. slotScopeIds,
  814. )
  815. if (__DEV__) {
  816. // necessary for HMR
  817. traverseStaticChildren(n1, n2)
  818. }
  819. } else if (!optimized) {
  820. // full diff
  821. patchChildren(
  822. n1,
  823. n2,
  824. el,
  825. null,
  826. parentComponent,
  827. parentSuspense,
  828. resolveChildrenNamespace(n2, namespace),
  829. slotScopeIds,
  830. false,
  831. )
  832. }
  833. if (patchFlag > 0) {
  834. // the presence of a patchFlag means this element's render code was
  835. // generated by the compiler and can take the fast path.
  836. // in this path old node and new node are guaranteed to have the same shape
  837. // (i.e. at the exact same position in the source template)
  838. if (patchFlag & PatchFlags.FULL_PROPS) {
  839. // element props contain dynamic keys, full diff needed
  840. patchProps(el, oldProps, newProps, parentComponent, namespace)
  841. } else {
  842. // class
  843. // this flag is matched when the element has dynamic class bindings.
  844. if (patchFlag & PatchFlags.CLASS) {
  845. if (oldProps.class !== newProps.class) {
  846. hostPatchProp(el, 'class', null, newProps.class, namespace)
  847. }
  848. }
  849. // style
  850. // this flag is matched when the element has dynamic style bindings
  851. if (patchFlag & PatchFlags.STYLE) {
  852. hostPatchProp(el, 'style', oldProps.style, newProps.style, namespace)
  853. }
  854. // props
  855. // This flag is matched when the element has dynamic prop/attr bindings
  856. // other than class and style. The keys of dynamic prop/attrs are saved for
  857. // faster iteration.
  858. // Note dynamic keys like :[foo]="bar" will cause this optimization to
  859. // bail out and go through a full diff because we need to unset the old key
  860. if (patchFlag & PatchFlags.PROPS) {
  861. // if the flag is present then dynamicProps must be non-null
  862. const propsToUpdate = n2.dynamicProps!
  863. for (let i = 0; i < propsToUpdate.length; i++) {
  864. const key = propsToUpdate[i]
  865. const prev = oldProps[key]
  866. const next = newProps[key]
  867. // #1471 force patch value
  868. if (next !== prev || key === 'value') {
  869. hostPatchProp(el, key, prev, next, namespace, parentComponent)
  870. }
  871. }
  872. }
  873. }
  874. // text
  875. // This flag is matched when the element has only dynamic text children.
  876. if (patchFlag & PatchFlags.TEXT) {
  877. if (n1.children !== n2.children) {
  878. hostSetElementText(el, n2.children as string)
  879. }
  880. }
  881. } else if (!optimized && dynamicChildren == null) {
  882. // unoptimized, full diff
  883. patchProps(el, oldProps, newProps, parentComponent, namespace)
  884. }
  885. if ((vnodeHook = newProps.onVnodeUpdated) || dirs) {
  886. queuePostRenderEffect(
  887. () => {
  888. vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, n2, n1)
  889. dirs && invokeDirectiveHook(n2, n1, parentComponent, 'updated')
  890. },
  891. undefined,
  892. parentSuspense,
  893. )
  894. }
  895. }
  896. // The fast path for blocks.
  897. const patchBlockChildren: PatchBlockChildrenFn = (
  898. oldChildren,
  899. newChildren,
  900. fallbackContainer,
  901. parentComponent,
  902. parentSuspense,
  903. namespace: ElementNamespace,
  904. slotScopeIds,
  905. ) => {
  906. for (let i = 0; i < newChildren.length; i++) {
  907. const oldVNode = oldChildren[i]
  908. const newVNode = newChildren[i]
  909. // Determine the container (parent element) for the patch.
  910. const container =
  911. // oldVNode may be an errored async setup() component inside Suspense
  912. // which will not have a mounted element
  913. oldVNode.el &&
  914. // - In the case of a Fragment, we need to provide the actual parent
  915. // of the Fragment itself so it can move its children.
  916. (oldVNode.type === Fragment ||
  917. // - In the case of different nodes, there is going to be a replacement
  918. // which also requires the correct parent container
  919. !isSameVNodeType(oldVNode, newVNode) ||
  920. // - In the case of a component, it could contain anything.
  921. oldVNode.shapeFlag &
  922. (ShapeFlags.COMPONENT | ShapeFlags.TELEPORT | ShapeFlags.SUSPENSE))
  923. ? hostParentNode(oldVNode.el)!
  924. : // In other cases, the parent container is not actually used so we
  925. // just pass the block element here to avoid a DOM parentNode call.
  926. fallbackContainer
  927. patch(
  928. oldVNode,
  929. newVNode,
  930. container,
  931. null,
  932. parentComponent,
  933. parentSuspense,
  934. namespace,
  935. slotScopeIds,
  936. true,
  937. )
  938. }
  939. }
  940. const patchProps = (
  941. el: RendererElement,
  942. oldProps: Data,
  943. newProps: Data,
  944. parentComponent: ComponentInternalInstance | null,
  945. namespace: ElementNamespace,
  946. ) => {
  947. if (oldProps !== newProps) {
  948. if (oldProps !== EMPTY_OBJ) {
  949. for (const key in oldProps) {
  950. if (!isReservedProp(key) && !(key in newProps)) {
  951. hostPatchProp(
  952. el,
  953. key,
  954. oldProps[key],
  955. null,
  956. namespace,
  957. parentComponent,
  958. )
  959. }
  960. }
  961. }
  962. for (const key in newProps) {
  963. // empty string is not valid prop
  964. if (isReservedProp(key)) continue
  965. const next = newProps[key]
  966. const prev = oldProps[key]
  967. // defer patching value
  968. if (next !== prev && key !== 'value') {
  969. hostPatchProp(el, key, prev, next, namespace, parentComponent)
  970. }
  971. }
  972. if ('value' in newProps) {
  973. hostPatchProp(el, 'value', oldProps.value, newProps.value, namespace)
  974. }
  975. }
  976. }
  977. const processFragment = (
  978. n1: VNode | null,
  979. n2: VNode,
  980. container: RendererElement,
  981. anchor: RendererNode | null,
  982. parentComponent: ComponentInternalInstance | null,
  983. parentSuspense: SuspenseBoundary | null,
  984. namespace: ElementNamespace,
  985. slotScopeIds: string[] | null,
  986. optimized: boolean,
  987. ) => {
  988. const fragmentStartAnchor = (n2.el = n1 ? n1.el : hostCreateText(''))!
  989. const fragmentEndAnchor = (n2.anchor = n1 ? n1.anchor : hostCreateText(''))!
  990. let { patchFlag, dynamicChildren, slotScopeIds: fragmentSlotScopeIds } = n2
  991. if (
  992. __DEV__ &&
  993. // #5523 dev root fragment may inherit directives
  994. (isHmrUpdating || patchFlag & PatchFlags.DEV_ROOT_FRAGMENT)
  995. ) {
  996. // HMR updated / Dev root fragment (w/ comments), force full diff
  997. patchFlag = 0
  998. optimized = false
  999. dynamicChildren = null
  1000. }
  1001. // check if this is a slot fragment with :slotted scope ids
  1002. if (fragmentSlotScopeIds) {
  1003. slotScopeIds = slotScopeIds
  1004. ? slotScopeIds.concat(fragmentSlotScopeIds)
  1005. : fragmentSlotScopeIds
  1006. }
  1007. if (n1 == null) {
  1008. hostInsert(fragmentStartAnchor, container, anchor)
  1009. hostInsert(fragmentEndAnchor, container, anchor)
  1010. // a fragment can only have array children
  1011. // since they are either generated by the compiler, or implicitly created
  1012. // from arrays.
  1013. mountChildren(
  1014. // #10007
  1015. // such fragment like `<></>` will be compiled into
  1016. // a fragment which doesn't have a children.
  1017. // In this case fallback to an empty array
  1018. (n2.children || []) as VNodeArrayChildren,
  1019. container,
  1020. fragmentEndAnchor,
  1021. parentComponent,
  1022. parentSuspense,
  1023. namespace,
  1024. slotScopeIds,
  1025. optimized,
  1026. )
  1027. } else {
  1028. if (
  1029. patchFlag > 0 &&
  1030. patchFlag & PatchFlags.STABLE_FRAGMENT &&
  1031. dynamicChildren &&
  1032. // #2715 the previous fragment could've been a BAILed one as a result
  1033. // of renderSlot() with no valid children
  1034. n1.dynamicChildren
  1035. ) {
  1036. // a stable fragment (template root or <template v-for>) doesn't need to
  1037. // patch children order, but it may contain dynamicChildren.
  1038. patchBlockChildren(
  1039. n1.dynamicChildren,
  1040. dynamicChildren,
  1041. container,
  1042. parentComponent,
  1043. parentSuspense,
  1044. namespace,
  1045. slotScopeIds,
  1046. )
  1047. if (__DEV__) {
  1048. // necessary for HMR
  1049. traverseStaticChildren(n1, n2)
  1050. } else if (
  1051. // #2080 if the stable fragment has a key, it's a <template v-for> that may
  1052. // get moved around. Make sure all root level vnodes inherit el.
  1053. // #2134 or if it's a component root, it may also get moved around
  1054. // as the component is being moved.
  1055. n2.key != null ||
  1056. (parentComponent && n2 === parentComponent.subTree)
  1057. ) {
  1058. traverseStaticChildren(n1, n2, true /* shallow */)
  1059. }
  1060. } else {
  1061. // keyed / unkeyed, or manual fragments.
  1062. // for keyed & unkeyed, since they are compiler generated from v-for,
  1063. // each child is guaranteed to be a block so the fragment will never
  1064. // have dynamicChildren.
  1065. patchChildren(
  1066. n1,
  1067. n2,
  1068. container,
  1069. fragmentEndAnchor,
  1070. parentComponent,
  1071. parentSuspense,
  1072. namespace,
  1073. slotScopeIds,
  1074. optimized,
  1075. )
  1076. }
  1077. }
  1078. }
  1079. const processComponent = (
  1080. n1: VNode | null,
  1081. n2: VNode,
  1082. container: RendererElement,
  1083. anchor: RendererNode | null,
  1084. parentComponent: ComponentInternalInstance | null,
  1085. parentSuspense: SuspenseBoundary | null,
  1086. namespace: ElementNamespace,
  1087. slotScopeIds: string[] | null,
  1088. optimized: boolean,
  1089. ) => {
  1090. n2.slotScopeIds = slotScopeIds
  1091. if ((n2.type as ConcreteComponent).__vapor) {
  1092. if (n1 == null) {
  1093. if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
  1094. getVaporInterface(parentComponent, n2).activate(
  1095. n2,
  1096. container,
  1097. anchor,
  1098. parentComponent!,
  1099. )
  1100. } else {
  1101. getVaporInterface(parentComponent, n2).mount(
  1102. n2,
  1103. container,
  1104. anchor,
  1105. parentComponent,
  1106. )
  1107. }
  1108. } else {
  1109. getVaporInterface(parentComponent, n2).update(
  1110. n1,
  1111. n2,
  1112. shouldUpdateComponent(n1, n2, optimized),
  1113. )
  1114. }
  1115. } else if (n1 == null) {
  1116. if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
  1117. ;(parentComponent!.ctx as KeepAliveContext).activate(
  1118. n2,
  1119. container,
  1120. anchor,
  1121. namespace,
  1122. optimized,
  1123. )
  1124. } else {
  1125. mountComponent(
  1126. n2,
  1127. container,
  1128. anchor,
  1129. parentComponent,
  1130. parentSuspense,
  1131. namespace,
  1132. optimized,
  1133. )
  1134. }
  1135. } else {
  1136. updateComponent(n1, n2, optimized)
  1137. }
  1138. }
  1139. const mountComponent: MountComponentFn = (
  1140. initialVNode,
  1141. container,
  1142. anchor,
  1143. parentComponent,
  1144. parentSuspense,
  1145. namespace: ElementNamespace,
  1146. optimized,
  1147. ) => {
  1148. // 2.x compat may pre-create the component instance before actually
  1149. // mounting
  1150. const compatMountInstance =
  1151. __COMPAT__ && initialVNode.isCompatRoot && initialVNode.component
  1152. const instance: ComponentInternalInstance =
  1153. compatMountInstance ||
  1154. (initialVNode.component = createComponentInstance(
  1155. initialVNode,
  1156. parentComponent,
  1157. parentSuspense,
  1158. ))
  1159. if (__DEV__ && instance.type.__hmrId) {
  1160. registerHMR(instance)
  1161. }
  1162. if (__DEV__) {
  1163. pushWarningContext(initialVNode)
  1164. startMeasure(instance, `mount`)
  1165. }
  1166. // inject renderer internals for keepAlive
  1167. if (isKeepAlive(initialVNode)) {
  1168. ;(instance.ctx as KeepAliveContext).renderer = internals
  1169. }
  1170. // resolve props and slots for setup context
  1171. if (!(__COMPAT__ && compatMountInstance)) {
  1172. if (__DEV__) {
  1173. startMeasure(instance, `init`)
  1174. }
  1175. setupComponent(instance, false, optimized)
  1176. if (__DEV__) {
  1177. endMeasure(instance, `init`)
  1178. }
  1179. }
  1180. // avoid hydration for hmr updating
  1181. if (__DEV__ && isHmrUpdating) initialVNode.el = null
  1182. // setup() is async. This component relies on async logic to be resolved
  1183. // before proceeding
  1184. if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
  1185. parentSuspense &&
  1186. parentSuspense.registerDep(instance, setupRenderEffect, optimized)
  1187. // Give it a placeholder if this is not hydration
  1188. // TODO handle self-defined fallback
  1189. if (!initialVNode.el) {
  1190. const placeholder = (instance.subTree = createVNode(Comment))
  1191. processCommentNode(null, placeholder, container!, anchor)
  1192. initialVNode.placeholder = placeholder.el
  1193. }
  1194. } else {
  1195. setupRenderEffect(
  1196. instance,
  1197. initialVNode,
  1198. container,
  1199. anchor,
  1200. parentSuspense,
  1201. namespace,
  1202. optimized,
  1203. )
  1204. }
  1205. if (__DEV__) {
  1206. popWarningContext()
  1207. endMeasure(instance, `mount`)
  1208. }
  1209. }
  1210. const updateComponent = (n1: VNode, n2: VNode, optimized: boolean) => {
  1211. const instance = (n2.component = n1.component)!
  1212. if (shouldUpdateComponent(n1, n2, optimized)) {
  1213. if (
  1214. __FEATURE_SUSPENSE__ &&
  1215. instance.asyncDep &&
  1216. !instance.asyncResolved
  1217. ) {
  1218. // async & still pending - just update props and slots
  1219. // since the component's reactive effect for render isn't set-up yet
  1220. if (__DEV__) {
  1221. pushWarningContext(n2)
  1222. }
  1223. updateComponentPreRender(instance, n2, optimized)
  1224. if (__DEV__) {
  1225. popWarningContext()
  1226. }
  1227. return
  1228. } else {
  1229. // normal update
  1230. instance.next = n2
  1231. // instance.update is the reactive effect.
  1232. instance.effect.run()
  1233. }
  1234. } else {
  1235. // no update needed. just copy over properties
  1236. n2.el = n1.el
  1237. instance.vnode = n2
  1238. }
  1239. }
  1240. class SetupRenderEffect extends ReactiveEffect {
  1241. job: SchedulerJob
  1242. constructor(
  1243. private instance: ComponentInternalInstance,
  1244. private initialVNode: VNode,
  1245. private container: RendererElement,
  1246. private anchor: RendererNode | null,
  1247. private parentSuspense: SuspenseBoundary | null,
  1248. private namespace: ElementNamespace,
  1249. private optimized: boolean,
  1250. ) {
  1251. const prevScope = setCurrentScope(instance.scope)
  1252. super()
  1253. setCurrentScope(prevScope)
  1254. this.job = instance.job = () => {
  1255. if (this.dirty) {
  1256. this.run()
  1257. }
  1258. }
  1259. this.job.i = instance
  1260. if (__DEV__) {
  1261. this.onTrack = instance.rtc
  1262. ? e => invokeArrayFns(instance.rtc!, e)
  1263. : void 0
  1264. this.onTrigger = instance.rtg
  1265. ? e => invokeArrayFns(instance.rtg!, e)
  1266. : void 0
  1267. }
  1268. }
  1269. notify(): void {
  1270. if (!(this.flags & EffectFlags.PAUSED)) {
  1271. const job = this.job
  1272. queueJob(job, job.i!.uid)
  1273. }
  1274. }
  1275. fn() {
  1276. const {
  1277. instance,
  1278. initialVNode,
  1279. container,
  1280. anchor,
  1281. parentSuspense,
  1282. namespace,
  1283. optimized,
  1284. } = this
  1285. if (!instance.isMounted) {
  1286. let vnodeHook: VNodeHook | null | undefined
  1287. const { el, props } = initialVNode
  1288. const { bm, m, parent, root, type } = instance
  1289. const isAsyncWrapperVNode = isAsyncWrapper(initialVNode)
  1290. toggleRecurse(instance, false)
  1291. // beforeMount hook
  1292. if (bm) {
  1293. invokeArrayFns(bm)
  1294. }
  1295. // onVnodeBeforeMount
  1296. if (
  1297. !isAsyncWrapperVNode &&
  1298. (vnodeHook = props && props.onVnodeBeforeMount)
  1299. ) {
  1300. invokeVNodeHook(vnodeHook, parent, initialVNode)
  1301. }
  1302. if (
  1303. __COMPAT__ &&
  1304. isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
  1305. ) {
  1306. instance.emit('hook:beforeMount')
  1307. }
  1308. toggleRecurse(instance, true)
  1309. if (el && hydrateNode) {
  1310. // vnode has adopted host node - perform hydration instead of mount.
  1311. const hydrateSubTree = () => {
  1312. if (__DEV__) {
  1313. startMeasure(instance, `render`)
  1314. }
  1315. instance.subTree = renderComponentRoot(instance)
  1316. if (__DEV__) {
  1317. endMeasure(instance, `render`)
  1318. }
  1319. if (__DEV__) {
  1320. startMeasure(instance, `hydrate`)
  1321. }
  1322. hydrateNode!(
  1323. el as Node,
  1324. instance.subTree,
  1325. instance,
  1326. parentSuspense,
  1327. null,
  1328. )
  1329. if (__DEV__) {
  1330. endMeasure(instance, `hydrate`)
  1331. }
  1332. }
  1333. if (
  1334. isAsyncWrapperVNode &&
  1335. (type as ComponentOptions).__asyncHydrate
  1336. ) {
  1337. ;(type as ComponentOptions).__asyncHydrate!(
  1338. el as Element,
  1339. instance,
  1340. hydrateSubTree,
  1341. )
  1342. } else {
  1343. hydrateSubTree()
  1344. }
  1345. } else {
  1346. // custom element style injection
  1347. if (
  1348. (root as ComponentInternalInstance).ce &&
  1349. // @ts-expect-error _def is private
  1350. ((root as ComponentInternalInstance).ce as VueElement)._def
  1351. .shadowRoot !== false
  1352. ) {
  1353. ;(root as ComponentInternalInstance).ce!._injectChildStyle(type)
  1354. }
  1355. if (__DEV__) {
  1356. startMeasure(instance, `render`)
  1357. }
  1358. const subTree = (instance.subTree = renderComponentRoot(instance))
  1359. if (__DEV__) {
  1360. endMeasure(instance, `render`)
  1361. }
  1362. if (__DEV__) {
  1363. startMeasure(instance, `patch`)
  1364. }
  1365. patch(
  1366. null,
  1367. subTree,
  1368. container,
  1369. anchor,
  1370. instance,
  1371. parentSuspense,
  1372. namespace,
  1373. )
  1374. if (__DEV__) {
  1375. endMeasure(instance, `patch`)
  1376. }
  1377. initialVNode.el = subTree.el
  1378. }
  1379. // mounted hook
  1380. if (m) {
  1381. queuePostRenderEffect(m, undefined, parentSuspense)
  1382. }
  1383. // onVnodeMounted
  1384. if (
  1385. !isAsyncWrapperVNode &&
  1386. (vnodeHook = props && props.onVnodeMounted)
  1387. ) {
  1388. const scopedInitialVNode = initialVNode
  1389. queuePostRenderEffect(
  1390. () => invokeVNodeHook(vnodeHook!, parent, scopedInitialVNode),
  1391. undefined,
  1392. parentSuspense,
  1393. )
  1394. }
  1395. if (
  1396. __COMPAT__ &&
  1397. isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
  1398. ) {
  1399. queuePostRenderEffect(
  1400. () => instance.emit('hook:mounted'),
  1401. undefined,
  1402. parentSuspense,
  1403. )
  1404. }
  1405. // activated hook for keep-alive roots.
  1406. // #1742 activated hook must be accessed after first render
  1407. // since the hook may be injected by a child keep-alive
  1408. if (
  1409. initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE ||
  1410. (parent &&
  1411. parent.vnode &&
  1412. isAsyncWrapper(parent.vnode) &&
  1413. parent.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE)
  1414. ) {
  1415. instance.a &&
  1416. queuePostRenderEffect(instance.a, undefined, parentSuspense)
  1417. if (
  1418. __COMPAT__ &&
  1419. isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
  1420. ) {
  1421. queuePostRenderEffect(
  1422. () => instance.emit('hook:activated'),
  1423. undefined,
  1424. parentSuspense,
  1425. )
  1426. }
  1427. }
  1428. instance.isMounted = true
  1429. if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
  1430. devtoolsComponentAdded(instance)
  1431. }
  1432. // #2458: deference mount-only object parameters to prevent memleaks
  1433. this.initialVNode = this.container = this.anchor = null as any
  1434. } else {
  1435. let { next, bu, u, parent, vnode } = instance
  1436. if (__FEATURE_SUSPENSE__) {
  1437. const nonHydratedAsyncRoot = locateNonHydratedAsyncRoot(instance)
  1438. // we are trying to update some async comp before hydration
  1439. // this will cause crash because we don't know the root node yet
  1440. if (nonHydratedAsyncRoot) {
  1441. // only sync the properties and abort the rest of operations
  1442. if (next) {
  1443. next.el = vnode.el
  1444. updateComponentPreRender(instance, next, optimized)
  1445. }
  1446. // and continue the rest of operations once the deps are resolved
  1447. nonHydratedAsyncRoot.asyncDep!.then(() => {
  1448. // the instance may be destroyed during the time period
  1449. if (!instance.isUnmounted) {
  1450. this.fn()
  1451. }
  1452. })
  1453. return
  1454. }
  1455. }
  1456. // updateComponent
  1457. // This is triggered by mutation of component's own state (next: null)
  1458. // OR parent calling processComponent (next: VNode)
  1459. let originNext = next
  1460. let vnodeHook: VNodeHook | null | undefined
  1461. if (__DEV__) {
  1462. pushWarningContext(next || instance.vnode)
  1463. }
  1464. // Disallow component effect recursion during pre-lifecycle hooks.
  1465. toggleRecurse(instance, false)
  1466. if (next) {
  1467. next.el = vnode.el
  1468. updateComponentPreRender(instance, next, optimized)
  1469. } else {
  1470. next = vnode
  1471. }
  1472. // beforeUpdate hook
  1473. if (bu) {
  1474. invokeArrayFns(bu)
  1475. }
  1476. // onVnodeBeforeUpdate
  1477. if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
  1478. invokeVNodeHook(vnodeHook, parent, next, vnode)
  1479. }
  1480. if (
  1481. __COMPAT__ &&
  1482. isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
  1483. ) {
  1484. instance.emit('hook:beforeUpdate')
  1485. }
  1486. toggleRecurse(instance, true)
  1487. // render
  1488. if (__DEV__) {
  1489. startMeasure(instance, `render`)
  1490. }
  1491. const nextTree = renderComponentRoot(instance)
  1492. if (__DEV__) {
  1493. endMeasure(instance, `render`)
  1494. }
  1495. const prevTree = instance.subTree
  1496. instance.subTree = nextTree
  1497. if (__DEV__) {
  1498. startMeasure(instance, `patch`)
  1499. }
  1500. patch(
  1501. prevTree,
  1502. nextTree,
  1503. // parent may have changed if it's in a teleport
  1504. hostParentNode(prevTree.el!)!,
  1505. // anchor may have changed if it's in a fragment
  1506. getNextHostNode(prevTree),
  1507. instance,
  1508. parentSuspense,
  1509. namespace,
  1510. )
  1511. if (__DEV__) {
  1512. endMeasure(instance, `patch`)
  1513. }
  1514. next.el = nextTree.el
  1515. if (originNext === null) {
  1516. // self-triggered update. In case of HOC, update parent component
  1517. // vnode el. HOC is indicated by parent instance's subTree pointing
  1518. // to child component's vnode
  1519. updateHOCHostEl(instance, nextTree.el)
  1520. }
  1521. // updated hook
  1522. if (u) {
  1523. queuePostRenderEffect(u, undefined, parentSuspense)
  1524. }
  1525. // onVnodeUpdated
  1526. if ((vnodeHook = next.props && next.props.onVnodeUpdated)) {
  1527. queuePostRenderEffect(
  1528. () => invokeVNodeHook(vnodeHook!, parent, next!, vnode),
  1529. undefined,
  1530. parentSuspense,
  1531. )
  1532. }
  1533. if (
  1534. __COMPAT__ &&
  1535. isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
  1536. ) {
  1537. queuePostRenderEffect(
  1538. () => instance.emit('hook:updated'),
  1539. undefined,
  1540. parentSuspense,
  1541. )
  1542. }
  1543. if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
  1544. devtoolsComponentUpdated(instance)
  1545. }
  1546. if (__DEV__) {
  1547. popWarningContext()
  1548. }
  1549. }
  1550. }
  1551. }
  1552. const setupRenderEffect: SetupRenderEffectFn = (
  1553. instance,
  1554. initialVNode,
  1555. container,
  1556. anchor,
  1557. parentSuspense,
  1558. namespace: ElementNamespace,
  1559. optimized,
  1560. ) => {
  1561. // create reactive effect for rendering
  1562. const effect = (instance.effect = new SetupRenderEffect(
  1563. instance,
  1564. initialVNode,
  1565. container,
  1566. anchor,
  1567. parentSuspense,
  1568. namespace,
  1569. optimized,
  1570. ))
  1571. instance.update = effect.run.bind(effect)
  1572. // allowRecurse
  1573. // #1801, #2043 component render effects should allow recursive updates
  1574. toggleRecurse(instance, true)
  1575. effect.run()
  1576. }
  1577. const updateComponentPreRender = (
  1578. instance: ComponentInternalInstance,
  1579. nextVNode: VNode,
  1580. optimized: boolean,
  1581. ) => {
  1582. nextVNode.component = instance
  1583. const prevProps = instance.vnode.props
  1584. instance.vnode = nextVNode
  1585. instance.next = null
  1586. updateProps(instance, nextVNode.props, prevProps, optimized)
  1587. updateSlots(instance, nextVNode.children, optimized)
  1588. const prevSub = setActiveSub()
  1589. // props update may have triggered pre-flush watchers.
  1590. // flush them before the render update.
  1591. flushPreFlushCbs(instance)
  1592. setActiveSub(prevSub)
  1593. }
  1594. const patchChildren: PatchChildrenFn = (
  1595. n1,
  1596. n2,
  1597. container,
  1598. anchor,
  1599. parentComponent,
  1600. parentSuspense,
  1601. namespace: ElementNamespace,
  1602. slotScopeIds,
  1603. optimized = false,
  1604. ) => {
  1605. const c1 = n1 && n1.children
  1606. const prevShapeFlag = n1 ? n1.shapeFlag : 0
  1607. const c2 = n2.children
  1608. const { patchFlag, shapeFlag } = n2
  1609. // fast path
  1610. if (patchFlag > 0) {
  1611. if (patchFlag & PatchFlags.KEYED_FRAGMENT) {
  1612. // this could be either fully-keyed or mixed (some keyed some not)
  1613. // presence of patchFlag means children are guaranteed to be arrays
  1614. patchKeyedChildren(
  1615. c1 as VNode[],
  1616. c2 as VNodeArrayChildren,
  1617. container,
  1618. anchor,
  1619. parentComponent,
  1620. parentSuspense,
  1621. namespace,
  1622. slotScopeIds,
  1623. optimized,
  1624. )
  1625. return
  1626. } else if (patchFlag & PatchFlags.UNKEYED_FRAGMENT) {
  1627. // unkeyed
  1628. patchUnkeyedChildren(
  1629. c1 as VNode[],
  1630. c2 as VNodeArrayChildren,
  1631. container,
  1632. anchor,
  1633. parentComponent,
  1634. parentSuspense,
  1635. namespace,
  1636. slotScopeIds,
  1637. optimized,
  1638. )
  1639. return
  1640. }
  1641. }
  1642. // children has 3 possibilities: text, array or no children.
  1643. if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
  1644. // text children fast path
  1645. if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
  1646. unmountChildren(c1 as VNode[], parentComponent, parentSuspense)
  1647. }
  1648. if (c2 !== c1) {
  1649. hostSetElementText(container, c2 as string)
  1650. }
  1651. } else {
  1652. if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
  1653. // prev children was array
  1654. if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
  1655. // two arrays, cannot assume anything, do full diff
  1656. patchKeyedChildren(
  1657. c1 as VNode[],
  1658. c2 as VNodeArrayChildren,
  1659. container,
  1660. anchor,
  1661. parentComponent,
  1662. parentSuspense,
  1663. namespace,
  1664. slotScopeIds,
  1665. optimized,
  1666. )
  1667. } else {
  1668. // no new children, just unmount old
  1669. unmountChildren(c1 as VNode[], parentComponent, parentSuspense, true)
  1670. }
  1671. } else {
  1672. // prev children was text OR null
  1673. // new children is array OR null
  1674. if (prevShapeFlag & ShapeFlags.TEXT_CHILDREN) {
  1675. hostSetElementText(container, '')
  1676. }
  1677. // mount new if array
  1678. if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
  1679. mountChildren(
  1680. c2 as VNodeArrayChildren,
  1681. container,
  1682. anchor,
  1683. parentComponent,
  1684. parentSuspense,
  1685. namespace,
  1686. slotScopeIds,
  1687. optimized,
  1688. )
  1689. }
  1690. }
  1691. }
  1692. }
  1693. const patchUnkeyedChildren = (
  1694. c1: VNode[],
  1695. c2: VNodeArrayChildren,
  1696. container: RendererElement,
  1697. anchor: RendererNode | null,
  1698. parentComponent: ComponentInternalInstance | null,
  1699. parentSuspense: SuspenseBoundary | null,
  1700. namespace: ElementNamespace,
  1701. slotScopeIds: string[] | null,
  1702. optimized: boolean,
  1703. ) => {
  1704. c1 = c1 || EMPTY_ARR
  1705. c2 = c2 || EMPTY_ARR
  1706. const oldLength = c1.length
  1707. const newLength = c2.length
  1708. const commonLength = Math.min(oldLength, newLength)
  1709. let i
  1710. for (i = 0; i < commonLength; i++) {
  1711. const nextChild = (c2[i] = optimized
  1712. ? cloneIfMounted(c2[i] as VNode)
  1713. : normalizeVNode(c2[i]))
  1714. patch(
  1715. c1[i],
  1716. nextChild,
  1717. container,
  1718. null,
  1719. parentComponent,
  1720. parentSuspense,
  1721. namespace,
  1722. slotScopeIds,
  1723. optimized,
  1724. )
  1725. }
  1726. if (oldLength > newLength) {
  1727. // remove old
  1728. unmountChildren(
  1729. c1,
  1730. parentComponent,
  1731. parentSuspense,
  1732. true,
  1733. false,
  1734. commonLength,
  1735. )
  1736. } else {
  1737. // mount new
  1738. mountChildren(
  1739. c2,
  1740. container,
  1741. anchor,
  1742. parentComponent,
  1743. parentSuspense,
  1744. namespace,
  1745. slotScopeIds,
  1746. optimized,
  1747. commonLength,
  1748. )
  1749. }
  1750. }
  1751. // can be all-keyed or mixed
  1752. const patchKeyedChildren = (
  1753. c1: VNode[],
  1754. c2: VNodeArrayChildren,
  1755. container: RendererElement,
  1756. parentAnchor: RendererNode | null,
  1757. parentComponent: ComponentInternalInstance | null,
  1758. parentSuspense: SuspenseBoundary | null,
  1759. namespace: ElementNamespace,
  1760. slotScopeIds: string[] | null,
  1761. optimized: boolean,
  1762. ) => {
  1763. let i = 0
  1764. const l2 = c2.length
  1765. let e1 = c1.length - 1 // prev ending index
  1766. let e2 = l2 - 1 // next ending index
  1767. // 1. sync from start
  1768. // (a b) c
  1769. // (a b) d e
  1770. while (i <= e1 && i <= e2) {
  1771. const n1 = c1[i]
  1772. const n2 = (c2[i] = optimized
  1773. ? cloneIfMounted(c2[i] as VNode)
  1774. : normalizeVNode(c2[i]))
  1775. if (isSameVNodeType(n1, n2)) {
  1776. patch(
  1777. n1,
  1778. n2,
  1779. container,
  1780. null,
  1781. parentComponent,
  1782. parentSuspense,
  1783. namespace,
  1784. slotScopeIds,
  1785. optimized,
  1786. )
  1787. } else {
  1788. break
  1789. }
  1790. i++
  1791. }
  1792. // 2. sync from end
  1793. // a (b c)
  1794. // d e (b c)
  1795. while (i <= e1 && i <= e2) {
  1796. const n1 = c1[e1]
  1797. const n2 = (c2[e2] = optimized
  1798. ? cloneIfMounted(c2[e2] as VNode)
  1799. : normalizeVNode(c2[e2]))
  1800. if (isSameVNodeType(n1, n2)) {
  1801. patch(
  1802. n1,
  1803. n2,
  1804. container,
  1805. null,
  1806. parentComponent,
  1807. parentSuspense,
  1808. namespace,
  1809. slotScopeIds,
  1810. optimized,
  1811. )
  1812. } else {
  1813. break
  1814. }
  1815. e1--
  1816. e2--
  1817. }
  1818. // 3. common sequence + mount
  1819. // (a b)
  1820. // (a b) c
  1821. // i = 2, e1 = 1, e2 = 2
  1822. // (a b)
  1823. // c (a b)
  1824. // i = 0, e1 = -1, e2 = 0
  1825. if (i > e1) {
  1826. if (i <= e2) {
  1827. const nextPos = e2 + 1
  1828. const anchor = nextPos < l2 ? (c2[nextPos] as VNode).el : parentAnchor
  1829. while (i <= e2) {
  1830. patch(
  1831. null,
  1832. (c2[i] = optimized
  1833. ? cloneIfMounted(c2[i] as VNode)
  1834. : normalizeVNode(c2[i])),
  1835. container,
  1836. anchor,
  1837. parentComponent,
  1838. parentSuspense,
  1839. namespace,
  1840. slotScopeIds,
  1841. optimized,
  1842. )
  1843. i++
  1844. }
  1845. }
  1846. }
  1847. // 4. common sequence + unmount
  1848. // (a b) c
  1849. // (a b)
  1850. // i = 2, e1 = 2, e2 = 1
  1851. // a (b c)
  1852. // (b c)
  1853. // i = 0, e1 = 0, e2 = -1
  1854. else if (i > e2) {
  1855. while (i <= e1) {
  1856. unmount(c1[i], parentComponent, parentSuspense, true)
  1857. i++
  1858. }
  1859. }
  1860. // 5. unknown sequence
  1861. // [i ... e1 + 1]: a b [c d e] f g
  1862. // [i ... e2 + 1]: a b [e d c h] f g
  1863. // i = 2, e1 = 4, e2 = 5
  1864. else {
  1865. const s1 = i // prev starting index
  1866. const s2 = i // next starting index
  1867. // 5.1 build key:index map for newChildren
  1868. const keyToNewIndexMap: Map<PropertyKey, number> = new Map()
  1869. for (i = s2; i <= e2; i++) {
  1870. const nextChild = (c2[i] = optimized
  1871. ? cloneIfMounted(c2[i] as VNode)
  1872. : normalizeVNode(c2[i]))
  1873. if (nextChild.key != null) {
  1874. if (__DEV__ && keyToNewIndexMap.has(nextChild.key)) {
  1875. warn(
  1876. `Duplicate keys found during update:`,
  1877. JSON.stringify(nextChild.key),
  1878. `Make sure keys are unique.`,
  1879. )
  1880. }
  1881. keyToNewIndexMap.set(nextChild.key, i)
  1882. }
  1883. }
  1884. // 5.2 loop through old children left to be patched and try to patch
  1885. // matching nodes & remove nodes that are no longer present
  1886. let j
  1887. let patched = 0
  1888. const toBePatched = e2 - s2 + 1
  1889. let moved = false
  1890. // used to track whether any node has moved
  1891. let maxNewIndexSoFar = 0
  1892. // works as Map<newIndex, oldIndex>
  1893. // Note that oldIndex is offset by +1
  1894. // and oldIndex = 0 is a special value indicating the new node has
  1895. // no corresponding old node.
  1896. // used for determining longest stable subsequence
  1897. const newIndexToOldIndexMap = new Array(toBePatched)
  1898. for (i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0
  1899. for (i = s1; i <= e1; i++) {
  1900. const prevChild = c1[i]
  1901. if (patched >= toBePatched) {
  1902. // all new children have been patched so this can only be a removal
  1903. unmount(prevChild, parentComponent, parentSuspense, true)
  1904. continue
  1905. }
  1906. let newIndex
  1907. if (prevChild.key != null) {
  1908. newIndex = keyToNewIndexMap.get(prevChild.key)
  1909. } else {
  1910. // key-less node, try to locate a key-less node of the same type
  1911. for (j = s2; j <= e2; j++) {
  1912. if (
  1913. newIndexToOldIndexMap[j - s2] === 0 &&
  1914. isSameVNodeType(prevChild, c2[j] as VNode)
  1915. ) {
  1916. newIndex = j
  1917. break
  1918. }
  1919. }
  1920. }
  1921. if (newIndex === undefined) {
  1922. unmount(prevChild, parentComponent, parentSuspense, true)
  1923. } else {
  1924. newIndexToOldIndexMap[newIndex - s2] = i + 1
  1925. if (newIndex >= maxNewIndexSoFar) {
  1926. maxNewIndexSoFar = newIndex
  1927. } else {
  1928. moved = true
  1929. }
  1930. patch(
  1931. prevChild,
  1932. c2[newIndex] as VNode,
  1933. container,
  1934. null,
  1935. parentComponent,
  1936. parentSuspense,
  1937. namespace,
  1938. slotScopeIds,
  1939. optimized,
  1940. )
  1941. patched++
  1942. }
  1943. }
  1944. // 5.3 move and mount
  1945. // generate longest stable subsequence only when nodes have moved
  1946. const increasingNewIndexSequence = moved
  1947. ? getSequence(newIndexToOldIndexMap)
  1948. : EMPTY_ARR
  1949. j = increasingNewIndexSequence.length - 1
  1950. // looping backwards so that we can use last patched node as anchor
  1951. for (i = toBePatched - 1; i >= 0; i--) {
  1952. const nextIndex = s2 + i
  1953. const nextChild = c2[nextIndex] as VNode
  1954. const anchorVNode = c2[nextIndex + 1] as VNode
  1955. const anchor =
  1956. nextIndex + 1 < l2
  1957. ? // #13559, fallback to el placeholder for unresolved async component
  1958. anchorVNode.el || anchorVNode.placeholder
  1959. : parentAnchor
  1960. if (newIndexToOldIndexMap[i] === 0) {
  1961. // mount new
  1962. patch(
  1963. null,
  1964. nextChild,
  1965. container,
  1966. anchor,
  1967. parentComponent,
  1968. parentSuspense,
  1969. namespace,
  1970. slotScopeIds,
  1971. optimized,
  1972. )
  1973. } else if (moved) {
  1974. // move if:
  1975. // There is no stable subsequence (e.g. a reverse)
  1976. // OR current node is not among the stable sequence
  1977. if (j < 0 || i !== increasingNewIndexSequence[j]) {
  1978. move(
  1979. nextChild,
  1980. container,
  1981. anchor,
  1982. MoveType.REORDER,
  1983. parentComponent,
  1984. )
  1985. } else {
  1986. j--
  1987. }
  1988. }
  1989. }
  1990. }
  1991. }
  1992. const move: MoveFn = (
  1993. vnode,
  1994. container,
  1995. anchor,
  1996. moveType,
  1997. parentComponent,
  1998. parentSuspense = null,
  1999. ) => {
  2000. const { el, type, transition, children, shapeFlag } = vnode
  2001. if (shapeFlag & ShapeFlags.COMPONENT) {
  2002. if ((type as ConcreteComponent).__vapor) {
  2003. getVaporInterface(parentComponent, vnode).move(vnode, container, anchor)
  2004. } else {
  2005. move(
  2006. vnode.component!.subTree,
  2007. container,
  2008. anchor,
  2009. moveType,
  2010. parentComponent,
  2011. )
  2012. }
  2013. return
  2014. }
  2015. if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
  2016. vnode.suspense!.move(container, anchor, moveType)
  2017. return
  2018. }
  2019. if (shapeFlag & ShapeFlags.TELEPORT) {
  2020. ;(type as typeof TeleportImpl).move(
  2021. vnode,
  2022. container,
  2023. anchor,
  2024. internals,
  2025. parentComponent,
  2026. )
  2027. return
  2028. }
  2029. if (type === Fragment) {
  2030. hostInsert(el!, container, anchor)
  2031. for (let i = 0; i < (children as VNode[]).length; i++) {
  2032. move(
  2033. (children as VNode[])[i],
  2034. container,
  2035. anchor,
  2036. moveType,
  2037. parentComponent,
  2038. )
  2039. }
  2040. hostInsert(vnode.anchor!, container, anchor)
  2041. return
  2042. }
  2043. if (type === Static) {
  2044. moveStaticNode(vnode, container, anchor)
  2045. return
  2046. }
  2047. // single nodes
  2048. const needTransition =
  2049. moveType !== MoveType.REORDER &&
  2050. shapeFlag & ShapeFlags.ELEMENT &&
  2051. transition
  2052. if (needTransition) {
  2053. if (moveType === MoveType.ENTER) {
  2054. performTransitionEnter(
  2055. el!,
  2056. transition,
  2057. () => hostInsert(el!, container, anchor),
  2058. parentSuspense,
  2059. true,
  2060. )
  2061. } else {
  2062. const { leave, delayLeave, afterLeave } = transition!
  2063. const remove = () => {
  2064. if (vnode.ctx!.isUnmounted) {
  2065. hostRemove(el!)
  2066. } else {
  2067. hostInsert(el!, container, anchor)
  2068. }
  2069. }
  2070. const performLeave = () => {
  2071. // #13153 move kept-alive node before v-show transition leave finishes
  2072. // it needs to call the leaving callback to ensure element's `display`
  2073. // is `none`
  2074. if (el!._isLeaving) {
  2075. el![leaveCbKey](true /* cancelled */)
  2076. }
  2077. leave(el!, () => {
  2078. remove()
  2079. afterLeave && afterLeave()
  2080. })
  2081. }
  2082. if (delayLeave) {
  2083. delayLeave(el!, remove, performLeave)
  2084. } else {
  2085. performLeave()
  2086. }
  2087. }
  2088. } else {
  2089. hostInsert(el!, container, anchor)
  2090. }
  2091. }
  2092. const unmount: UnmountFn = (
  2093. vnode,
  2094. parentComponent,
  2095. parentSuspense,
  2096. doRemove = false,
  2097. optimized = false,
  2098. ) => {
  2099. const {
  2100. type,
  2101. props,
  2102. ref,
  2103. children,
  2104. dynamicChildren,
  2105. shapeFlag,
  2106. patchFlag,
  2107. dirs,
  2108. cacheIndex,
  2109. } = vnode
  2110. if (patchFlag === PatchFlags.BAIL) {
  2111. optimized = false
  2112. }
  2113. // unset ref
  2114. if (ref != null) {
  2115. const prevSub = setActiveSub()
  2116. setRef(ref, null, parentSuspense, vnode, true)
  2117. setActiveSub(prevSub)
  2118. }
  2119. // #6593 should clean memo cache when unmount
  2120. if (cacheIndex != null) {
  2121. parentComponent!.renderCache[cacheIndex] = undefined
  2122. }
  2123. if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
  2124. if ((vnode.type as ConcreteComponent).__vapor) {
  2125. getVaporInterface(parentComponent!, vnode).deactivate(
  2126. vnode,
  2127. (parentComponent!.ctx as KeepAliveContext).getStorageContainer(),
  2128. )
  2129. } else {
  2130. ;(parentComponent!.ctx as KeepAliveContext).deactivate(vnode)
  2131. }
  2132. return
  2133. }
  2134. const shouldInvokeDirs = shapeFlag & ShapeFlags.ELEMENT && dirs
  2135. const shouldInvokeVnodeHook = !isAsyncWrapper(vnode)
  2136. let vnodeHook: VNodeHook | undefined | null
  2137. if (
  2138. shouldInvokeVnodeHook &&
  2139. (vnodeHook = props && props.onVnodeBeforeUnmount)
  2140. ) {
  2141. invokeVNodeHook(vnodeHook, parentComponent, vnode)
  2142. }
  2143. if (shapeFlag & ShapeFlags.COMPONENT) {
  2144. if ((type as ConcreteComponent).__vapor) {
  2145. getVaporInterface(parentComponent, vnode).unmount(vnode, doRemove)
  2146. return
  2147. } else {
  2148. unmountComponent(vnode.component!, parentSuspense, doRemove)
  2149. }
  2150. } else {
  2151. if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
  2152. vnode.suspense!.unmount(parentSuspense, doRemove)
  2153. return
  2154. }
  2155. if (shouldInvokeDirs) {
  2156. invokeDirectiveHook(vnode, null, parentComponent, 'beforeUnmount')
  2157. }
  2158. if (shapeFlag & ShapeFlags.TELEPORT) {
  2159. ;(vnode.type as typeof TeleportImpl).remove(
  2160. vnode,
  2161. parentComponent,
  2162. parentSuspense,
  2163. internals,
  2164. doRemove,
  2165. )
  2166. } else if (
  2167. dynamicChildren &&
  2168. // #5154
  2169. // when v-once is used inside a block, setBlockTracking(-1) marks the
  2170. // parent block with hasOnce: true
  2171. // so that it doesn't take the fast path during unmount - otherwise
  2172. // components nested in v-once are never unmounted.
  2173. !dynamicChildren.hasOnce &&
  2174. // #1153: fast path should not be taken for non-stable (v-for) fragments
  2175. (type !== Fragment ||
  2176. (patchFlag > 0 && patchFlag & PatchFlags.STABLE_FRAGMENT))
  2177. ) {
  2178. // fast path for block nodes: only need to unmount dynamic children.
  2179. unmountChildren(
  2180. dynamicChildren,
  2181. parentComponent,
  2182. parentSuspense,
  2183. false,
  2184. true,
  2185. )
  2186. } else if (
  2187. (type === Fragment &&
  2188. patchFlag &
  2189. (PatchFlags.KEYED_FRAGMENT | PatchFlags.UNKEYED_FRAGMENT)) ||
  2190. (!optimized && shapeFlag & ShapeFlags.ARRAY_CHILDREN)
  2191. ) {
  2192. unmountChildren(children as VNode[], parentComponent, parentSuspense)
  2193. }
  2194. if (type === VaporSlot) {
  2195. getVaporInterface(parentComponent, vnode).unmount(vnode, doRemove)
  2196. return
  2197. }
  2198. if (doRemove) {
  2199. remove(vnode)
  2200. }
  2201. }
  2202. if (
  2203. (shouldInvokeVnodeHook &&
  2204. (vnodeHook = props && props.onVnodeUnmounted)) ||
  2205. shouldInvokeDirs
  2206. ) {
  2207. queuePostRenderEffect(
  2208. () => {
  2209. vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
  2210. shouldInvokeDirs &&
  2211. invokeDirectiveHook(vnode, null, parentComponent, 'unmounted')
  2212. },
  2213. undefined,
  2214. parentSuspense,
  2215. )
  2216. }
  2217. }
  2218. const remove: RemoveFn = vnode => {
  2219. const { type, el, anchor, transition } = vnode
  2220. if (type === Fragment) {
  2221. if (
  2222. __DEV__ &&
  2223. vnode.patchFlag > 0 &&
  2224. vnode.patchFlag & PatchFlags.DEV_ROOT_FRAGMENT &&
  2225. transition &&
  2226. !transition.persisted
  2227. ) {
  2228. ;(vnode.children as VNode[]).forEach(child => {
  2229. if (child.type === Comment) {
  2230. hostRemove(child.el!)
  2231. } else {
  2232. remove(child)
  2233. }
  2234. })
  2235. } else {
  2236. removeFragment(el!, anchor!)
  2237. }
  2238. return
  2239. }
  2240. if (type === Static) {
  2241. removeStaticNode(vnode)
  2242. return
  2243. }
  2244. if (transition) {
  2245. performTransitionLeave(
  2246. el!,
  2247. transition,
  2248. () => hostRemove(el!),
  2249. !!(vnode.shapeFlag & ShapeFlags.ELEMENT),
  2250. )
  2251. } else {
  2252. hostRemove(el!)
  2253. }
  2254. }
  2255. const removeFragment = (cur: RendererNode, end: RendererNode) => {
  2256. // For fragments, directly remove all contained DOM nodes.
  2257. // (fragment child nodes cannot have transition)
  2258. let next
  2259. while (cur !== end) {
  2260. next = hostNextSibling(cur)!
  2261. hostRemove(cur)
  2262. cur = next
  2263. }
  2264. hostRemove(end)
  2265. }
  2266. const unmountComponent: UnmountComponentFn = (
  2267. instance,
  2268. parentSuspense,
  2269. doRemove,
  2270. ) => {
  2271. if (__DEV__ && instance.type.__hmrId) {
  2272. unregisterHMR(instance)
  2273. }
  2274. const { bum, scope, effect, subTree, um, m, a } = instance
  2275. invalidateMount(m)
  2276. invalidateMount(a)
  2277. // beforeUnmount hook
  2278. if (bum) {
  2279. invokeArrayFns(bum)
  2280. }
  2281. if (
  2282. __COMPAT__ &&
  2283. isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
  2284. ) {
  2285. instance.emit('hook:beforeDestroy')
  2286. }
  2287. // stop effects in component scope
  2288. scope.stop()
  2289. // job may be null if a component is unmounted before its async
  2290. // setup has resolved.
  2291. if (effect) {
  2292. // so that scheduler will no longer invoke it
  2293. effect.stop()
  2294. unmount(subTree, instance, parentSuspense, doRemove)
  2295. }
  2296. // unmounted hook
  2297. if (um) {
  2298. queuePostRenderEffect(um, undefined, parentSuspense)
  2299. }
  2300. if (
  2301. __COMPAT__ &&
  2302. isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
  2303. ) {
  2304. queuePostRenderEffect(
  2305. () => instance.emit('hook:destroyed'),
  2306. undefined,
  2307. parentSuspense,
  2308. )
  2309. }
  2310. queuePostRenderEffect(
  2311. () => (instance.isUnmounted = true),
  2312. undefined,
  2313. parentSuspense,
  2314. )
  2315. if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
  2316. devtoolsComponentRemoved(instance)
  2317. }
  2318. }
  2319. const unmountChildren: UnmountChildrenFn = (
  2320. children,
  2321. parentComponent,
  2322. parentSuspense,
  2323. doRemove = false,
  2324. optimized = false,
  2325. start = 0,
  2326. ) => {
  2327. for (let i = start; i < children.length; i++) {
  2328. unmount(children[i], parentComponent, parentSuspense, doRemove, optimized)
  2329. }
  2330. }
  2331. const getNextHostNode: NextFn = vnode => {
  2332. if (vnode.shapeFlag & ShapeFlags.COMPONENT) {
  2333. if ((vnode.type as ConcreteComponent).__vapor) {
  2334. return hostNextSibling(vnode.anchor!)
  2335. }
  2336. return getNextHostNode(vnode.component!.subTree)
  2337. }
  2338. if (__FEATURE_SUSPENSE__ && vnode.shapeFlag & ShapeFlags.SUSPENSE) {
  2339. return vnode.suspense!.next()
  2340. }
  2341. const el = hostNextSibling((vnode.anchor || vnode.el)!)
  2342. // #9071, #9313
  2343. // teleported content can mess up nextSibling searches during patch so
  2344. // we need to skip them during nextSibling search
  2345. const teleportEnd = el && el[TeleportEndKey]
  2346. return teleportEnd ? hostNextSibling(teleportEnd) : el
  2347. }
  2348. const render: RootRenderFunction = (vnode, container, namespace) => {
  2349. if (vnode == null) {
  2350. if (container._vnode) {
  2351. unmount(container._vnode, null, null, true)
  2352. }
  2353. } else {
  2354. patch(
  2355. container._vnode || null,
  2356. vnode,
  2357. container,
  2358. null,
  2359. null,
  2360. null,
  2361. namespace,
  2362. )
  2363. }
  2364. container._vnode = vnode
  2365. flushOnAppMount()
  2366. }
  2367. const internals: RendererInternals = {
  2368. p: patch,
  2369. um: unmount,
  2370. m: move,
  2371. r: remove,
  2372. mt: mountComponent,
  2373. umt: unmountComponent,
  2374. mc: mountChildren,
  2375. pc: patchChildren,
  2376. pbc: patchBlockChildren,
  2377. n: getNextHostNode,
  2378. o: options,
  2379. }
  2380. let hydrate: ReturnType<typeof createHydrationFunctions>[0] | undefined
  2381. let hydrateNode: ReturnType<typeof createHydrationFunctions>[1] | undefined
  2382. if (createHydrationFns) {
  2383. ;[hydrate, hydrateNode] = createHydrationFns(
  2384. internals as RendererInternals<Node, Element>,
  2385. )
  2386. }
  2387. const mountApp: AppMountFn<Element> = (
  2388. app,
  2389. container,
  2390. isHydrate,
  2391. namespace,
  2392. ) => {
  2393. const vnode = app._ceVNode || createVNode(app._component, app._props)
  2394. // store app context on the root VNode.
  2395. // this will be set on the root instance on initial mount.
  2396. vnode.appContext = app._context
  2397. if (namespace === true) {
  2398. namespace = 'svg'
  2399. } else if (namespace === false) {
  2400. namespace = undefined
  2401. }
  2402. // HMR root reload
  2403. if (__DEV__) {
  2404. app._context.reload = () => {
  2405. const cloned = cloneVNode(vnode)
  2406. // avoid hydration for hmr updating
  2407. cloned.el = null
  2408. // casting to ElementNamespace because TS doesn't guarantee type narrowing
  2409. // over function boundaries
  2410. render(cloned, container, namespace as ElementNamespace)
  2411. }
  2412. }
  2413. if (isHydrate && hydrate) {
  2414. hydrate(vnode as VNode<Node, Element>, container as any)
  2415. } else {
  2416. render(vnode, container, namespace)
  2417. }
  2418. return vnode.component!
  2419. }
  2420. const unmountApp: AppUnmountFn = app => {
  2421. render(null, app._container)
  2422. }
  2423. return {
  2424. render,
  2425. hydrate,
  2426. hydrateNode,
  2427. internals,
  2428. createApp: createAppAPI(
  2429. mountApp,
  2430. unmountApp,
  2431. getComponentPublicInstance as any,
  2432. render,
  2433. ),
  2434. }
  2435. }
  2436. function resolveChildrenNamespace(
  2437. { type, props }: VNode,
  2438. currentNamespace: ElementNamespace,
  2439. ): ElementNamespace {
  2440. return (currentNamespace === 'svg' && type === 'foreignObject') ||
  2441. (currentNamespace === 'mathml' &&
  2442. type === 'annotation-xml' &&
  2443. props &&
  2444. props.encoding &&
  2445. props.encoding.includes('html'))
  2446. ? undefined
  2447. : currentNamespace
  2448. }
  2449. function toggleRecurse(
  2450. { effect, job, vapor }: ComponentInternalInstance,
  2451. allowed: boolean,
  2452. ) {
  2453. if (!vapor) {
  2454. if (allowed) {
  2455. effect.flags |= EffectFlags.ALLOW_RECURSE
  2456. job.flags! |= SchedulerJobFlags.ALLOW_RECURSE
  2457. } else {
  2458. effect.flags &= ~EffectFlags.ALLOW_RECURSE
  2459. job.flags! &= ~SchedulerJobFlags.ALLOW_RECURSE
  2460. }
  2461. }
  2462. }
  2463. export function needTransition(
  2464. parentSuspense: SuspenseBoundary | null,
  2465. transition: TransitionHooks | null,
  2466. ): boolean | null {
  2467. return (
  2468. (!parentSuspense || (parentSuspense && !parentSuspense.pendingBranch)) &&
  2469. transition &&
  2470. !transition.persisted
  2471. )
  2472. }
  2473. /**
  2474. * #1156
  2475. * When a component is HMR-enabled, we need to make sure that all static nodes
  2476. * inside a block also inherit the DOM element from the previous tree so that
  2477. * HMR updates (which are full updates) can retrieve the element for patching.
  2478. *
  2479. * #2080
  2480. * Inside keyed `template` fragment static children, if a fragment is moved,
  2481. * the children will always be moved. Therefore, in order to ensure correct move
  2482. * position, el should be inherited from previous nodes.
  2483. */
  2484. export function traverseStaticChildren(
  2485. n1: VNode,
  2486. n2: VNode,
  2487. shallow = false,
  2488. ): void {
  2489. const ch1 = n1.children
  2490. const ch2 = n2.children
  2491. if (isArray(ch1) && isArray(ch2)) {
  2492. for (let i = 0; i < ch1.length; i++) {
  2493. // this is only called in the optimized path so array children are
  2494. // guaranteed to be vnodes
  2495. const c1 = ch1[i] as VNode
  2496. let c2 = ch2[i] as VNode
  2497. if (c2.shapeFlag & ShapeFlags.ELEMENT && !c2.dynamicChildren) {
  2498. if (c2.patchFlag <= 0 || c2.patchFlag === PatchFlags.NEED_HYDRATION) {
  2499. c2 = ch2[i] = cloneIfMounted(ch2[i] as VNode)
  2500. c2.el = c1.el
  2501. }
  2502. if (!shallow && c2.patchFlag !== PatchFlags.BAIL)
  2503. traverseStaticChildren(c1, c2)
  2504. }
  2505. // #6852 also inherit for text nodes
  2506. if (
  2507. c2.type === Text &&
  2508. // avoid cached text nodes retaining detached dom nodes
  2509. c2.patchFlag !== PatchFlags.CACHED
  2510. ) {
  2511. c2.el = c1.el
  2512. }
  2513. // #2324 also inherit for comment nodes, but not placeholders (e.g. v-if which
  2514. // would have received .el during block patch)
  2515. if (c2.type === Comment && !c2.el) {
  2516. c2.el = c1.el
  2517. }
  2518. if (__DEV__) {
  2519. c2.el && (c2.el.__vnode = c2)
  2520. }
  2521. }
  2522. }
  2523. }
  2524. function locateNonHydratedAsyncRoot(
  2525. instance: GenericComponentInstance,
  2526. ): ComponentInternalInstance | undefined {
  2527. const subComponent = instance.subTree && instance.subTree.component
  2528. if (subComponent) {
  2529. if (subComponent.asyncDep && !subComponent.asyncResolved) {
  2530. return subComponent
  2531. } else {
  2532. return locateNonHydratedAsyncRoot(subComponent)
  2533. }
  2534. }
  2535. }
  2536. export function invalidateMount(hooks: LifecycleHook | undefined): void {
  2537. if (hooks) {
  2538. for (let i = 0; i < hooks.length; i++)
  2539. hooks[i].flags! |= SchedulerJobFlags.DISPOSED
  2540. }
  2541. }
  2542. // shared between vdom and vapor
  2543. export function performTransitionEnter(
  2544. el: RendererElement,
  2545. transition: TransitionHooks,
  2546. insert: () => void,
  2547. parentSuspense: SuspenseBoundary | null,
  2548. force: boolean = false,
  2549. ): void {
  2550. if (force || needTransition(parentSuspense, transition)) {
  2551. transition.beforeEnter(el)
  2552. insert()
  2553. queuePostRenderEffect(() => transition.enter(el), undefined, parentSuspense)
  2554. } else {
  2555. insert()
  2556. }
  2557. }
  2558. // shared between vdom and vapor
  2559. export function performTransitionLeave(
  2560. el: RendererElement,
  2561. transition: TransitionHooks,
  2562. remove: () => void,
  2563. isElement: boolean = true,
  2564. ): void {
  2565. const performRemove = () => {
  2566. remove()
  2567. if (transition && !transition.persisted && transition.afterLeave) {
  2568. transition.afterLeave()
  2569. }
  2570. }
  2571. if (isElement && transition && !transition.persisted) {
  2572. const { leave, delayLeave } = transition
  2573. const performLeave = () => leave(el, performRemove)
  2574. if (delayLeave) {
  2575. delayLeave(el, performRemove, performLeave)
  2576. } else {
  2577. performLeave()
  2578. }
  2579. } else {
  2580. performRemove()
  2581. }
  2582. }
  2583. export function getVaporInterface(
  2584. instance: ComponentInternalInstance | null,
  2585. vnode: VNode,
  2586. ): VaporInteropInterface {
  2587. const ctx = instance ? instance.appContext : vnode.appContext
  2588. const res = ctx && ctx.vapor
  2589. if (__DEV__ && !res) {
  2590. warn(
  2591. `Vapor component found in vdom tree but vapor-in-vdom interop was not installed. ` +
  2592. `Make sure to install it:\n` +
  2593. `\`\`\`\nimport { vaporInteropPlugin } from 'vue'\n` +
  2594. `app.use(vaporInteropPlugin)\n` +
  2595. `\`\`\``,
  2596. )
  2597. }
  2598. return res!
  2599. }
  2600. /**
  2601. * shared between vdom and vapor
  2602. */
  2603. export function getInheritedScopeIds(
  2604. vnode: VNode,
  2605. parentComponent: GenericComponentInstance | null,
  2606. ): string[] {
  2607. const inheritedScopeIds: string[] = []
  2608. let currentParent = parentComponent
  2609. let currentVNode = vnode
  2610. while (currentParent) {
  2611. let subTree = currentParent.subTree
  2612. if (!subTree) break
  2613. if (
  2614. __DEV__ &&
  2615. subTree.patchFlag > 0 &&
  2616. subTree.patchFlag & PatchFlags.DEV_ROOT_FRAGMENT
  2617. ) {
  2618. subTree =
  2619. filterSingleRoot(subTree.children as VNodeArrayChildren) || subTree
  2620. }
  2621. if (
  2622. currentVNode === subTree ||
  2623. (isSuspense(subTree.type) &&
  2624. (subTree.ssContent === currentVNode ||
  2625. subTree.ssFallback === currentVNode))
  2626. ) {
  2627. const parentVNode = currentParent.vnode!
  2628. if (parentVNode.scopeId) {
  2629. inheritedScopeIds.push(parentVNode.scopeId)
  2630. }
  2631. if (parentVNode.slotScopeIds) {
  2632. inheritedScopeIds.push(...parentVNode.slotScopeIds)
  2633. }
  2634. currentVNode = parentVNode
  2635. currentParent = currentParent.parent
  2636. } else {
  2637. break
  2638. }
  2639. }
  2640. return inheritedScopeIds
  2641. }