renderer.ts 68 KB

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