renderer.ts 66 KB

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