2
0

renderer.ts 54 KB

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