createRenderer.ts 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573
  1. import {
  2. autorun,
  3. stop,
  4. Autorun,
  5. immutable,
  6. AutorunOptions
  7. } from '@vue/observer'
  8. import {
  9. queueJob,
  10. handleSchedulerError,
  11. nextTick,
  12. queueEffect,
  13. flushEffects,
  14. queueNodeOp
  15. } from '@vue/scheduler'
  16. import { VNodeFlags, ChildrenFlags } from './flags'
  17. import { EMPTY_OBJ, reservedPropRE, isString } from '@vue/shared'
  18. import {
  19. VNode,
  20. MountedVNode,
  21. RenderNode,
  22. createTextVNode,
  23. VNodeChildren
  24. } from './vdom'
  25. import { ComponentInstance } from './component'
  26. import {
  27. renderInstanceRoot,
  28. renderFunctionalRoot,
  29. createComponentInstance,
  30. teardownComponentInstance,
  31. shouldUpdateComponent,
  32. getReasonForComponentUpdate
  33. } from './componentUtils'
  34. import { KeepAliveSymbol } from './optional/keepAlive'
  35. import { pushWarningContext, popWarningContext, warn } from './warning'
  36. import { resolveProps } from './componentProps'
  37. import {
  38. handleError,
  39. ErrorTypes,
  40. callLifecycleHookWithHandler
  41. } from './errorHandling'
  42. export interface NodeOps {
  43. createElement: (tag: string, isSVG?: boolean) => any
  44. createText: (text: string) => any
  45. setText: (node: any, text: string) => void
  46. appendChild: (parent: any, child: any) => void
  47. insertBefore: (parent: any, child: any, ref: any) => void
  48. removeChild: (parent: any, child: any) => void
  49. clearContent: (node: any) => void
  50. parentNode: (node: any) => any
  51. nextSibling: (node: any) => any
  52. querySelector: (selector: string) => any
  53. }
  54. export interface PatchDataFunction {
  55. (
  56. el: any,
  57. key: string,
  58. prevValue: any,
  59. nextValue: any,
  60. preVNode: VNode | null,
  61. nextVNode: VNode,
  62. isSVG: boolean,
  63. // passed for DOM operations that removes child content
  64. // e.g. innerHTML & textContent
  65. unmountChildren: (children: VNode[], childFlags: ChildrenFlags) => void
  66. ): void
  67. }
  68. export interface RendererOptions {
  69. nodeOps: NodeOps
  70. patchData: PatchDataFunction
  71. teardownVNode?: (vnode: VNode) => void
  72. }
  73. export interface FunctionalHandle {
  74. current: VNode
  75. prevTree: VNode
  76. runner: Autorun
  77. forceUpdate: () => void
  78. }
  79. handleSchedulerError(err => handleError(err, null, ErrorTypes.SCHEDULER))
  80. // The whole mounting / patching / unmouting logic is placed inside this
  81. // single function so that we can create multiple renderes with different
  82. // platform definitions. This allows for use cases like creating a test
  83. // renderer alongside an actual renderer.
  84. export function createRenderer(options: RendererOptions) {
  85. const {
  86. nodeOps: {
  87. createElement: platformCreateElement,
  88. createText: platformCreateText,
  89. setText: platformSetText,
  90. appendChild: platformAppendChild,
  91. insertBefore: platformInsertBefore,
  92. removeChild: platformRemoveChild,
  93. clearContent: platformClearContent,
  94. parentNode: platformParentNode,
  95. nextSibling: platformNextSibling,
  96. querySelector: platformQuerySelector
  97. },
  98. patchData: platformPatchData,
  99. teardownVNode
  100. } = options
  101. function insertOrAppend(
  102. container: RenderNode,
  103. newNode: RenderNode,
  104. refNode: RenderNode | null
  105. ) {
  106. if (refNode === null) {
  107. queueNodeOp([platformAppendChild, container, newNode])
  108. } else {
  109. queueNodeOp([platformInsertBefore, container, newNode, refNode])
  110. }
  111. }
  112. // mounting ------------------------------------------------------------------
  113. function mount(
  114. vnode: VNode,
  115. container: RenderNode | null,
  116. contextVNode: MountedVNode | null,
  117. isSVG: boolean,
  118. endNode: RenderNode | null
  119. ) {
  120. const { flags } = vnode
  121. if (flags & VNodeFlags.ELEMENT) {
  122. mountElement(vnode, container, contextVNode, isSVG, endNode)
  123. } else if (flags & VNodeFlags.COMPONENT) {
  124. mountComponent(vnode, container, contextVNode, isSVG, endNode)
  125. } else if (flags & VNodeFlags.TEXT) {
  126. mountText(vnode, container, endNode)
  127. } else if (flags & VNodeFlags.FRAGMENT) {
  128. mountFragment(vnode, container, contextVNode, isSVG, endNode)
  129. } else if (flags & VNodeFlags.PORTAL) {
  130. mountPortal(vnode, container, contextVNode)
  131. }
  132. }
  133. function mountArrayChildren(
  134. children: VNode[],
  135. container: RenderNode | null,
  136. contextVNode: MountedVNode | null,
  137. isSVG: boolean,
  138. endNode: RenderNode | null
  139. ) {
  140. for (let i = 0; i < children.length; i++) {
  141. mount(children[i], container, contextVNode, isSVG, endNode)
  142. }
  143. }
  144. function mountElement(
  145. vnode: VNode,
  146. container: RenderNode | null,
  147. contextVNode: MountedVNode | null,
  148. isSVG: boolean,
  149. endNode: RenderNode | null
  150. ) {
  151. const { flags, tag, data, children, childFlags, ref } = vnode
  152. isSVG = isSVG || (flags & VNodeFlags.ELEMENT_SVG) > 0
  153. const el = (vnode.el = platformCreateElement(tag as string, isSVG))
  154. if (data != null) {
  155. for (const key in data) {
  156. patchData(el, key, null, data[key], null, vnode, isSVG)
  157. }
  158. if (data.vnodeBeforeMount) {
  159. data.vnodeBeforeMount(vnode)
  160. }
  161. }
  162. if (childFlags !== ChildrenFlags.NO_CHILDREN) {
  163. const hasSVGChildren = isSVG && tag !== 'foreignObject'
  164. if (childFlags & ChildrenFlags.SINGLE_VNODE) {
  165. mount(children as VNode, el, contextVNode, hasSVGChildren, null)
  166. } else if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
  167. mountArrayChildren(
  168. children as VNode[],
  169. el,
  170. contextVNode,
  171. hasSVGChildren,
  172. null
  173. )
  174. }
  175. }
  176. if (container != null) {
  177. insertOrAppend(container, el, endNode)
  178. }
  179. if (ref) {
  180. queueEffect(() => {
  181. ref(el)
  182. })
  183. }
  184. if (data != null && data.vnodeMounted) {
  185. queueEffect(() => {
  186. data.vnodeMounted(vnode)
  187. })
  188. }
  189. }
  190. function mountComponent(
  191. vnode: VNode,
  192. container: RenderNode | null,
  193. contextVNode: MountedVNode | null,
  194. isSVG: boolean,
  195. endNode: RenderNode | null
  196. ) {
  197. vnode.contextVNode = contextVNode
  198. const { flags } = vnode
  199. if (flags & VNodeFlags.COMPONENT_STATEFUL) {
  200. mountStatefulComponent(vnode, container, isSVG, endNode)
  201. } else {
  202. mountFunctionalComponent(vnode, container, isSVG, endNode)
  203. }
  204. }
  205. function mountStatefulComponent(
  206. vnode: VNode,
  207. container: RenderNode | null,
  208. isSVG: boolean,
  209. endNode: RenderNode | null
  210. ) {
  211. if (vnode.flags & VNodeFlags.COMPONENT_STATEFUL_KEPT_ALIVE) {
  212. // kept-alive
  213. activateComponentInstance(vnode, container, endNode)
  214. } else {
  215. if (__COMPAT__) {
  216. mountComponentInstance(vnode, container, isSVG, endNode)
  217. } else {
  218. queueJob(() => mountComponentInstance(vnode, container, isSVG, endNode))
  219. }
  220. }
  221. }
  222. function mountFunctionalComponent(
  223. vnode: VNode,
  224. container: RenderNode | null,
  225. isSVG: boolean,
  226. endNode: RenderNode | null
  227. ) {
  228. if (__DEV__ && vnode.ref) {
  229. warn(
  230. `cannot use ref on a functional component because there is no ` +
  231. `instance to reference to.`
  232. )
  233. }
  234. const handle: FunctionalHandle = (vnode.handle = {
  235. current: vnode,
  236. prevTree: null as any,
  237. runner: null as any,
  238. forceUpdate: null as any
  239. })
  240. const queueUpdate = (handle.forceUpdate = () => {
  241. queueJob(handle.runner)
  242. })
  243. const doMount = () => {
  244. handle.runner = autorun(
  245. () => {
  246. if (!handle.prevTree) {
  247. // initial mount
  248. if (__DEV__) {
  249. pushWarningContext(vnode)
  250. }
  251. const subTree = (handle.prevTree = vnode.children = renderFunctionalRoot(
  252. vnode
  253. ))
  254. queueEffect(() => {
  255. vnode.el = subTree.el as RenderNode
  256. })
  257. mount(subTree, container, vnode as MountedVNode, isSVG, endNode)
  258. if (__DEV__) {
  259. popWarningContext()
  260. }
  261. } else {
  262. updateFunctionalComponent(handle, isSVG)
  263. }
  264. },
  265. {
  266. scheduler: queueUpdate
  267. }
  268. )
  269. }
  270. // we are using vnode.ref to store the functional component's update job
  271. if (__COMPAT__) {
  272. doMount()
  273. } else {
  274. queueJob(() => {
  275. doMount()
  276. // cleanup if mount is invalidated before committed
  277. return () => {
  278. stop(handle.runner)
  279. }
  280. })
  281. }
  282. }
  283. function updateFunctionalComponent(handle: FunctionalHandle, isSVG: boolean) {
  284. // mounted
  285. const { prevTree, current } = handle
  286. if (__DEV__) {
  287. pushWarningContext(current)
  288. }
  289. const nextTree = (handle.prevTree = current.children = renderFunctionalRoot(
  290. current
  291. ))
  292. queueEffect(() => {
  293. current.el = nextTree.el
  294. })
  295. patch(
  296. prevTree as MountedVNode,
  297. nextTree,
  298. platformParentNode(current.el),
  299. current as MountedVNode,
  300. isSVG
  301. )
  302. if (__DEV__) {
  303. popWarningContext()
  304. }
  305. }
  306. function mountText(
  307. vnode: VNode,
  308. container: RenderNode | null,
  309. endNode: RenderNode | null
  310. ) {
  311. const el = (vnode.el = platformCreateText(vnode.children as string))
  312. if (container != null) {
  313. insertOrAppend(container, el, endNode)
  314. }
  315. }
  316. function mountFragment(
  317. vnode: VNode,
  318. container: RenderNode | null,
  319. contextVNode: MountedVNode | null,
  320. isSVG: boolean,
  321. endNode: RenderNode | null
  322. ) {
  323. const { children, childFlags } = vnode
  324. switch (childFlags) {
  325. case ChildrenFlags.SINGLE_VNODE:
  326. queueEffect(() => {
  327. vnode.el = (children as MountedVNode).el
  328. })
  329. mount(children as VNode, container, contextVNode, isSVG, endNode)
  330. break
  331. case ChildrenFlags.NO_CHILDREN:
  332. const placeholder = createTextVNode('')
  333. mountText(placeholder, container, null)
  334. vnode.el = placeholder.el
  335. break
  336. default:
  337. queueEffect(() => {
  338. vnode.el = (children as MountedVNode[])[0].el
  339. })
  340. mountArrayChildren(
  341. children as VNode[],
  342. container,
  343. contextVNode,
  344. isSVG,
  345. endNode
  346. )
  347. }
  348. }
  349. function mountPortal(
  350. vnode: VNode,
  351. container: RenderNode | null,
  352. contextVNode: MountedVNode | null
  353. ) {
  354. const { tag, children, childFlags, ref } = vnode
  355. const target = isString(tag) ? platformQuerySelector(tag) : tag
  356. if (__DEV__ && !target) {
  357. // TODO warn poartal target not found
  358. }
  359. if (childFlags & ChildrenFlags.SINGLE_VNODE) {
  360. mount(children as VNode, target as RenderNode, contextVNode, false, null)
  361. } else if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
  362. mountArrayChildren(
  363. children as VNode[],
  364. target as RenderNode,
  365. contextVNode,
  366. false,
  367. null
  368. )
  369. }
  370. if (ref) {
  371. queueEffect(() => {
  372. ref(target)
  373. })
  374. }
  375. const placeholder = createTextVNode('')
  376. mountText(placeholder, container, null)
  377. vnode.el = placeholder.el
  378. }
  379. // patching ------------------------------------------------------------------
  380. function patchData(
  381. el: RenderNode | (() => RenderNode),
  382. key: string,
  383. prevValue: any,
  384. nextValue: any,
  385. preVNode: VNode | null,
  386. nextVNode: VNode,
  387. isSVG: boolean
  388. ) {
  389. if (reservedPropRE.test(key)) {
  390. return
  391. }
  392. platformPatchData(
  393. typeof el === 'function' ? el() : el,
  394. key,
  395. prevValue,
  396. nextValue,
  397. preVNode,
  398. nextVNode,
  399. isSVG,
  400. unmountChildren
  401. )
  402. }
  403. function patch(
  404. prevVNode: MountedVNode,
  405. nextVNode: VNode,
  406. container: RenderNode,
  407. contextVNode: MountedVNode | null,
  408. isSVG: boolean
  409. ) {
  410. if (prevVNode === nextVNode) {
  411. nextVNode.el = prevVNode.el
  412. return
  413. }
  414. const nextFlags = nextVNode.flags
  415. const prevFlags = prevVNode.flags
  416. if (prevFlags !== nextFlags) {
  417. replaceVNode(prevVNode, nextVNode, container, contextVNode, isSVG)
  418. } else if (nextFlags & VNodeFlags.ELEMENT) {
  419. patchElement(prevVNode, nextVNode, container, contextVNode, isSVG)
  420. } else if (nextFlags & VNodeFlags.COMPONENT) {
  421. patchComponent(prevVNode, nextVNode, container, contextVNode, isSVG)
  422. } else if (nextFlags & VNodeFlags.TEXT) {
  423. patchText(prevVNode, nextVNode)
  424. } else if (nextFlags & VNodeFlags.FRAGMENT) {
  425. patchFragment(prevVNode, nextVNode, container, contextVNode, isSVG)
  426. } else if (nextFlags & VNodeFlags.PORTAL) {
  427. patchPortal(prevVNode, nextVNode, contextVNode)
  428. }
  429. }
  430. function patchElement(
  431. prevVNode: MountedVNode,
  432. nextVNode: VNode,
  433. container: RenderNode,
  434. contextVNode: MountedVNode | null,
  435. isSVG: boolean
  436. ) {
  437. const { flags, tag } = nextVNode
  438. isSVG = isSVG || (flags & VNodeFlags.ELEMENT_SVG) > 0
  439. if (prevVNode.tag !== tag) {
  440. replaceVNode(prevVNode, nextVNode, container, contextVNode, isSVG)
  441. return
  442. }
  443. const el = (nextVNode.el = prevVNode.el)
  444. const prevData = prevVNode.data
  445. const nextData = nextVNode.data
  446. if (nextData != null && nextData.vnodeBeforeUpdate) {
  447. nextData.vnodeBeforeUpdate(nextVNode, prevVNode)
  448. }
  449. // patch data
  450. if (prevData !== nextData) {
  451. const prevDataOrEmpty = prevData || EMPTY_OBJ
  452. const nextDataOrEmpty = nextData || EMPTY_OBJ
  453. if (nextDataOrEmpty !== EMPTY_OBJ) {
  454. for (const key in nextDataOrEmpty) {
  455. const prevValue = prevDataOrEmpty[key]
  456. const nextValue = nextDataOrEmpty[key]
  457. if (prevValue !== nextValue) {
  458. patchData(
  459. el,
  460. key,
  461. prevValue,
  462. nextValue,
  463. prevVNode,
  464. nextVNode,
  465. isSVG
  466. )
  467. }
  468. }
  469. }
  470. if (prevDataOrEmpty !== EMPTY_OBJ) {
  471. for (const key in prevDataOrEmpty) {
  472. const prevValue = prevDataOrEmpty[key]
  473. if (prevValue != null && !nextDataOrEmpty.hasOwnProperty(key)) {
  474. patchData(el, key, prevValue, null, prevVNode, nextVNode, isSVG)
  475. }
  476. }
  477. }
  478. }
  479. // children
  480. patchChildren(
  481. prevVNode.childFlags,
  482. nextVNode.childFlags,
  483. prevVNode.children,
  484. nextVNode.children,
  485. el,
  486. contextVNode,
  487. isSVG && nextVNode.tag !== 'foreignObject',
  488. null
  489. )
  490. if (nextData != null && nextData.vnodeUpdated) {
  491. // TODO fix me
  492. // vnodeUpdatedHooks.push(() => {
  493. // nextData.vnodeUpdated(nextVNode, prevVNode)
  494. // })
  495. }
  496. }
  497. function patchComponent(
  498. prevVNode: MountedVNode,
  499. nextVNode: VNode,
  500. container: RenderNode,
  501. contextVNode: MountedVNode | null,
  502. isSVG: boolean
  503. ) {
  504. nextVNode.contextVNode = contextVNode
  505. const { tag, flags } = nextVNode
  506. if (tag !== prevVNode.tag) {
  507. replaceVNode(prevVNode, nextVNode, container, contextVNode, isSVG)
  508. } else if (flags & VNodeFlags.COMPONENT_STATEFUL) {
  509. patchStatefulComponent(prevVNode, nextVNode)
  510. } else {
  511. patchFunctionalComponent(prevVNode, nextVNode)
  512. }
  513. }
  514. function patchStatefulComponent(prevVNode: MountedVNode, nextVNode: VNode) {
  515. const { data: prevData } = prevVNode
  516. const { data: nextData, slots: nextSlots } = nextVNode
  517. const instance = (nextVNode.children =
  518. prevVNode.children) as ComponentInstance
  519. if (nextData !== prevData) {
  520. const { 0: props, 1: attrs } = resolveProps(
  521. nextData,
  522. instance.$options.props
  523. )
  524. instance.$props = __DEV__ ? immutable(props) : props
  525. instance.$attrs = __DEV__ ? immutable(attrs) : attrs
  526. }
  527. instance.$slots = nextSlots || EMPTY_OBJ
  528. instance.$parentVNode = nextVNode as MountedVNode
  529. if (shouldUpdateComponent(prevVNode, nextVNode)) {
  530. if (__DEV__ && instance.$options.renderTriggered) {
  531. callLifecycleHookWithHandler(
  532. instance.$options.renderTriggered,
  533. instance.$proxy,
  534. ErrorTypes.RENDER_TRIGGERED,
  535. getReasonForComponentUpdate(prevVNode, nextVNode)
  536. )
  537. }
  538. instance.$forceUpdate()
  539. } else if (instance.$vnode.flags & VNodeFlags.COMPONENT) {
  540. instance.$vnode.contextVNode = nextVNode
  541. }
  542. nextVNode.el = instance.$vnode.el
  543. }
  544. function patchFunctionalComponent(prevVNode: MountedVNode, nextVNode: VNode) {
  545. const prevTree = prevVNode.children as VNode
  546. const handle = (nextVNode.handle = prevVNode.handle as FunctionalHandle)
  547. handle.current = nextVNode
  548. if (shouldUpdateComponent(prevVNode, nextVNode)) {
  549. handle.forceUpdate()
  550. } else if (prevTree.flags & VNodeFlags.COMPONENT) {
  551. // functional component returned another component
  552. prevTree.contextVNode = nextVNode
  553. }
  554. }
  555. function patchFragment(
  556. prevVNode: MountedVNode,
  557. nextVNode: VNode,
  558. container: RenderNode,
  559. contextVNode: MountedVNode | null,
  560. isSVG: boolean
  561. ) {
  562. // determine the tail node of the previous fragment,
  563. // then retrieve its next sibling to use as the end node for patchChildren.
  564. const endNode = platformNextSibling(getVNodeLastEl(prevVNode))
  565. const { childFlags, children } = nextVNode
  566. queueEffect(() => {
  567. switch (childFlags) {
  568. case ChildrenFlags.SINGLE_VNODE:
  569. nextVNode.el = (children as MountedVNode).el
  570. break
  571. case ChildrenFlags.NO_CHILDREN:
  572. nextVNode.el = prevVNode.el
  573. break
  574. default:
  575. nextVNode.el = (children as MountedVNode[])[0].el
  576. }
  577. })
  578. patchChildren(
  579. prevVNode.childFlags,
  580. childFlags,
  581. prevVNode.children,
  582. children,
  583. container,
  584. contextVNode,
  585. isSVG,
  586. endNode
  587. )
  588. }
  589. function getVNodeLastEl(vnode: MountedVNode): RenderNode {
  590. const { el, flags, children, childFlags } = vnode
  591. if (flags & VNodeFlags.FRAGMENT) {
  592. if (childFlags & ChildrenFlags.SINGLE_VNODE) {
  593. return getVNodeLastEl(children as MountedVNode)
  594. } else if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
  595. return getVNodeLastEl(
  596. (children as MountedVNode[])[(children as MountedVNode[]).length - 1]
  597. )
  598. } else {
  599. return el
  600. }
  601. } else {
  602. return el
  603. }
  604. }
  605. function patchText(prevVNode: MountedVNode, nextVNode: VNode) {
  606. const el = (nextVNode.el = prevVNode.el) as RenderNode
  607. const nextText = nextVNode.children
  608. if (nextText !== prevVNode.children) {
  609. queueNodeOp([platformSetText, el, nextText])
  610. }
  611. }
  612. function patchPortal(
  613. prevVNode: MountedVNode,
  614. nextVNode: VNode,
  615. contextVNode: MountedVNode | null
  616. ) {
  617. const prevContainer = prevVNode.tag as RenderNode
  618. const nextContainer = nextVNode.tag as RenderNode
  619. const nextChildren = nextVNode.children
  620. patchChildren(
  621. prevVNode.childFlags,
  622. nextVNode.childFlags,
  623. prevVNode.children,
  624. nextChildren,
  625. prevContainer,
  626. contextVNode,
  627. false,
  628. null
  629. )
  630. nextVNode.el = prevVNode.el
  631. if (nextContainer !== prevContainer) {
  632. switch (nextVNode.childFlags) {
  633. case ChildrenFlags.SINGLE_VNODE:
  634. insertVNode(nextChildren as MountedVNode, nextContainer, null)
  635. break
  636. case ChildrenFlags.NO_CHILDREN:
  637. break
  638. default:
  639. for (let i = 0; i < (nextChildren as MountedVNode[]).length; i++) {
  640. insertVNode(
  641. (nextChildren as MountedVNode[])[i],
  642. nextContainer,
  643. null
  644. )
  645. }
  646. break
  647. }
  648. }
  649. }
  650. function replaceVNode(
  651. prevVNode: MountedVNode,
  652. nextVNode: VNode,
  653. container: RenderNode,
  654. contextVNode: MountedVNode | null,
  655. isSVG: boolean
  656. ) {
  657. const refNode = platformNextSibling(getVNodeLastEl(prevVNode))
  658. queueRemoveVNode(prevVNode, container)
  659. mount(nextVNode, container, contextVNode, isSVG, refNode)
  660. }
  661. function patchChildren(
  662. prevChildFlags: ChildrenFlags,
  663. nextChildFlags: ChildrenFlags,
  664. prevChildren: VNodeChildren,
  665. nextChildren: VNodeChildren,
  666. container: RenderNode,
  667. contextVNode: MountedVNode | null,
  668. isSVG: boolean,
  669. endNode: RenderNode | null
  670. ) {
  671. switch (prevChildFlags) {
  672. case ChildrenFlags.SINGLE_VNODE:
  673. switch (nextChildFlags) {
  674. case ChildrenFlags.SINGLE_VNODE:
  675. patch(
  676. prevChildren as MountedVNode,
  677. nextChildren as VNode,
  678. container,
  679. contextVNode,
  680. isSVG
  681. )
  682. break
  683. case ChildrenFlags.NO_CHILDREN:
  684. queueRemoveVNode(prevChildren as MountedVNode, container)
  685. break
  686. default:
  687. queueRemoveVNode(prevChildren as MountedVNode, container)
  688. mountArrayChildren(
  689. nextChildren as VNode[],
  690. container,
  691. contextVNode,
  692. isSVG,
  693. endNode
  694. )
  695. break
  696. }
  697. break
  698. case ChildrenFlags.NO_CHILDREN:
  699. switch (nextChildFlags) {
  700. case ChildrenFlags.SINGLE_VNODE:
  701. mount(
  702. nextChildren as VNode,
  703. container,
  704. contextVNode,
  705. isSVG,
  706. endNode
  707. )
  708. break
  709. case ChildrenFlags.NO_CHILDREN:
  710. break
  711. default:
  712. mountArrayChildren(
  713. nextChildren as VNode[],
  714. container,
  715. contextVNode,
  716. isSVG,
  717. endNode
  718. )
  719. break
  720. }
  721. break
  722. default:
  723. // MULTIPLE_CHILDREN
  724. if (nextChildFlags === ChildrenFlags.SINGLE_VNODE) {
  725. queueRemoveChildren(
  726. prevChildren as MountedVNode[],
  727. container,
  728. endNode
  729. )
  730. mount(nextChildren as VNode, container, contextVNode, isSVG, endNode)
  731. } else if (nextChildFlags === ChildrenFlags.NO_CHILDREN) {
  732. queueRemoveChildren(
  733. prevChildren as MountedVNode[],
  734. container,
  735. endNode
  736. )
  737. } else {
  738. const prevLength = (prevChildren as VNode[]).length
  739. const nextLength = (nextChildren as VNode[]).length
  740. if (prevLength === 0) {
  741. if (nextLength > 0) {
  742. mountArrayChildren(
  743. nextChildren as VNode[],
  744. container,
  745. contextVNode,
  746. isSVG,
  747. endNode
  748. )
  749. }
  750. } else if (nextLength === 0) {
  751. queueRemoveChildren(
  752. prevChildren as MountedVNode[],
  753. container,
  754. endNode
  755. )
  756. } else if (
  757. prevChildFlags === ChildrenFlags.KEYED_VNODES &&
  758. nextChildFlags === ChildrenFlags.KEYED_VNODES
  759. ) {
  760. patchKeyedChildren(
  761. prevChildren as MountedVNode[],
  762. nextChildren as VNode[],
  763. container,
  764. prevLength,
  765. nextLength,
  766. contextVNode,
  767. isSVG,
  768. endNode
  769. )
  770. } else {
  771. patchNonKeyedChildren(
  772. prevChildren as MountedVNode[],
  773. nextChildren as VNode[],
  774. container,
  775. prevLength,
  776. nextLength,
  777. contextVNode,
  778. isSVG,
  779. endNode
  780. )
  781. }
  782. }
  783. break
  784. }
  785. }
  786. function patchNonKeyedChildren(
  787. prevChildren: MountedVNode[],
  788. nextChildren: VNode[],
  789. container: RenderNode,
  790. prevLength: number,
  791. nextLength: number,
  792. contextVNode: MountedVNode | null,
  793. isSVG: boolean,
  794. endNode: RenderNode | null
  795. ) {
  796. const commonLength = prevLength > nextLength ? nextLength : prevLength
  797. let i = 0
  798. let nextChild
  799. let prevChild
  800. for (i; i < commonLength; i++) {
  801. nextChild = nextChildren[i]
  802. prevChild = prevChildren[i]
  803. patch(prevChild, nextChild, container, contextVNode, isSVG)
  804. prevChildren[i] = nextChild as MountedVNode
  805. }
  806. if (prevLength < nextLength) {
  807. for (i = commonLength; i < nextLength; i++) {
  808. mount(nextChildren[i], container, contextVNode, isSVG, endNode)
  809. }
  810. } else if (prevLength > nextLength) {
  811. for (i = commonLength; i < prevLength; i++) {
  812. queueRemoveVNode(prevChildren[i], container)
  813. }
  814. }
  815. }
  816. function patchKeyedChildren(
  817. prevChildren: MountedVNode[],
  818. nextChildren: VNode[],
  819. container: RenderNode,
  820. prevLength: number,
  821. nextLength: number,
  822. contextVNode: MountedVNode | null,
  823. isSVG: boolean,
  824. endNode: RenderNode | null
  825. ) {
  826. let prevEnd = prevLength - 1
  827. let nextEnd = nextLength - 1
  828. let i
  829. let j = 0
  830. let prevVNode = prevChildren[j]
  831. let nextVNode = nextChildren[j]
  832. let nextPos
  833. outer: {
  834. // Sync nodes with the same key at the beginning.
  835. while (prevVNode.key === nextVNode.key) {
  836. patch(prevVNode, nextVNode, container, contextVNode, isSVG)
  837. prevChildren[j] = nextVNode as MountedVNode
  838. j++
  839. if (j > prevEnd || j > nextEnd) {
  840. break outer
  841. }
  842. prevVNode = prevChildren[j]
  843. nextVNode = nextChildren[j]
  844. }
  845. prevVNode = prevChildren[prevEnd]
  846. nextVNode = nextChildren[nextEnd]
  847. // Sync nodes with the same key at the end.
  848. while (prevVNode.key === nextVNode.key) {
  849. patch(prevVNode, nextVNode, container, contextVNode, isSVG)
  850. prevChildren[prevEnd] = nextVNode as MountedVNode
  851. prevEnd--
  852. nextEnd--
  853. if (j > prevEnd || j > nextEnd) {
  854. break outer
  855. }
  856. prevVNode = prevChildren[prevEnd]
  857. nextVNode = nextChildren[nextEnd]
  858. }
  859. }
  860. if (j > prevEnd) {
  861. if (j <= nextEnd) {
  862. nextPos = nextEnd + 1
  863. const nextNode =
  864. nextPos < nextLength ? nextChildren[nextPos].el : endNode
  865. while (j <= nextEnd) {
  866. nextVNode = nextChildren[j++]
  867. mount(nextVNode, container, contextVNode, isSVG, nextNode)
  868. }
  869. }
  870. } else if (j > nextEnd) {
  871. while (j <= prevEnd) {
  872. queueRemoveVNode(prevChildren[j++], container)
  873. }
  874. } else {
  875. let prevStart = j
  876. const nextStart = j
  877. const prevLeft = prevEnd - j + 1
  878. const nextLeft = nextEnd - j + 1
  879. const sources: number[] = []
  880. for (i = 0; i < nextLeft; i++) {
  881. sources.push(0)
  882. }
  883. // Keep track if its possible to remove whole DOM using textContent = ''
  884. let canRemoveWholeContent = prevLeft === prevLength
  885. let moved = false
  886. let pos = 0
  887. let patched = 0
  888. // When sizes are small, just loop them through
  889. if (nextLength < 4 || (prevLeft | nextLeft) < 32) {
  890. for (i = prevStart; i <= prevEnd; i++) {
  891. prevVNode = prevChildren[i]
  892. if (patched < nextLeft) {
  893. for (j = nextStart; j <= nextEnd; j++) {
  894. nextVNode = nextChildren[j]
  895. if (prevVNode.key === nextVNode.key) {
  896. sources[j - nextStart] = i + 1
  897. if (canRemoveWholeContent) {
  898. canRemoveWholeContent = false
  899. while (i > prevStart) {
  900. queueRemoveVNode(prevChildren[prevStart++], container)
  901. }
  902. }
  903. if (pos > j) {
  904. moved = true
  905. } else {
  906. pos = j
  907. }
  908. patch(prevVNode, nextVNode, container, contextVNode, isSVG)
  909. patched++
  910. break
  911. }
  912. }
  913. if (!canRemoveWholeContent && j > nextEnd) {
  914. queueRemoveVNode(prevVNode, container)
  915. }
  916. } else if (!canRemoveWholeContent) {
  917. queueRemoveVNode(prevVNode, container)
  918. }
  919. }
  920. } else {
  921. const keyIndex: Record<string, number> = {}
  922. // Map keys by their index
  923. for (i = nextStart; i <= nextEnd; i++) {
  924. keyIndex[nextChildren[i].key as string] = i
  925. }
  926. // Try to patch same keys
  927. for (i = prevStart; i <= prevEnd; i++) {
  928. prevVNode = prevChildren[i]
  929. if (patched < nextLeft) {
  930. j = keyIndex[prevVNode.key as string]
  931. if (j !== void 0) {
  932. if (canRemoveWholeContent) {
  933. canRemoveWholeContent = false
  934. while (i > prevStart) {
  935. queueRemoveVNode(prevChildren[prevStart++], container)
  936. }
  937. }
  938. nextVNode = nextChildren[j]
  939. sources[j - nextStart] = i + 1
  940. if (pos > j) {
  941. moved = true
  942. } else {
  943. pos = j
  944. }
  945. patch(prevVNode, nextVNode, container, contextVNode, isSVG)
  946. patched++
  947. } else if (!canRemoveWholeContent) {
  948. queueRemoveVNode(prevVNode, container)
  949. }
  950. } else if (!canRemoveWholeContent) {
  951. queueRemoveVNode(prevVNode, container)
  952. }
  953. }
  954. }
  955. // fast-path: if nothing patched remove all old and add all new
  956. if (canRemoveWholeContent) {
  957. queueRemoveChildren(prevChildren as MountedVNode[], container, endNode)
  958. mountArrayChildren(
  959. nextChildren,
  960. container,
  961. contextVNode,
  962. isSVG,
  963. endNode
  964. )
  965. } else {
  966. if (moved) {
  967. const seq = lis(sources)
  968. j = seq.length - 1
  969. for (i = nextLeft - 1; i >= 0; i--) {
  970. if (sources[i] === 0) {
  971. pos = i + nextStart
  972. nextVNode = nextChildren[pos]
  973. nextPos = pos + 1
  974. mount(
  975. nextVNode,
  976. container,
  977. contextVNode,
  978. isSVG,
  979. nextPos < nextLength ? nextChildren[nextPos].el : endNode
  980. )
  981. } else if (j < 0 || i !== seq[j]) {
  982. pos = i + nextStart
  983. nextVNode = nextChildren[pos]
  984. nextPos = pos + 1
  985. insertVNode(
  986. nextVNode as MountedVNode,
  987. container,
  988. nextPos < nextLength ? nextChildren[nextPos].el : endNode
  989. )
  990. } else {
  991. j--
  992. }
  993. }
  994. } else if (patched !== nextLeft) {
  995. // when patched count doesn't match b length we need to insert those
  996. // new ones loop backwards so we can use insertBefore
  997. for (i = nextLeft - 1; i >= 0; i--) {
  998. if (sources[i] === 0) {
  999. pos = i + nextStart
  1000. nextVNode = nextChildren[pos]
  1001. nextPos = pos + 1
  1002. mount(
  1003. nextVNode,
  1004. container,
  1005. contextVNode,
  1006. isSVG,
  1007. nextPos < nextLength ? nextChildren[nextPos].el : endNode
  1008. )
  1009. }
  1010. }
  1011. }
  1012. }
  1013. }
  1014. }
  1015. function insertVNode(
  1016. vnode: MountedVNode,
  1017. container: RenderNode,
  1018. refNode: RenderNode | null
  1019. ) {
  1020. const { flags, childFlags, children } = vnode
  1021. if (flags & VNodeFlags.FRAGMENT) {
  1022. switch (childFlags) {
  1023. case ChildrenFlags.SINGLE_VNODE:
  1024. insertVNode(children as MountedVNode, container, refNode)
  1025. break
  1026. case ChildrenFlags.NO_CHILDREN:
  1027. break
  1028. default:
  1029. for (let i = 0; i < (children as MountedVNode[]).length; i++) {
  1030. insertVNode((children as MountedVNode[])[i], container, refNode)
  1031. }
  1032. }
  1033. } else {
  1034. insertOrAppend(container, vnode.el as RenderNode, refNode)
  1035. }
  1036. }
  1037. // unmounting ----------------------------------------------------------------
  1038. function unmount(vnode: MountedVNode) {
  1039. const { flags, data, children, childFlags, ref, handle } = vnode
  1040. const isElement = flags & VNodeFlags.ELEMENT
  1041. if (isElement || flags & VNodeFlags.FRAGMENT) {
  1042. if (isElement && data != null && data.vnodeBeforeUnmount) {
  1043. data.vnodeBeforeUnmount(vnode)
  1044. }
  1045. unmountChildren(children as VNodeChildren, childFlags)
  1046. if (teardownVNode !== void 0) {
  1047. teardownVNode(vnode)
  1048. }
  1049. if (isElement && data != null && data.vnodeUnmounted) {
  1050. data.vnodeUnmounted(vnode)
  1051. }
  1052. } else if (flags & VNodeFlags.COMPONENT) {
  1053. if (flags & VNodeFlags.COMPONENT_STATEFUL) {
  1054. if (flags & VNodeFlags.COMPONENT_STATEFUL_SHOULD_KEEP_ALIVE) {
  1055. deactivateComponentInstance(children as ComponentInstance)
  1056. } else {
  1057. unmountComponentInstance(children as ComponentInstance)
  1058. }
  1059. } else {
  1060. // functional
  1061. stop((handle as FunctionalHandle).runner)
  1062. unmount(children as MountedVNode)
  1063. }
  1064. } else if (flags & VNodeFlags.PORTAL) {
  1065. if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
  1066. queueRemoveChildren(
  1067. children as MountedVNode[],
  1068. vnode.tag as RenderNode,
  1069. null
  1070. )
  1071. } else if (childFlags === ChildrenFlags.SINGLE_VNODE) {
  1072. queueRemoveVNode(children as MountedVNode, vnode.tag as RenderNode)
  1073. }
  1074. }
  1075. if (ref) {
  1076. ref(null)
  1077. }
  1078. }
  1079. function unmountChildren(children: VNodeChildren, childFlags: ChildrenFlags) {
  1080. if (childFlags & ChildrenFlags.MULTIPLE_VNODES) {
  1081. unmountArrayChildren(children as MountedVNode[])
  1082. } else if (childFlags === ChildrenFlags.SINGLE_VNODE) {
  1083. unmount(children as MountedVNode)
  1084. }
  1085. }
  1086. function unmountArrayChildren(children: MountedVNode[]) {
  1087. for (let i = 0; i < children.length; i++) {
  1088. unmount(children[i])
  1089. }
  1090. }
  1091. function queueRemoveVNode(vnode: MountedVNode, container: RenderNode) {
  1092. queueNodeOp([removeVNode, vnode, container])
  1093. }
  1094. function removeVNode(vnode: MountedVNode, container: RenderNode) {
  1095. unmount(vnode)
  1096. const { el, flags, children, childFlags } = vnode
  1097. if (container && el) {
  1098. if (flags & VNodeFlags.FRAGMENT) {
  1099. switch (childFlags) {
  1100. case ChildrenFlags.SINGLE_VNODE:
  1101. removeVNode(children as MountedVNode, container)
  1102. break
  1103. case ChildrenFlags.NO_CHILDREN:
  1104. platformRemoveChild(container, el)
  1105. break
  1106. default:
  1107. for (let i = 0; i < (children as MountedVNode[]).length; i++) {
  1108. removeVNode((children as MountedVNode[])[i], container)
  1109. }
  1110. }
  1111. } else {
  1112. platformRemoveChild(container, el)
  1113. }
  1114. }
  1115. ;(vnode as any).el = null
  1116. }
  1117. function queueRemoveChildren(
  1118. children: MountedVNode[],
  1119. container: RenderNode,
  1120. refNode: RenderNode | null
  1121. ) {
  1122. queueNodeOp([removeChildren, children, container, refNode])
  1123. }
  1124. function removeChildren(
  1125. children: MountedVNode[],
  1126. container: RenderNode,
  1127. refNode: RenderNode | null
  1128. ) {
  1129. unmountArrayChildren(children)
  1130. if (refNode === null) {
  1131. platformClearContent(container)
  1132. } else {
  1133. for (let i = 0; i < children.length; i++) {
  1134. removeVNode(children[i], container)
  1135. }
  1136. }
  1137. }
  1138. // Component lifecycle -------------------------------------------------------
  1139. function mountComponentInstance(
  1140. vnode: VNode,
  1141. container: RenderNode | null,
  1142. isSVG: boolean,
  1143. endNode: RenderNode | null
  1144. ): Function {
  1145. if (__DEV__) {
  1146. pushWarningContext(vnode)
  1147. }
  1148. // a vnode may already have an instance if this is a compat call with
  1149. // new Vue()
  1150. const instance = ((__COMPAT__ && vnode.children) ||
  1151. createComponentInstance(vnode as any)) as ComponentInstance
  1152. // inject platform-specific unmount to keep-alive container
  1153. if ((vnode.tag as any)[KeepAliveSymbol] === true) {
  1154. ;(instance as any).$unmount = unmountComponentInstance
  1155. }
  1156. const {
  1157. $proxy,
  1158. $options: { beforeMount, renderTracked, renderTriggered }
  1159. } = instance
  1160. const queueUpdate = (instance.$forceUpdate = () => {
  1161. queueJob(instance._updateHandle)
  1162. })
  1163. const autorunOptions: AutorunOptions = {
  1164. scheduler: queueUpdate
  1165. }
  1166. if (__DEV__) {
  1167. if (renderTracked) {
  1168. autorunOptions.onTrack = event => {
  1169. callLifecycleHookWithHandler(
  1170. renderTracked,
  1171. $proxy,
  1172. ErrorTypes.RENDER_TRACKED,
  1173. event
  1174. )
  1175. }
  1176. }
  1177. if (renderTriggered) {
  1178. autorunOptions.onTrigger = event => {
  1179. callLifecycleHookWithHandler(
  1180. renderTriggered,
  1181. $proxy,
  1182. ErrorTypes.RENDER_TRIGGERED,
  1183. event
  1184. )
  1185. }
  1186. }
  1187. }
  1188. instance._updateHandle = autorun(() => {
  1189. if (instance._unmounted) {
  1190. return
  1191. }
  1192. if (instance._mounted) {
  1193. updateComponentInstance(instance, isSVG)
  1194. } else {
  1195. if (beforeMount) {
  1196. callLifecycleHookWithHandler(
  1197. beforeMount,
  1198. $proxy,
  1199. ErrorTypes.BEFORE_MOUNT
  1200. )
  1201. }
  1202. instance.$vnode = renderInstanceRoot(instance) as MountedVNode
  1203. queueEffect(() => {
  1204. vnode.el = instance.$vnode.el
  1205. if (__COMPAT__) {
  1206. // expose __vue__ for devtools
  1207. ;(vnode.el as any).__vue__ = instance
  1208. }
  1209. if (vnode.ref) {
  1210. vnode.ref($proxy)
  1211. }
  1212. // retrieve mounted value after initial render so that we get
  1213. // to inject effects in hooks
  1214. const { mounted } = instance.$options
  1215. if (mounted) {
  1216. callLifecycleHookWithHandler(mounted, $proxy, ErrorTypes.MOUNTED)
  1217. }
  1218. instance._mounted = true
  1219. })
  1220. mount(instance.$vnode, container, vnode as MountedVNode, isSVG, endNode)
  1221. }
  1222. }, autorunOptions)
  1223. if (__DEV__) {
  1224. popWarningContext()
  1225. }
  1226. // cleanup if mount is invalidated before committed
  1227. return () => {
  1228. teardownComponentInstance(instance)
  1229. }
  1230. }
  1231. function updateComponentInstance(
  1232. instance: ComponentInstance,
  1233. isSVG: boolean
  1234. ) {
  1235. if (__DEV__ && instance.$parentVNode) {
  1236. pushWarningContext(instance.$parentVNode as VNode)
  1237. }
  1238. const {
  1239. $vnode: prevVNode,
  1240. $parentVNode,
  1241. $proxy,
  1242. $options: { beforeUpdate }
  1243. } = instance
  1244. if (beforeUpdate) {
  1245. callLifecycleHookWithHandler(
  1246. beforeUpdate,
  1247. $proxy,
  1248. ErrorTypes.BEFORE_UPDATE,
  1249. prevVNode
  1250. )
  1251. }
  1252. const nextVNode = renderInstanceRoot(instance) as MountedVNode
  1253. queueEffect(() => {
  1254. instance.$vnode = nextVNode
  1255. const el = nextVNode.el as RenderNode
  1256. if (__COMPAT__) {
  1257. // expose __vue__ for devtools
  1258. ;(el as any).__vue__ = instance
  1259. }
  1260. // recursively update contextVNode el for nested HOCs
  1261. if ((nextVNode.flags & VNodeFlags.PORTAL) === 0) {
  1262. let vnode = $parentVNode
  1263. while (vnode !== null) {
  1264. if ((vnode.flags & VNodeFlags.COMPONENT) > 0) {
  1265. vnode.el = el
  1266. }
  1267. vnode = vnode.contextVNode
  1268. }
  1269. }
  1270. const { updated } = instance.$options
  1271. if (updated) {
  1272. callLifecycleHookWithHandler(
  1273. updated,
  1274. $proxy,
  1275. ErrorTypes.UPDATED,
  1276. nextVNode
  1277. )
  1278. }
  1279. // TODO fix me
  1280. // if (vnodeUpdatedHooks.length > 0) {
  1281. // const vnodeUpdatedHooksForCurrentInstance = vnodeUpdatedHooks.slice()
  1282. // vnodeUpdatedHooks.length = 0
  1283. // for (let i = 0; i < vnodeUpdatedHooksForCurrentInstance.length; i++) {
  1284. // vnodeUpdatedHooksForCurrentInstance[i]()
  1285. // }
  1286. // }
  1287. })
  1288. const container = platformParentNode(prevVNode.el) as RenderNode
  1289. patch(prevVNode, nextVNode, container, $parentVNode as MountedVNode, isSVG)
  1290. if (__DEV__ && instance.$parentVNode) {
  1291. popWarningContext()
  1292. }
  1293. }
  1294. function unmountComponentInstance(instance: ComponentInstance) {
  1295. if (instance._unmounted) {
  1296. return
  1297. }
  1298. const {
  1299. $vnode,
  1300. $proxy,
  1301. $options: { beforeUnmount, unmounted }
  1302. } = instance
  1303. if (beforeUnmount) {
  1304. callLifecycleHookWithHandler(
  1305. beforeUnmount,
  1306. $proxy,
  1307. ErrorTypes.BEFORE_UNMOUNT
  1308. )
  1309. }
  1310. if ($vnode) {
  1311. unmount($vnode)
  1312. }
  1313. teardownComponentInstance(instance)
  1314. instance._unmounted = true
  1315. if (unmounted) {
  1316. callLifecycleHookWithHandler(unmounted, $proxy, ErrorTypes.UNMOUNTED)
  1317. }
  1318. }
  1319. // Keep Alive ----------------------------------------------------------------
  1320. function activateComponentInstance(
  1321. vnode: VNode,
  1322. container: RenderNode | null,
  1323. endNode: RenderNode | null
  1324. ) {
  1325. if (__DEV__) {
  1326. pushWarningContext(vnode)
  1327. }
  1328. const instance = vnode.children as ComponentInstance
  1329. vnode.el = instance.$el as RenderNode
  1330. if (container != null) {
  1331. insertVNode(instance.$vnode, container, endNode)
  1332. }
  1333. if (__DEV__) {
  1334. popWarningContext()
  1335. }
  1336. queueEffect(() => {
  1337. callActivatedHook(instance, true)
  1338. })
  1339. }
  1340. function callActivatedHook(instance: ComponentInstance, asRoot: boolean) {
  1341. // 1. check if we are inside an inactive parent tree.
  1342. if (asRoot) {
  1343. instance._inactiveRoot = false
  1344. if (isInInactiveTree(instance)) return
  1345. }
  1346. if (asRoot || !instance._inactiveRoot) {
  1347. // 2. recursively call activated on child tree, depth-first
  1348. const {
  1349. $children,
  1350. $proxy,
  1351. $options: { activated }
  1352. } = instance
  1353. for (let i = 0; i < $children.length; i++) {
  1354. callActivatedHook($children[i], false)
  1355. }
  1356. if (activated) {
  1357. callLifecycleHookWithHandler(activated, $proxy, ErrorTypes.ACTIVATED)
  1358. }
  1359. }
  1360. }
  1361. function deactivateComponentInstance(instance: ComponentInstance) {
  1362. callDeactivateHook(instance, true)
  1363. }
  1364. function callDeactivateHook(instance: ComponentInstance, asRoot: boolean) {
  1365. if (asRoot) {
  1366. instance._inactiveRoot = true
  1367. if (isInInactiveTree(instance)) return
  1368. }
  1369. if (asRoot || !instance._inactiveRoot) {
  1370. // 2. recursively call deactivated on child tree, depth-first
  1371. const {
  1372. $children,
  1373. $proxy,
  1374. $options: { deactivated }
  1375. } = instance
  1376. for (let i = 0; i < $children.length; i++) {
  1377. callDeactivateHook($children[i], false)
  1378. }
  1379. if (deactivated) {
  1380. callLifecycleHookWithHandler(
  1381. deactivated,
  1382. $proxy,
  1383. ErrorTypes.DEACTIVATED
  1384. )
  1385. }
  1386. }
  1387. }
  1388. function isInInactiveTree(instance: ComponentInstance): boolean {
  1389. while ((instance = instance.$parent as any) !== null) {
  1390. if (instance._inactiveRoot) return true
  1391. }
  1392. return false
  1393. }
  1394. // TODO hydrating ------------------------------------------------------------
  1395. // API -----------------------------------------------------------------------
  1396. function render(vnode: VNode | null, container: any) {
  1397. const prevVNode = container.vnode
  1398. if (prevVNode == null) {
  1399. if (vnode) {
  1400. mount(vnode, container, null, false, null)
  1401. container.vnode = vnode
  1402. }
  1403. } else {
  1404. if (vnode) {
  1405. patch(prevVNode, vnode, container, null, false)
  1406. container.vnode = vnode
  1407. } else {
  1408. queueRemoveVNode(prevVNode, container)
  1409. container.vnode = null
  1410. }
  1411. }
  1412. if (__COMPAT__) {
  1413. flushEffects()
  1414. return vnode && vnode.flags & VNodeFlags.COMPONENT_STATEFUL
  1415. ? (vnode.children as ComponentInstance).$proxy
  1416. : null
  1417. } else {
  1418. return nextTick(() => {
  1419. return vnode && vnode.flags & VNodeFlags.COMPONENT_STATEFUL
  1420. ? (vnode.children as ComponentInstance).$proxy
  1421. : null
  1422. })
  1423. }
  1424. }
  1425. return { render }
  1426. }
  1427. // https://en.wikipedia.org/wiki/Longest_increasing_subsequence
  1428. export function lis(arr: number[]): number[] {
  1429. const p = arr.slice()
  1430. const result = [0]
  1431. let i
  1432. let j
  1433. let u
  1434. let v
  1435. let c
  1436. const len = arr.length
  1437. for (i = 0; i < len; i++) {
  1438. const arrI = arr[i]
  1439. if (arrI !== 0) {
  1440. j = result[result.length - 1]
  1441. if (arr[j] < arrI) {
  1442. p[i] = j
  1443. result.push(i)
  1444. continue
  1445. }
  1446. u = 0
  1447. v = result.length - 1
  1448. while (u < v) {
  1449. c = ((u + v) / 2) | 0
  1450. if (arr[result[c]] < arrI) {
  1451. u = c + 1
  1452. } else {
  1453. v = c
  1454. }
  1455. }
  1456. if (arrI < arr[result[u]]) {
  1457. if (u > 0) {
  1458. p[i] = result[u - 1]
  1459. }
  1460. result[u] = i
  1461. }
  1462. }
  1463. }
  1464. u = result.length
  1465. v = result[u - 1]
  1466. while (u-- > 0) {
  1467. result[u] = v
  1468. v = p[v]
  1469. }
  1470. return result
  1471. }