renderer.ts 54 KB

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