renderer.ts 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921
  1. import {
  2. Text,
  3. Fragment,
  4. Comment,
  5. cloneIfMounted,
  6. normalizeVNode,
  7. VNode,
  8. VNodeArrayChildren,
  9. createVNode,
  10. isSameVNodeType,
  11. Static,
  12. VNodeNormalizedRef
  13. } from './vnode'
  14. import {
  15. ComponentInternalInstance,
  16. createComponentInstance,
  17. Component,
  18. Data,
  19. setupComponent
  20. } from './component'
  21. import {
  22. renderComponentRoot,
  23. shouldUpdateComponent,
  24. updateHOCHostEl
  25. } from './componentRenderUtils'
  26. import {
  27. isString,
  28. EMPTY_OBJ,
  29. EMPTY_ARR,
  30. isReservedProp,
  31. isFunction,
  32. PatchFlags,
  33. ShapeFlags,
  34. NOOP
  35. } from '@vue/shared'
  36. import {
  37. queueJob,
  38. queuePostFlushCb,
  39. flushPostFlushCbs,
  40. invalidateJob
  41. } from './scheduler'
  42. import {
  43. effect,
  44. stop,
  45. ReactiveEffectOptions,
  46. isRef,
  47. toRaw,
  48. DebuggerEvent
  49. } from '@vue/reactivity'
  50. import { resolveProps } from './componentProps'
  51. import { resolveSlots } from './componentSlots'
  52. import { pushWarningContext, popWarningContext, warn } from './warning'
  53. import { invokeDirectiveHook } from './directives'
  54. import { ComponentPublicInstance } from './componentProxy'
  55. import { createAppAPI, CreateAppFunction } from './apiCreateApp'
  56. import {
  57. SuspenseBoundary,
  58. queueEffectWithSuspense,
  59. SuspenseImpl
  60. } from './components/Suspense'
  61. import { PortalImpl } from './components/Portal'
  62. import { KeepAliveSink, isKeepAlive } from './components/KeepAlive'
  63. import { registerHMR, unregisterHMR } from './hmr'
  64. import { ErrorCodes, callWithErrorHandling } from './errorHandling'
  65. import { createHydrationFunctions, RootHydrateFunction } from './hydration'
  66. const __HMR__ = __BUNDLER__ && __DEV__
  67. export interface Renderer<HostNode = any, HostElement = any> {
  68. render: RootRenderFunction<HostNode, HostElement>
  69. createApp: CreateAppFunction<HostElement>
  70. }
  71. export interface HydrationRenderer extends Renderer<Node, Element> {
  72. hydrate: RootHydrateFunction
  73. }
  74. export type RootRenderFunction<HostNode, HostElement> = (
  75. vnode: VNode<HostNode, HostElement> | null,
  76. container: HostElement
  77. ) => void
  78. export interface RendererOptions<HostNode = any, HostElement = any> {
  79. patchProp(
  80. el: HostElement,
  81. key: string,
  82. prevValue: any,
  83. nextValue: any,
  84. isSVG?: boolean,
  85. prevChildren?: VNode<HostNode, HostElement>[],
  86. parentComponent?: ComponentInternalInstance | null,
  87. parentSuspense?: SuspenseBoundary<HostNode, HostElement> | null,
  88. unmountChildren?: UnmountChildrenFn<HostNode, HostElement>
  89. ): void
  90. insert(el: HostNode, parent: HostElement, anchor?: HostNode | null): void
  91. remove(el: HostNode): void
  92. createElement(type: string, isSVG?: boolean): HostElement
  93. createText(text: string): HostNode
  94. createComment(text: string): HostNode
  95. setText(node: HostNode, text: string): void
  96. setElementText(node: HostElement, text: string): void
  97. parentNode(node: HostNode): HostElement | null
  98. nextSibling(node: HostNode): HostNode | null
  99. querySelector?(selector: string): HostElement | null
  100. setScopeId?(el: HostElement, id: string): void
  101. cloneNode?(node: HostNode): HostNode
  102. insertStaticContent?(
  103. content: string,
  104. parent: HostElement,
  105. anchor: HostNode | null,
  106. isSVG: boolean
  107. ): HostElement
  108. }
  109. // An object exposing the internals of a renderer, passed to tree-shakeable
  110. // features so that they can be decoupled from this file. Keys are shortened
  111. // to optimize bundle size.
  112. export interface RendererInternals<HostNode = any, HostElement = any> {
  113. p: PatchFn<HostNode, HostElement>
  114. um: UnmountFn<HostNode, HostElement>
  115. m: MoveFn<HostNode, HostElement>
  116. mt: MountComponentFn<HostNode, HostElement>
  117. mc: MountChildrenFn<HostNode, HostElement>
  118. pc: PatchChildrenFn<HostNode, HostElement>
  119. pbc: PatchBlockChildrenFn<HostNode, HostElement>
  120. n: NextFn<HostNode, HostElement>
  121. o: RendererOptions<HostNode, HostElement>
  122. }
  123. // These functions are created inside a closure and therefore their types cannot
  124. // be directly exported. In order to avoid maintaining function signatures in
  125. // two places, we declare them once here and use them inside the closure.
  126. type PatchFn<HostNode, HostElement> = (
  127. n1: VNode<HostNode, HostElement> | null, // null means this is a mount
  128. n2: VNode<HostNode, HostElement>,
  129. container: HostElement,
  130. anchor?: HostNode | null,
  131. parentComponent?: ComponentInternalInstance | null,
  132. parentSuspense?: SuspenseBoundary<HostNode, HostElement> | null,
  133. isSVG?: boolean,
  134. optimized?: boolean
  135. ) => void
  136. type MountChildrenFn<HostNode, HostElement> = (
  137. children: VNodeArrayChildren<HostNode, HostElement>,
  138. container: HostElement,
  139. anchor: HostNode | null,
  140. parentComponent: ComponentInternalInstance | null,
  141. parentSuspense: SuspenseBoundary<HostNode, HostElement> | null,
  142. isSVG: boolean,
  143. optimized: boolean,
  144. start?: number
  145. ) => void
  146. type PatchChildrenFn<HostNode, HostElement> = (
  147. n1: VNode<HostNode, HostElement> | null,
  148. n2: VNode<HostNode, HostElement>,
  149. container: HostElement,
  150. anchor: HostNode | null,
  151. parentComponent: ComponentInternalInstance | null,
  152. parentSuspense: SuspenseBoundary<HostNode, HostElement> | null,
  153. isSVG: boolean,
  154. optimized?: boolean
  155. ) => void
  156. type PatchBlockChildrenFn<HostNode, HostElement> = (
  157. oldChildren: VNode<HostNode, HostElement>[],
  158. newChildren: VNode<HostNode, HostElement>[],
  159. fallbackContainer: HostElement,
  160. parentComponent: ComponentInternalInstance | null,
  161. parentSuspense: SuspenseBoundary<HostNode, HostElement> | null,
  162. isSVG: boolean
  163. ) => void
  164. type MoveFn<HostNode, HostElement> = (
  165. vnode: VNode<HostNode, HostElement>,
  166. container: HostElement,
  167. anchor: HostNode | null,
  168. type: MoveType,
  169. parentSuspense?: SuspenseBoundary<HostNode, HostElement> | null
  170. ) => void
  171. type NextFn<HostNode, HostElement> = (
  172. vnode: VNode<HostNode, HostElement>
  173. ) => HostNode | null
  174. type UnmountFn<HostNode, HostElement> = (
  175. vnode: VNode<HostNode, HostElement>,
  176. parentComponent: ComponentInternalInstance | null,
  177. parentSuspense: SuspenseBoundary<HostNode, HostElement> | null,
  178. doRemove?: boolean
  179. ) => void
  180. type UnmountChildrenFn<HostNode, HostElement> = (
  181. children: VNode<HostNode, HostElement>[],
  182. parentComponent: ComponentInternalInstance | null,
  183. parentSuspense: SuspenseBoundary<HostNode, HostElement> | null,
  184. doRemove?: boolean,
  185. start?: number
  186. ) => void
  187. export type MountComponentFn<HostNode, HostElement> = (
  188. initialVNode: VNode<HostNode, HostElement>,
  189. container: HostElement,
  190. anchor: HostNode | null,
  191. parentComponent: ComponentInternalInstance | null,
  192. parentSuspense: SuspenseBoundary<HostNode, HostElement> | null,
  193. isSVG: boolean
  194. ) => void
  195. type ProcessTextOrCommentFn<HostNode, HostElement> = (
  196. n1: VNode<HostNode, HostElement> | null,
  197. n2: VNode<HostNode, HostElement>,
  198. container: HostElement,
  199. anchor: HostNode | null
  200. ) => void
  201. export type SetupRenderEffectFn<HostNode, HostElement> = (
  202. instance: ComponentInternalInstance,
  203. initialVNode: VNode<HostNode, HostElement>,
  204. container: HostElement,
  205. anchor: HostNode | null,
  206. parentSuspense: SuspenseBoundary<HostNode, HostElement> | null,
  207. isSVG: boolean
  208. ) => void
  209. export const enum MoveType {
  210. ENTER,
  211. LEAVE,
  212. REORDER
  213. }
  214. const prodEffectOptions = {
  215. scheduler: queueJob
  216. }
  217. function createDevEffectOptions(
  218. instance: ComponentInternalInstance
  219. ): ReactiveEffectOptions {
  220. return {
  221. scheduler: queueJob,
  222. onTrack: instance.rtc ? e => invokeHooks(instance.rtc!, e) : void 0,
  223. onTrigger: instance.rtg ? e => invokeHooks(instance.rtg!, e) : void 0
  224. }
  225. }
  226. export function invokeHooks(hooks: Function[], arg?: DebuggerEvent) {
  227. for (let i = 0; i < hooks.length; i++) {
  228. hooks[i](arg)
  229. }
  230. }
  231. export const queuePostRenderEffect = __FEATURE_SUSPENSE__
  232. ? queueEffectWithSuspense
  233. : queuePostFlushCb
  234. /**
  235. * The createRenderer function accepts two generic arguments:
  236. * HostNode and HostElement, corresponding to Node and Element types in the
  237. * host environment. For example, for runtime-dom, HostNode would be the DOM
  238. * `Node` interface and HostElement would be the DOM `Element` interface.
  239. *
  240. * Custom renderers can pass in the platform specific types like this:
  241. *
  242. * ``` js
  243. * const { render, createApp } = createRenderer<Node, Element>({
  244. * patchProp,
  245. * ...nodeOps
  246. * })
  247. * ```
  248. */
  249. export function createRenderer<
  250. HostNode extends object = any,
  251. HostElement extends HostNode = any
  252. >(options: RendererOptions<HostNode, HostElement>) {
  253. return baseCreateRenderer<HostNode, HostElement>(options)
  254. }
  255. // Separate API for creating hydration-enabled renderer.
  256. // Hydration logic is only used when calling this function, making it
  257. // tree-shakable.
  258. export function createHydrationRenderer(
  259. options: RendererOptions<Node, Element>
  260. ) {
  261. return baseCreateRenderer<Node, Element>(options, createHydrationFunctions)
  262. }
  263. // overload 1: no hydration
  264. function baseCreateRenderer<
  265. HostNode extends object = any,
  266. HostElement extends HostNode = any
  267. >(
  268. options: RendererOptions<HostNode, HostElement>
  269. ): Renderer<HostNode, HostElement>
  270. // overload 2: with hydration
  271. function baseCreateRenderer<
  272. HostNode extends object = any,
  273. HostElement extends HostNode = any
  274. >(
  275. options: RendererOptions<HostNode, HostElement>,
  276. createHydrationFns: typeof createHydrationFunctions
  277. ): HydrationRenderer
  278. // implementation
  279. function baseCreateRenderer<
  280. HostNode extends object = any,
  281. HostElement extends HostNode = any
  282. >(
  283. options: RendererOptions<HostNode, HostElement>,
  284. createHydrationFns?: typeof createHydrationFunctions
  285. ) {
  286. type HostVNode = VNode<HostNode, HostElement>
  287. type HostVNodeChildren = VNodeArrayChildren<HostNode, HostElement>
  288. type HostSuspenseBoundary = SuspenseBoundary<HostNode, HostElement>
  289. const {
  290. insert: hostInsert,
  291. remove: hostRemove,
  292. patchProp: hostPatchProp,
  293. createElement: hostCreateElement,
  294. createText: hostCreateText,
  295. createComment: hostCreateComment,
  296. setText: hostSetText,
  297. setElementText: hostSetElementText,
  298. parentNode: hostParentNode,
  299. nextSibling: hostNextSibling,
  300. setScopeId: hostSetScopeId = NOOP,
  301. cloneNode: hostCloneNode,
  302. insertStaticContent: hostInsertStaticContent
  303. } = options
  304. // Note: functions inside this closure should use `const xxx = () => {}`
  305. // style in order to prevent being inlined by minifiers.
  306. const patch: PatchFn<HostNode, HostElement> = (
  307. n1,
  308. n2,
  309. container,
  310. anchor = null,
  311. parentComponent = null,
  312. parentSuspense = null,
  313. isSVG = false,
  314. optimized = false
  315. ) => {
  316. // patching & not same type, unmount old tree
  317. if (n1 != null && !isSameVNodeType(n1, n2)) {
  318. anchor = getNextHostNode(n1)
  319. unmount(n1, parentComponent, parentSuspense, true)
  320. n1 = null
  321. }
  322. const { type, shapeFlag } = n2
  323. switch (type) {
  324. case Text:
  325. processText(n1, n2, container, anchor)
  326. break
  327. case Comment:
  328. processCommentNode(n1, n2, container, anchor)
  329. break
  330. case Static:
  331. if (n1 == null) {
  332. mountStaticNode(n2, container, anchor, isSVG)
  333. } // static nodes are noop on patch
  334. break
  335. case Fragment:
  336. processFragment(
  337. n1,
  338. n2,
  339. container,
  340. anchor,
  341. parentComponent,
  342. parentSuspense,
  343. isSVG,
  344. optimized
  345. )
  346. break
  347. default:
  348. if (shapeFlag & ShapeFlags.ELEMENT) {
  349. processElement(
  350. n1,
  351. n2,
  352. container,
  353. anchor,
  354. parentComponent,
  355. parentSuspense,
  356. isSVG,
  357. optimized
  358. )
  359. } else if (shapeFlag & ShapeFlags.COMPONENT) {
  360. processComponent(
  361. n1,
  362. n2,
  363. container,
  364. anchor,
  365. parentComponent,
  366. parentSuspense,
  367. isSVG,
  368. optimized
  369. )
  370. } else if (shapeFlag & ShapeFlags.PORTAL) {
  371. ;(type as typeof PortalImpl).process(
  372. n1,
  373. n2,
  374. container,
  375. anchor,
  376. parentComponent,
  377. parentSuspense,
  378. isSVG,
  379. optimized,
  380. internals
  381. )
  382. } else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
  383. ;(type as typeof SuspenseImpl).process(
  384. n1,
  385. n2,
  386. container,
  387. anchor,
  388. parentComponent,
  389. parentSuspense,
  390. isSVG,
  391. optimized,
  392. internals
  393. )
  394. } else if (__DEV__) {
  395. warn('Invalid HostVNode type:', type, `(${typeof type})`)
  396. }
  397. }
  398. }
  399. const processText: ProcessTextOrCommentFn<HostNode, HostElement> = (
  400. n1,
  401. n2,
  402. container,
  403. anchor
  404. ) => {
  405. if (n1 == null) {
  406. hostInsert(
  407. (n2.el = hostCreateText(n2.children as string)),
  408. container,
  409. anchor
  410. )
  411. } else {
  412. const el = (n2.el = n1.el) as HostNode
  413. if (n2.children !== n1.children) {
  414. hostSetText(el, n2.children as string)
  415. }
  416. }
  417. }
  418. const processCommentNode: ProcessTextOrCommentFn<HostNode, HostElement> = (
  419. n1,
  420. n2,
  421. container,
  422. anchor
  423. ) => {
  424. if (n1 == null) {
  425. hostInsert(
  426. (n2.el = hostCreateComment((n2.children as string) || '')),
  427. container,
  428. anchor
  429. )
  430. } else {
  431. // there's no support for dynamic comments
  432. n2.el = n1.el
  433. }
  434. }
  435. const mountStaticNode = (
  436. n2: HostVNode,
  437. container: HostElement,
  438. anchor: HostNode | null,
  439. isSVG: boolean
  440. ) => {
  441. if (n2.el != null && hostCloneNode !== undefined) {
  442. hostInsert(hostCloneNode(n2.el), container, anchor)
  443. } else {
  444. // static nodes are only present when used with compiler-dom/runtime-dom
  445. // which guarantees presence of hostInsertStaticContent.
  446. n2.el = hostInsertStaticContent!(
  447. n2.children as string,
  448. container,
  449. anchor,
  450. isSVG
  451. )
  452. }
  453. }
  454. const processElement = (
  455. n1: HostVNode | null,
  456. n2: HostVNode,
  457. container: HostElement,
  458. anchor: HostNode | null,
  459. parentComponent: ComponentInternalInstance | null,
  460. parentSuspense: HostSuspenseBoundary | null,
  461. isSVG: boolean,
  462. optimized: boolean
  463. ) => {
  464. isSVG = isSVG || (n2.type as string) === 'svg'
  465. if (n1 == null) {
  466. mountElement(
  467. n2,
  468. container,
  469. anchor,
  470. parentComponent,
  471. parentSuspense,
  472. isSVG,
  473. optimized
  474. )
  475. } else {
  476. patchElement(n1, n2, parentComponent, parentSuspense, isSVG, optimized)
  477. }
  478. if (n2.ref !== null && parentComponent !== null) {
  479. setRef(n2.ref, n1 && n1.ref, parentComponent, n2.el)
  480. }
  481. }
  482. const mountElement = (
  483. vnode: HostVNode,
  484. container: HostElement,
  485. anchor: HostNode | null,
  486. parentComponent: ComponentInternalInstance | null,
  487. parentSuspense: HostSuspenseBoundary | null,
  488. isSVG: boolean,
  489. optimized: boolean
  490. ) => {
  491. let el: HostElement
  492. const { type, props, shapeFlag, transition, scopeId, patchFlag } = vnode
  493. if (
  494. vnode.el !== null &&
  495. hostCloneNode !== undefined &&
  496. patchFlag === PatchFlags.HOISTED
  497. ) {
  498. // If a vnode has non-null el, it means it's being reused.
  499. // Only static vnodes can be reused, so its mounted DOM nodes should be
  500. // exactly the same, and we can simply do a clone here.
  501. el = vnode.el = hostCloneNode(vnode.el) as HostElement
  502. } else {
  503. el = vnode.el = hostCreateElement(vnode.type as string, isSVG)
  504. // props
  505. if (props != null) {
  506. for (const key in props) {
  507. if (!isReservedProp(key)) {
  508. hostPatchProp(el, key, null, props[key], isSVG)
  509. }
  510. }
  511. if (props.onVnodeBeforeMount != null) {
  512. invokeDirectiveHook(props.onVnodeBeforeMount, parentComponent, vnode)
  513. }
  514. }
  515. // scopeId
  516. if (__BUNDLER__) {
  517. if (scopeId !== null) {
  518. hostSetScopeId(el, scopeId)
  519. }
  520. const treeOwnerId = parentComponent && parentComponent.type.__scopeId
  521. // vnode's own scopeId and the current patched component's scopeId is
  522. // different - this is a slot content node.
  523. if (treeOwnerId != null && treeOwnerId !== scopeId) {
  524. hostSetScopeId(el, treeOwnerId + '-s')
  525. }
  526. }
  527. // children
  528. if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
  529. hostSetElementText(el, vnode.children as string)
  530. } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
  531. mountChildren(
  532. vnode.children as HostVNodeChildren,
  533. el,
  534. null,
  535. parentComponent,
  536. parentSuspense,
  537. isSVG && type !== 'foreignObject',
  538. optimized || vnode.dynamicChildren !== null
  539. )
  540. }
  541. if (transition != null && !transition.persisted) {
  542. transition.beforeEnter(el)
  543. }
  544. }
  545. hostInsert(el, container, anchor)
  546. const vnodeMountedHook = props && props.onVnodeMounted
  547. if (
  548. vnodeMountedHook != null ||
  549. (transition != null && !transition.persisted)
  550. ) {
  551. queuePostRenderEffect(() => {
  552. vnodeMountedHook &&
  553. invokeDirectiveHook(vnodeMountedHook, parentComponent, vnode)
  554. transition && !transition.persisted && transition.enter(el)
  555. }, parentSuspense)
  556. }
  557. }
  558. const mountChildren: MountChildrenFn<HostNode, HostElement> = (
  559. children,
  560. container,
  561. anchor,
  562. parentComponent,
  563. parentSuspense,
  564. isSVG,
  565. optimized,
  566. start = 0
  567. ) => {
  568. for (let i = start; i < children.length; i++) {
  569. const child = (children[i] = optimized
  570. ? cloneIfMounted(children[i] as HostVNode)
  571. : normalizeVNode(children[i]))
  572. patch(
  573. null,
  574. child,
  575. container,
  576. anchor,
  577. parentComponent,
  578. parentSuspense,
  579. isSVG,
  580. optimized
  581. )
  582. }
  583. }
  584. const patchElement = (
  585. n1: HostVNode,
  586. n2: HostVNode,
  587. parentComponent: ComponentInternalInstance | null,
  588. parentSuspense: HostSuspenseBoundary | null,
  589. isSVG: boolean,
  590. optimized: boolean
  591. ) => {
  592. const el = (n2.el = n1.el) as HostElement
  593. let { patchFlag, dynamicChildren } = n2
  594. const oldProps = (n1 && n1.props) || EMPTY_OBJ
  595. const newProps = n2.props || EMPTY_OBJ
  596. if (newProps.onVnodeBeforeUpdate != null) {
  597. invokeDirectiveHook(newProps.onVnodeBeforeUpdate, parentComponent, n2, n1)
  598. }
  599. if (__HMR__ && parentComponent && parentComponent.renderUpdated) {
  600. // HMR updated, force full diff
  601. patchFlag = 0
  602. optimized = false
  603. dynamicChildren = null
  604. }
  605. if (patchFlag > 0) {
  606. // the presence of a patchFlag means this element's render code was
  607. // generated by the compiler and can take the fast path.
  608. // in this path old node and new node are guaranteed to have the same shape
  609. // (i.e. at the exact same position in the source template)
  610. if (patchFlag & PatchFlags.FULL_PROPS) {
  611. // element props contain dynamic keys, full diff needed
  612. patchProps(
  613. el,
  614. n2,
  615. oldProps,
  616. newProps,
  617. parentComponent,
  618. parentSuspense,
  619. isSVG
  620. )
  621. } else {
  622. // class
  623. // this flag is matched when the element has dynamic class bindings.
  624. if (patchFlag & PatchFlags.CLASS) {
  625. if (oldProps.class !== newProps.class) {
  626. hostPatchProp(el, 'class', null, newProps.class, isSVG)
  627. }
  628. }
  629. // style
  630. // this flag is matched when the element has dynamic style bindings
  631. if (patchFlag & PatchFlags.STYLE) {
  632. hostPatchProp(el, 'style', oldProps.style, newProps.style, isSVG)
  633. }
  634. // props
  635. // This flag is matched when the element has dynamic prop/attr bindings
  636. // other than class and style. The keys of dynamic prop/attrs are saved for
  637. // faster iteration.
  638. // Note dynamic keys like :[foo]="bar" will cause this optimization to
  639. // bail out and go through a full diff because we need to unset the old key
  640. if (patchFlag & PatchFlags.PROPS) {
  641. // if the flag is present then dynamicProps must be non-null
  642. const propsToUpdate = n2.dynamicProps!
  643. for (let i = 0; i < propsToUpdate.length; i++) {
  644. const key = propsToUpdate[i]
  645. const prev = oldProps[key]
  646. const next = newProps[key]
  647. if (prev !== next) {
  648. hostPatchProp(
  649. el,
  650. key,
  651. prev,
  652. next,
  653. isSVG,
  654. n1.children as HostVNode[],
  655. parentComponent,
  656. parentSuspense,
  657. unmountChildren
  658. )
  659. }
  660. }
  661. }
  662. }
  663. // text
  664. // This flag is matched when the element has only dynamic text children.
  665. if (patchFlag & PatchFlags.TEXT) {
  666. if (n1.children !== n2.children) {
  667. hostSetElementText(el, n2.children as string)
  668. }
  669. }
  670. } else if (!optimized && dynamicChildren == null) {
  671. // unoptimized, full diff
  672. patchProps(
  673. el,
  674. n2,
  675. oldProps,
  676. newProps,
  677. parentComponent,
  678. parentSuspense,
  679. isSVG
  680. )
  681. }
  682. const areChildrenSVG = isSVG && n2.type !== 'foreignObject'
  683. if (dynamicChildren != null) {
  684. patchBlockChildren(
  685. n1.dynamicChildren!,
  686. dynamicChildren,
  687. el,
  688. parentComponent,
  689. parentSuspense,
  690. areChildrenSVG
  691. )
  692. } else if (!optimized) {
  693. // full diff
  694. patchChildren(
  695. n1,
  696. n2,
  697. el,
  698. null,
  699. parentComponent,
  700. parentSuspense,
  701. areChildrenSVG
  702. )
  703. }
  704. if (newProps.onVnodeUpdated != null) {
  705. queuePostRenderEffect(() => {
  706. invokeDirectiveHook(newProps.onVnodeUpdated, parentComponent, n2, n1)
  707. }, parentSuspense)
  708. }
  709. }
  710. // The fast path for blocks.
  711. const patchBlockChildren: PatchBlockChildrenFn<HostNode, HostElement> = (
  712. oldChildren,
  713. newChildren,
  714. fallbackContainer,
  715. parentComponent,
  716. parentSuspense,
  717. isSVG
  718. ) => {
  719. for (let i = 0; i < newChildren.length; i++) {
  720. const oldVNode = oldChildren[i]
  721. const newVNode = newChildren[i]
  722. // Determine the container (parent element) for the patch.
  723. const container =
  724. // - In the case of a Fragment, we need to provide the actual parent
  725. // of the Fragment itself so it can move its children.
  726. oldVNode.type === Fragment ||
  727. // - In the case of different nodes, there is going to be a replacement
  728. // which also requires the correct parent container
  729. !isSameVNodeType(oldVNode, newVNode) ||
  730. // - In the case of a component, it could contain anything.
  731. oldVNode.shapeFlag & ShapeFlags.COMPONENT
  732. ? hostParentNode(oldVNode.el!)!
  733. : // In other cases, the parent container is not actually used so we
  734. // just pass the block element here to avoid a DOM parentNode call.
  735. fallbackContainer
  736. patch(
  737. oldVNode,
  738. newVNode,
  739. container,
  740. null,
  741. parentComponent,
  742. parentSuspense,
  743. isSVG,
  744. true
  745. )
  746. }
  747. }
  748. const patchProps = (
  749. el: HostElement,
  750. vnode: HostVNode,
  751. oldProps: Data,
  752. newProps: Data,
  753. parentComponent: ComponentInternalInstance | null,
  754. parentSuspense: HostSuspenseBoundary | null,
  755. isSVG: boolean
  756. ) => {
  757. if (oldProps !== newProps) {
  758. for (const key in newProps) {
  759. if (isReservedProp(key)) continue
  760. const next = newProps[key]
  761. const prev = oldProps[key]
  762. if (next !== prev) {
  763. hostPatchProp(
  764. el,
  765. key,
  766. prev,
  767. next,
  768. isSVG,
  769. vnode.children as HostVNode[],
  770. parentComponent,
  771. parentSuspense,
  772. unmountChildren
  773. )
  774. }
  775. }
  776. if (oldProps !== EMPTY_OBJ) {
  777. for (const key in oldProps) {
  778. if (!isReservedProp(key) && !(key in newProps)) {
  779. hostPatchProp(
  780. el,
  781. key,
  782. oldProps[key],
  783. null,
  784. isSVG,
  785. vnode.children as HostVNode[],
  786. parentComponent,
  787. parentSuspense,
  788. unmountChildren
  789. )
  790. }
  791. }
  792. }
  793. }
  794. }
  795. const processFragment = (
  796. n1: HostVNode | null,
  797. n2: HostVNode,
  798. container: HostElement,
  799. anchor: HostNode | null,
  800. parentComponent: ComponentInternalInstance | null,
  801. parentSuspense: HostSuspenseBoundary | null,
  802. isSVG: boolean,
  803. optimized: boolean
  804. ) => {
  805. const fragmentStartAnchor = (n2.el = n1 ? n1.el : hostCreateText(''))!
  806. const fragmentEndAnchor = (n2.anchor = n1 ? n1.anchor : hostCreateText(''))!
  807. let { patchFlag, dynamicChildren } = n2
  808. if (patchFlag > 0) {
  809. optimized = true
  810. }
  811. if (__HMR__ && parentComponent && parentComponent.renderUpdated) {
  812. // HMR updated, force full diff
  813. patchFlag = 0
  814. optimized = false
  815. dynamicChildren = null
  816. }
  817. if (n1 == null) {
  818. hostInsert(fragmentStartAnchor, container, anchor)
  819. hostInsert(fragmentEndAnchor, container, anchor)
  820. // a fragment can only have array children
  821. // since they are either generated by the compiler, or implicitly created
  822. // from arrays.
  823. mountChildren(
  824. n2.children as HostVNodeChildren,
  825. container,
  826. fragmentEndAnchor,
  827. parentComponent,
  828. parentSuspense,
  829. isSVG,
  830. optimized
  831. )
  832. } else {
  833. if (patchFlag & PatchFlags.STABLE_FRAGMENT && dynamicChildren != null) {
  834. // a stable fragment (template root or <template v-for>) doesn't need to
  835. // patch children order, but it may contain dynamicChildren.
  836. patchBlockChildren(
  837. n1.dynamicChildren!,
  838. dynamicChildren,
  839. container,
  840. parentComponent,
  841. parentSuspense,
  842. isSVG
  843. )
  844. } else {
  845. // keyed / unkeyed, or manual fragments.
  846. // for keyed & unkeyed, since they are compiler generated from v-for,
  847. // each child is guaranteed to be a block so the fragment will never
  848. // have dynamicChildren.
  849. patchChildren(
  850. n1,
  851. n2,
  852. container,
  853. fragmentEndAnchor,
  854. parentComponent,
  855. parentSuspense,
  856. isSVG,
  857. optimized
  858. )
  859. }
  860. }
  861. }
  862. const processComponent = (
  863. n1: HostVNode | null,
  864. n2: HostVNode,
  865. container: HostElement,
  866. anchor: HostNode | null,
  867. parentComponent: ComponentInternalInstance | null,
  868. parentSuspense: HostSuspenseBoundary | null,
  869. isSVG: boolean,
  870. optimized: boolean
  871. ) => {
  872. if (n1 == null) {
  873. if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
  874. ;(parentComponent!.sink as KeepAliveSink).activate(
  875. n2,
  876. container,
  877. anchor
  878. )
  879. } else {
  880. mountComponent(
  881. n2,
  882. container,
  883. anchor,
  884. parentComponent,
  885. parentSuspense,
  886. isSVG
  887. )
  888. }
  889. } else {
  890. const instance = (n2.component = n1.component)!
  891. if (shouldUpdateComponent(n1, n2, parentComponent, optimized)) {
  892. if (
  893. __FEATURE_SUSPENSE__ &&
  894. instance.asyncDep &&
  895. !instance.asyncResolved
  896. ) {
  897. // async & still pending - just update props and slots
  898. // since the component's reactive effect for render isn't set-up yet
  899. if (__DEV__) {
  900. pushWarningContext(n2)
  901. }
  902. updateComponentPreRender(instance, n2)
  903. if (__DEV__) {
  904. popWarningContext()
  905. }
  906. return
  907. } else {
  908. // normal update
  909. instance.next = n2
  910. // in case the child component is also queued, remove it to avoid
  911. // double updating the same child component in the same flush.
  912. invalidateJob(instance.update)
  913. // instance.update is the reactive effect runner.
  914. instance.update()
  915. }
  916. } else {
  917. // no update needed. just copy over properties
  918. n2.component = n1.component
  919. n2.el = n1.el
  920. }
  921. }
  922. if (n2.ref !== null && parentComponent !== null) {
  923. if (__DEV__ && !(n2.shapeFlag & ShapeFlags.STATEFUL_COMPONENT)) {
  924. pushWarningContext(n2)
  925. warn(
  926. `Functional components do not support "ref" because they do not ` +
  927. `have instances.`
  928. )
  929. popWarningContext()
  930. }
  931. setRef(n2.ref, n1 && n1.ref, parentComponent, n2.component!.proxy)
  932. }
  933. }
  934. const mountComponent: MountComponentFn<HostNode, HostElement> = (
  935. initialVNode,
  936. container,
  937. anchor,
  938. parentComponent,
  939. parentSuspense,
  940. isSVG
  941. ) => {
  942. const instance: ComponentInternalInstance = (initialVNode.component = createComponentInstance(
  943. initialVNode,
  944. parentComponent
  945. ))
  946. if (__HMR__ && instance.type.__hmrId != null) {
  947. registerHMR(instance)
  948. }
  949. if (__DEV__) {
  950. pushWarningContext(initialVNode)
  951. }
  952. // inject renderer internals for keepAlive
  953. if (isKeepAlive(initialVNode)) {
  954. const sink = instance.sink as KeepAliveSink
  955. sink.renderer = internals
  956. sink.parentSuspense = parentSuspense
  957. }
  958. // resolve props and slots for setup context
  959. setupComponent(instance, parentSuspense)
  960. // setup() is async. This component relies on async logic to be resolved
  961. // before proceeding
  962. if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
  963. if (!parentSuspense) {
  964. if (__DEV__) warn('async setup() is used without a suspense boundary!')
  965. return
  966. }
  967. parentSuspense.registerDep(instance, setupRenderEffect)
  968. // Give it a placeholder if this is not hydration
  969. if (!initialVNode.el) {
  970. const placeholder = (instance.subTree = createVNode(Comment))
  971. processCommentNode(null, placeholder, container!, anchor)
  972. }
  973. return
  974. }
  975. setupRenderEffect(
  976. instance,
  977. initialVNode,
  978. container,
  979. anchor,
  980. parentSuspense,
  981. isSVG
  982. )
  983. if (__DEV__) {
  984. popWarningContext()
  985. }
  986. }
  987. const setupRenderEffect: SetupRenderEffectFn<HostNode, HostElement> = (
  988. instance,
  989. initialVNode,
  990. container,
  991. anchor,
  992. parentSuspense,
  993. isSVG
  994. ) => {
  995. // create reactive effect for rendering
  996. instance.update = effect(function componentEffect() {
  997. if (!instance.isMounted) {
  998. const subTree = (instance.subTree = renderComponentRoot(instance))
  999. // beforeMount hook
  1000. if (instance.bm !== null) {
  1001. invokeHooks(instance.bm)
  1002. }
  1003. if (initialVNode.el && hydrateNode) {
  1004. // vnode has adopted host node - perform hydration instead of mount.
  1005. hydrateNode(
  1006. initialVNode.el as Node,
  1007. subTree,
  1008. instance,
  1009. parentSuspense
  1010. )
  1011. } else {
  1012. patch(
  1013. null,
  1014. subTree,
  1015. container,
  1016. anchor,
  1017. instance,
  1018. parentSuspense,
  1019. isSVG
  1020. )
  1021. initialVNode.el = subTree.el
  1022. }
  1023. // mounted hook
  1024. if (instance.m !== null) {
  1025. queuePostRenderEffect(instance.m, parentSuspense)
  1026. }
  1027. // activated hook for keep-alive roots.
  1028. if (
  1029. instance.a !== null &&
  1030. instance.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
  1031. ) {
  1032. queuePostRenderEffect(instance.a, parentSuspense)
  1033. }
  1034. instance.isMounted = true
  1035. } else {
  1036. // updateComponent
  1037. // This is triggered by mutation of component's own state (next: null)
  1038. // OR parent calling processComponent (next: HostVNode)
  1039. const { next } = instance
  1040. if (__DEV__) {
  1041. pushWarningContext(next || instance.vnode)
  1042. }
  1043. if (next !== null) {
  1044. updateComponentPreRender(instance, next)
  1045. }
  1046. const nextTree = renderComponentRoot(instance)
  1047. const prevTree = instance.subTree
  1048. instance.subTree = nextTree
  1049. // beforeUpdate hook
  1050. if (instance.bu !== null) {
  1051. invokeHooks(instance.bu)
  1052. }
  1053. // reset refs
  1054. // only needed if previous patch had refs
  1055. if (instance.refs !== EMPTY_OBJ) {
  1056. instance.refs = {}
  1057. }
  1058. patch(
  1059. prevTree,
  1060. nextTree,
  1061. // parent may have changed if it's in a portal
  1062. hostParentNode(prevTree.el as HostNode) as HostElement,
  1063. // anchor may have changed if it's in a fragment
  1064. getNextHostNode(prevTree),
  1065. instance,
  1066. parentSuspense,
  1067. isSVG
  1068. )
  1069. instance.vnode.el = nextTree.el
  1070. if (next === null) {
  1071. // self-triggered update. In case of HOC, update parent component
  1072. // vnode el. HOC is indicated by parent instance's subTree pointing
  1073. // to child component's vnode
  1074. updateHOCHostEl(instance, nextTree.el)
  1075. }
  1076. // updated hook
  1077. if (instance.u !== null) {
  1078. queuePostRenderEffect(instance.u, parentSuspense)
  1079. }
  1080. if (__DEV__) {
  1081. popWarningContext()
  1082. }
  1083. }
  1084. }, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions)
  1085. }
  1086. const updateComponentPreRender = (
  1087. instance: ComponentInternalInstance,
  1088. nextVNode: HostVNode
  1089. ) => {
  1090. nextVNode.component = instance
  1091. instance.vnode = nextVNode
  1092. instance.next = null
  1093. resolveProps(instance, nextVNode.props, (nextVNode.type as Component).props)
  1094. resolveSlots(instance, nextVNode.children)
  1095. }
  1096. const patchChildren: PatchChildrenFn<HostNode, HostElement> = (
  1097. n1,
  1098. n2,
  1099. container,
  1100. anchor,
  1101. parentComponent,
  1102. parentSuspense,
  1103. isSVG,
  1104. optimized = false
  1105. ) => {
  1106. const c1 = n1 && n1.children
  1107. const prevShapeFlag = n1 ? n1.shapeFlag : 0
  1108. const c2 = n2.children
  1109. const { patchFlag, shapeFlag } = n2
  1110. if (patchFlag === PatchFlags.BAIL) {
  1111. optimized = false
  1112. }
  1113. // fast path
  1114. if (patchFlag > 0) {
  1115. if (patchFlag & PatchFlags.KEYED_FRAGMENT) {
  1116. // this could be either fully-keyed or mixed (some keyed some not)
  1117. // presence of patchFlag means children are guaranteed to be arrays
  1118. patchKeyedChildren(
  1119. c1 as HostVNode[],
  1120. c2 as HostVNodeChildren,
  1121. container,
  1122. anchor,
  1123. parentComponent,
  1124. parentSuspense,
  1125. isSVG,
  1126. optimized
  1127. )
  1128. return
  1129. } else if (patchFlag & PatchFlags.UNKEYED_FRAGMENT) {
  1130. // unkeyed
  1131. patchUnkeyedChildren(
  1132. c1 as HostVNode[],
  1133. c2 as HostVNodeChildren,
  1134. container,
  1135. anchor,
  1136. parentComponent,
  1137. parentSuspense,
  1138. isSVG,
  1139. optimized
  1140. )
  1141. return
  1142. }
  1143. }
  1144. // children has 3 possibilities: text, array or no children.
  1145. if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
  1146. // text children fast path
  1147. if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
  1148. unmountChildren(c1 as HostVNode[], parentComponent, parentSuspense)
  1149. }
  1150. if (c2 !== c1) {
  1151. hostSetElementText(container, c2 as string)
  1152. }
  1153. } else {
  1154. if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
  1155. // prev children was array
  1156. if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
  1157. // two arrays, cannot assume anything, do full diff
  1158. patchKeyedChildren(
  1159. c1 as HostVNode[],
  1160. c2 as HostVNodeChildren,
  1161. container,
  1162. anchor,
  1163. parentComponent,
  1164. parentSuspense,
  1165. isSVG,
  1166. optimized
  1167. )
  1168. } else {
  1169. // no new children, just unmount old
  1170. unmountChildren(
  1171. c1 as HostVNode[],
  1172. parentComponent,
  1173. parentSuspense,
  1174. true
  1175. )
  1176. }
  1177. } else {
  1178. // prev children was text OR null
  1179. // new children is array OR null
  1180. if (prevShapeFlag & ShapeFlags.TEXT_CHILDREN) {
  1181. hostSetElementText(container, '')
  1182. }
  1183. // mount new if array
  1184. if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
  1185. mountChildren(
  1186. c2 as HostVNodeChildren,
  1187. container,
  1188. anchor,
  1189. parentComponent,
  1190. parentSuspense,
  1191. isSVG,
  1192. optimized
  1193. )
  1194. }
  1195. }
  1196. }
  1197. }
  1198. const patchUnkeyedChildren = (
  1199. c1: HostVNode[],
  1200. c2: HostVNodeChildren,
  1201. container: HostElement,
  1202. anchor: HostNode | null,
  1203. parentComponent: ComponentInternalInstance | null,
  1204. parentSuspense: HostSuspenseBoundary | null,
  1205. isSVG: boolean,
  1206. optimized: boolean
  1207. ) => {
  1208. c1 = c1 || EMPTY_ARR
  1209. c2 = c2 || EMPTY_ARR
  1210. const oldLength = c1.length
  1211. const newLength = c2.length
  1212. const commonLength = Math.min(oldLength, newLength)
  1213. let i
  1214. for (i = 0; i < commonLength; i++) {
  1215. const nextChild = (c2[i] = optimized
  1216. ? cloneIfMounted(c2[i] as HostVNode)
  1217. : normalizeVNode(c2[i]))
  1218. patch(
  1219. c1[i],
  1220. nextChild,
  1221. container,
  1222. null,
  1223. parentComponent,
  1224. parentSuspense,
  1225. isSVG,
  1226. optimized
  1227. )
  1228. }
  1229. if (oldLength > newLength) {
  1230. // remove old
  1231. unmountChildren(c1, parentComponent, parentSuspense, true, commonLength)
  1232. } else {
  1233. // mount new
  1234. mountChildren(
  1235. c2,
  1236. container,
  1237. anchor,
  1238. parentComponent,
  1239. parentSuspense,
  1240. isSVG,
  1241. optimized,
  1242. commonLength
  1243. )
  1244. }
  1245. }
  1246. // can be all-keyed or mixed
  1247. const patchKeyedChildren = (
  1248. c1: HostVNode[],
  1249. c2: HostVNodeChildren,
  1250. container: HostElement,
  1251. parentAnchor: HostNode | null,
  1252. parentComponent: ComponentInternalInstance | null,
  1253. parentSuspense: HostSuspenseBoundary | null,
  1254. isSVG: boolean,
  1255. optimized: boolean
  1256. ) => {
  1257. let i = 0
  1258. const l2 = c2.length
  1259. let e1 = c1.length - 1 // prev ending index
  1260. let e2 = l2 - 1 // next ending index
  1261. // 1. sync from start
  1262. // (a b) c
  1263. // (a b) d e
  1264. while (i <= e1 && i <= e2) {
  1265. const n1 = c1[i]
  1266. const n2 = (c2[i] = optimized
  1267. ? cloneIfMounted(c2[i] as HostVNode)
  1268. : normalizeVNode(c2[i]))
  1269. if (isSameVNodeType(n1, n2)) {
  1270. patch(
  1271. n1,
  1272. n2,
  1273. container,
  1274. parentAnchor,
  1275. parentComponent,
  1276. parentSuspense,
  1277. isSVG,
  1278. optimized
  1279. )
  1280. } else {
  1281. break
  1282. }
  1283. i++
  1284. }
  1285. // 2. sync from end
  1286. // a (b c)
  1287. // d e (b c)
  1288. while (i <= e1 && i <= e2) {
  1289. const n1 = c1[e1]
  1290. const n2 = (c2[e2] = optimized
  1291. ? cloneIfMounted(c2[e2] as HostVNode)
  1292. : normalizeVNode(c2[e2]))
  1293. if (isSameVNodeType(n1, n2)) {
  1294. patch(
  1295. n1,
  1296. n2,
  1297. container,
  1298. parentAnchor,
  1299. parentComponent,
  1300. parentSuspense,
  1301. isSVG,
  1302. optimized
  1303. )
  1304. } else {
  1305. break
  1306. }
  1307. e1--
  1308. e2--
  1309. }
  1310. // 3. common sequence + mount
  1311. // (a b)
  1312. // (a b) c
  1313. // i = 2, e1 = 1, e2 = 2
  1314. // (a b)
  1315. // c (a b)
  1316. // i = 0, e1 = -1, e2 = 0
  1317. if (i > e1) {
  1318. if (i <= e2) {
  1319. const nextPos = e2 + 1
  1320. const anchor =
  1321. nextPos < l2 ? (c2[nextPos] as HostVNode).el : parentAnchor
  1322. while (i <= e2) {
  1323. patch(
  1324. null,
  1325. (c2[i] = optimized
  1326. ? cloneIfMounted(c2[i] as HostVNode)
  1327. : normalizeVNode(c2[i])),
  1328. container,
  1329. anchor,
  1330. parentComponent,
  1331. parentSuspense,
  1332. isSVG
  1333. )
  1334. i++
  1335. }
  1336. }
  1337. }
  1338. // 4. common sequence + unmount
  1339. // (a b) c
  1340. // (a b)
  1341. // i = 2, e1 = 2, e2 = 1
  1342. // a (b c)
  1343. // (b c)
  1344. // i = 0, e1 = 0, e2 = -1
  1345. else if (i > e2) {
  1346. while (i <= e1) {
  1347. unmount(c1[i], parentComponent, parentSuspense, true)
  1348. i++
  1349. }
  1350. }
  1351. // 5. unknown sequence
  1352. // [i ... e1 + 1]: a b [c d e] f g
  1353. // [i ... e2 + 1]: a b [e d c h] f g
  1354. // i = 2, e1 = 4, e2 = 5
  1355. else {
  1356. const s1 = i // prev starting index
  1357. const s2 = i // next starting index
  1358. // 5.1 build key:index map for newChildren
  1359. const keyToNewIndexMap: Map<string | number, number> = new Map()
  1360. for (i = s2; i <= e2; i++) {
  1361. const nextChild = (c2[i] = optimized
  1362. ? cloneIfMounted(c2[i] as HostVNode)
  1363. : normalizeVNode(c2[i]))
  1364. if (nextChild.key != null) {
  1365. if (__DEV__ && keyToNewIndexMap.has(nextChild.key)) {
  1366. warn(
  1367. `Duplicate keys found during update:`,
  1368. JSON.stringify(nextChild.key),
  1369. `Make sure keys are unique.`
  1370. )
  1371. }
  1372. keyToNewIndexMap.set(nextChild.key, i)
  1373. }
  1374. }
  1375. // 5.2 loop through old children left to be patched and try to patch
  1376. // matching nodes & remove nodes that are no longer present
  1377. let j
  1378. let patched = 0
  1379. const toBePatched = e2 - s2 + 1
  1380. let moved = false
  1381. // used to track whether any node has moved
  1382. let maxNewIndexSoFar = 0
  1383. // works as Map<newIndex, oldIndex>
  1384. // Note that oldIndex is offset by +1
  1385. // and oldIndex = 0 is a special value indicating the new node has
  1386. // no corresponding old node.
  1387. // used for determining longest stable subsequence
  1388. const newIndexToOldIndexMap = new Array(toBePatched)
  1389. for (i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0
  1390. for (i = s1; i <= e1; i++) {
  1391. const prevChild = c1[i]
  1392. if (patched >= toBePatched) {
  1393. // all new children have been patched so this can only be a removal
  1394. unmount(prevChild, parentComponent, parentSuspense, true)
  1395. continue
  1396. }
  1397. let newIndex
  1398. if (prevChild.key != null) {
  1399. newIndex = keyToNewIndexMap.get(prevChild.key)
  1400. } else {
  1401. // key-less node, try to locate a key-less node of the same type
  1402. for (j = s2; j <= e2; j++) {
  1403. if (
  1404. newIndexToOldIndexMap[j - s2] === 0 &&
  1405. isSameVNodeType(prevChild, c2[j] as HostVNode)
  1406. ) {
  1407. newIndex = j
  1408. break
  1409. }
  1410. }
  1411. }
  1412. if (newIndex === undefined) {
  1413. unmount(prevChild, parentComponent, parentSuspense, true)
  1414. } else {
  1415. newIndexToOldIndexMap[newIndex - s2] = i + 1
  1416. if (newIndex >= maxNewIndexSoFar) {
  1417. maxNewIndexSoFar = newIndex
  1418. } else {
  1419. moved = true
  1420. }
  1421. patch(
  1422. prevChild,
  1423. c2[newIndex] as HostVNode,
  1424. container,
  1425. null,
  1426. parentComponent,
  1427. parentSuspense,
  1428. isSVG,
  1429. optimized
  1430. )
  1431. patched++
  1432. }
  1433. }
  1434. // 5.3 move and mount
  1435. // generate longest stable subsequence only when nodes have moved
  1436. const increasingNewIndexSequence = moved
  1437. ? getSequence(newIndexToOldIndexMap)
  1438. : EMPTY_ARR
  1439. j = increasingNewIndexSequence.length - 1
  1440. // looping backwards so that we can use last patched node as anchor
  1441. for (i = toBePatched - 1; i >= 0; i--) {
  1442. const nextIndex = s2 + i
  1443. const nextChild = c2[nextIndex] as HostVNode
  1444. const anchor =
  1445. nextIndex + 1 < l2
  1446. ? (c2[nextIndex + 1] as HostVNode).el
  1447. : parentAnchor
  1448. if (newIndexToOldIndexMap[i] === 0) {
  1449. // mount new
  1450. patch(
  1451. null,
  1452. nextChild,
  1453. container,
  1454. anchor,
  1455. parentComponent,
  1456. parentSuspense,
  1457. isSVG
  1458. )
  1459. } else if (moved) {
  1460. // move if:
  1461. // There is no stable subsequence (e.g. a reverse)
  1462. // OR current node is not among the stable sequence
  1463. if (j < 0 || i !== increasingNewIndexSequence[j]) {
  1464. move(nextChild, container, anchor, MoveType.REORDER)
  1465. } else {
  1466. j--
  1467. }
  1468. }
  1469. }
  1470. }
  1471. }
  1472. const move: MoveFn<HostNode, HostElement> = (
  1473. vnode,
  1474. container,
  1475. anchor,
  1476. type,
  1477. parentSuspense = null
  1478. ) => {
  1479. if (vnode.shapeFlag & ShapeFlags.COMPONENT) {
  1480. move(vnode.component!.subTree, container, anchor, type)
  1481. return
  1482. }
  1483. if (__FEATURE_SUSPENSE__ && vnode.shapeFlag & ShapeFlags.SUSPENSE) {
  1484. vnode.suspense!.move(container, anchor, type)
  1485. return
  1486. }
  1487. if (vnode.type === Fragment) {
  1488. hostInsert(vnode.el!, container, anchor)
  1489. const children = vnode.children as HostVNode[]
  1490. for (let i = 0; i < children.length; i++) {
  1491. move(children[i], container, anchor, type)
  1492. }
  1493. hostInsert(vnode.anchor!, container, anchor)
  1494. } else {
  1495. // Plain element
  1496. const { el, transition, shapeFlag } = vnode
  1497. const needTransition =
  1498. type !== MoveType.REORDER &&
  1499. shapeFlag & ShapeFlags.ELEMENT &&
  1500. transition != null
  1501. if (needTransition) {
  1502. if (type === MoveType.ENTER) {
  1503. transition!.beforeEnter(el!)
  1504. hostInsert(el!, container, anchor)
  1505. queuePostRenderEffect(() => transition!.enter(el!), parentSuspense)
  1506. } else {
  1507. const { leave, delayLeave, afterLeave } = transition!
  1508. const remove = () => hostInsert(el!, container, anchor)
  1509. const performLeave = () => {
  1510. leave(el!, () => {
  1511. remove()
  1512. afterLeave && afterLeave()
  1513. })
  1514. }
  1515. if (delayLeave) {
  1516. delayLeave(el!, remove, performLeave)
  1517. } else {
  1518. performLeave()
  1519. }
  1520. }
  1521. } else {
  1522. hostInsert(el!, container, anchor)
  1523. }
  1524. }
  1525. }
  1526. const unmount: UnmountFn<HostNode, HostElement> = (
  1527. vnode,
  1528. parentComponent,
  1529. parentSuspense,
  1530. doRemove = false
  1531. ) => {
  1532. const { props, ref, children, dynamicChildren, shapeFlag } = vnode
  1533. // unset ref
  1534. if (ref !== null && parentComponent !== null) {
  1535. setRef(ref, null, parentComponent, null)
  1536. }
  1537. if (shapeFlag & ShapeFlags.COMPONENT) {
  1538. if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
  1539. ;(parentComponent!.sink as KeepAliveSink).deactivate(vnode)
  1540. } else {
  1541. unmountComponent(vnode.component!, parentSuspense, doRemove)
  1542. }
  1543. return
  1544. }
  1545. if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
  1546. vnode.suspense!.unmount(parentSuspense, doRemove)
  1547. return
  1548. }
  1549. if (props != null && props.onVnodeBeforeUnmount != null) {
  1550. invokeDirectiveHook(props.onVnodeBeforeUnmount, parentComponent, vnode)
  1551. }
  1552. if (dynamicChildren != null) {
  1553. // fast path for block nodes: only need to unmount dynamic children.
  1554. unmountChildren(dynamicChildren, parentComponent, parentSuspense)
  1555. } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
  1556. unmountChildren(children as HostVNode[], parentComponent, parentSuspense)
  1557. }
  1558. if (doRemove) {
  1559. remove(vnode)
  1560. }
  1561. if (props != null && props.onVnodeUnmounted != null) {
  1562. queuePostRenderEffect(() => {
  1563. invokeDirectiveHook(props.onVnodeUnmounted!, parentComponent, vnode)
  1564. }, parentSuspense)
  1565. }
  1566. }
  1567. const remove = (vnode: HostVNode) => {
  1568. const { type, el, anchor, transition } = vnode
  1569. if (type === Fragment) {
  1570. removeFragment(el!, anchor!)
  1571. return
  1572. }
  1573. const performRemove = () => {
  1574. hostRemove(el!)
  1575. if (
  1576. transition != null &&
  1577. !transition.persisted &&
  1578. transition.afterLeave
  1579. ) {
  1580. transition.afterLeave()
  1581. }
  1582. }
  1583. if (
  1584. vnode.shapeFlag & ShapeFlags.ELEMENT &&
  1585. transition != null &&
  1586. !transition.persisted
  1587. ) {
  1588. const { leave, delayLeave } = transition
  1589. const performLeave = () => leave(el!, performRemove)
  1590. if (delayLeave) {
  1591. delayLeave(vnode.el!, performRemove, performLeave)
  1592. } else {
  1593. performLeave()
  1594. }
  1595. } else {
  1596. performRemove()
  1597. }
  1598. }
  1599. const removeFragment = (cur: HostNode, end: HostNode) => {
  1600. // For fragments, directly remove all contained DOM nodes.
  1601. // (fragment child nodes cannot have transition)
  1602. let next
  1603. while (cur !== end) {
  1604. next = hostNextSibling(cur)!
  1605. hostRemove(cur)
  1606. cur = next
  1607. }
  1608. hostRemove(end)
  1609. }
  1610. const unmountComponent = (
  1611. instance: ComponentInternalInstance,
  1612. parentSuspense: HostSuspenseBoundary | null,
  1613. doRemove?: boolean
  1614. ) => {
  1615. if (__HMR__ && instance.type.__hmrId != null) {
  1616. unregisterHMR(instance)
  1617. }
  1618. const { bum, effects, update, subTree, um, da, isDeactivated } = instance
  1619. // beforeUnmount hook
  1620. if (bum !== null) {
  1621. invokeHooks(bum)
  1622. }
  1623. if (effects !== null) {
  1624. for (let i = 0; i < effects.length; i++) {
  1625. stop(effects[i])
  1626. }
  1627. }
  1628. // update may be null if a component is unmounted before its async
  1629. // setup has resolved.
  1630. if (update !== null) {
  1631. stop(update)
  1632. unmount(subTree, instance, parentSuspense, doRemove)
  1633. }
  1634. // unmounted hook
  1635. if (um !== null) {
  1636. queuePostRenderEffect(um, parentSuspense)
  1637. }
  1638. // deactivated hook
  1639. if (
  1640. da !== null &&
  1641. !isDeactivated &&
  1642. instance.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
  1643. ) {
  1644. queuePostRenderEffect(da, parentSuspense)
  1645. }
  1646. queuePostFlushCb(() => {
  1647. instance.isUnmounted = true
  1648. })
  1649. // A component with async dep inside a pending suspense is unmounted before
  1650. // its async dep resolves. This should remove the dep from the suspense, and
  1651. // cause the suspense to resolve immediately if that was the last dep.
  1652. if (
  1653. __FEATURE_SUSPENSE__ &&
  1654. parentSuspense !== null &&
  1655. !parentSuspense.isResolved &&
  1656. !parentSuspense.isUnmounted &&
  1657. instance.asyncDep !== null &&
  1658. !instance.asyncResolved
  1659. ) {
  1660. parentSuspense.deps--
  1661. if (parentSuspense.deps === 0) {
  1662. parentSuspense.resolve()
  1663. }
  1664. }
  1665. }
  1666. const unmountChildren: UnmountChildrenFn<HostNode, HostElement> = (
  1667. children,
  1668. parentComponent,
  1669. parentSuspense,
  1670. doRemove = false,
  1671. start = 0
  1672. ) => {
  1673. for (let i = start; i < children.length; i++) {
  1674. unmount(children[i], parentComponent, parentSuspense, doRemove)
  1675. }
  1676. }
  1677. const getNextHostNode: NextFn<HostNode, HostElement> = vnode => {
  1678. if (vnode.shapeFlag & ShapeFlags.COMPONENT) {
  1679. return getNextHostNode(vnode.component!.subTree)
  1680. }
  1681. if (__FEATURE_SUSPENSE__ && vnode.shapeFlag & ShapeFlags.SUSPENSE) {
  1682. return vnode.suspense!.next()
  1683. }
  1684. return hostNextSibling((vnode.anchor || vnode.el)!)
  1685. }
  1686. const setRef = (
  1687. rawRef: VNodeNormalizedRef,
  1688. oldRawRef: VNodeNormalizedRef | null,
  1689. parent: ComponentInternalInstance,
  1690. value: HostNode | ComponentPublicInstance | null
  1691. ) => {
  1692. const [owner, ref] = rawRef
  1693. if (__DEV__ && !owner) {
  1694. warn(
  1695. `Missing ref owner context. ref cannot be used on hoisted vnodes. ` +
  1696. `A vnode with ref must be created inside the render function.`
  1697. )
  1698. return
  1699. }
  1700. const oldRef = oldRawRef && oldRawRef[1]
  1701. const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
  1702. const renderContext = toRaw(owner.renderContext)
  1703. // unset old ref
  1704. if (oldRef !== null && oldRef !== ref) {
  1705. if (isString(oldRef)) {
  1706. refs[oldRef] = null
  1707. const oldSetupRef = renderContext[oldRef]
  1708. if (isRef(oldSetupRef)) {
  1709. oldSetupRef.value = null
  1710. }
  1711. } else if (isRef(oldRef)) {
  1712. oldRef.value = null
  1713. }
  1714. }
  1715. if (isString(ref)) {
  1716. const setupRef = renderContext[ref]
  1717. if (isRef(setupRef)) {
  1718. setupRef.value = value
  1719. }
  1720. refs[ref] = value
  1721. } else if (isRef(ref)) {
  1722. ref.value = value
  1723. } else if (isFunction(ref)) {
  1724. callWithErrorHandling(ref, parent, ErrorCodes.FUNCTION_REF, [value, refs])
  1725. } else if (__DEV__) {
  1726. warn('Invalid template ref type:', value, `(${typeof value})`)
  1727. }
  1728. }
  1729. type HostRootElement = HostElement & { _vnode: HostVNode | null }
  1730. const render: RootRenderFunction<HostNode, HostElement> = (
  1731. vnode,
  1732. container: HostRootElement
  1733. ) => {
  1734. if (vnode == null) {
  1735. if (container._vnode) {
  1736. unmount(container._vnode, null, null, true)
  1737. }
  1738. } else {
  1739. patch(container._vnode || null, vnode, container)
  1740. }
  1741. flushPostFlushCbs()
  1742. container._vnode = vnode
  1743. }
  1744. const internals: RendererInternals<HostNode, HostElement> = {
  1745. p: patch,
  1746. um: unmount,
  1747. m: move,
  1748. mt: mountComponent,
  1749. mc: mountChildren,
  1750. pc: patchChildren,
  1751. pbc: patchBlockChildren,
  1752. n: getNextHostNode,
  1753. o: options
  1754. }
  1755. let hydrate: ReturnType<typeof createHydrationFunctions>[0] | undefined
  1756. let hydrateNode: ReturnType<typeof createHydrationFunctions>[1] | undefined
  1757. if (createHydrationFns) {
  1758. ;[hydrate, hydrateNode] = createHydrationFns(internals as RendererInternals<
  1759. Node,
  1760. Element
  1761. >)
  1762. }
  1763. return {
  1764. render,
  1765. hydrate,
  1766. createApp: createAppAPI(render, hydrate) as CreateAppFunction<HostElement>
  1767. }
  1768. }
  1769. // https://en.wikipedia.org/wiki/Longest_increasing_subsequence
  1770. function getSequence(arr: number[]): number[] {
  1771. const p = arr.slice()
  1772. const result = [0]
  1773. let i, j, u, v, c
  1774. const len = arr.length
  1775. for (i = 0; i < len; i++) {
  1776. const arrI = arr[i]
  1777. if (arrI !== 0) {
  1778. j = result[result.length - 1]
  1779. if (arr[j] < arrI) {
  1780. p[i] = j
  1781. result.push(i)
  1782. continue
  1783. }
  1784. u = 0
  1785. v = result.length - 1
  1786. while (u < v) {
  1787. c = ((u + v) / 2) | 0
  1788. if (arr[result[c]] < arrI) {
  1789. u = c + 1
  1790. } else {
  1791. v = c
  1792. }
  1793. }
  1794. if (arrI < arr[result[u]]) {
  1795. if (u > 0) {
  1796. p[i] = result[u - 1]
  1797. }
  1798. result[u] = i
  1799. }
  1800. }
  1801. }
  1802. u = result.length
  1803. v = result[u - 1]
  1804. while (u-- > 0) {
  1805. result[u] = v
  1806. v = p[v]
  1807. }
  1808. return result
  1809. }