wip(runtime): support multi-element static vnode in renderer
This commit is contained in:
		
							parent
							
								
									cb9444807e
								
							
						
					
					
						commit
						dbf627f136
					
				| @ -116,8 +116,7 @@ export interface RendererOptions< | ||||
|     parent: HostElement, | ||||
|     anchor: HostNode | null, | ||||
|     isSVG: boolean | ||||
|   ): HostElement | ||||
|   setStaticContent?(node: HostElement, content: string): void | ||||
|   ): HostElement[] | ||||
| } | ||||
| 
 | ||||
| // Renderer Node can technically be any object in the context of core renderer
 | ||||
| @ -333,8 +332,7 @@ function baseCreateRenderer( | ||||
|     nextSibling: hostNextSibling, | ||||
|     setScopeId: hostSetScopeId = NOOP, | ||||
|     cloneNode: hostCloneNode, | ||||
|     insertStaticContent: hostInsertStaticContent, | ||||
|     setStaticContent: hostSetStaticContent | ||||
|     insertStaticContent: hostInsertStaticContent | ||||
|   } = options | ||||
| 
 | ||||
|   // Note: functions inside this closure should use `const xxx = () => {}`
 | ||||
| @ -373,11 +371,7 @@ function baseCreateRenderer( | ||||
|         if (n1 == null) { | ||||
|           mountStaticNode(n2, container, anchor, isSVG) | ||||
|         } else if (__DEV__) { | ||||
|           // static nodes are only patched during dev for HMR
 | ||||
|           n2.el = n1.el | ||||
|           if (n2.children !== n1.children) { | ||||
|             hostSetStaticContent!(n2.el!, n2.children as string) | ||||
|           } | ||||
|           patchStaticNode(n1, n2, container, isSVG) | ||||
|         } | ||||
|         break | ||||
|       case Fragment: | ||||
| @ -492,11 +486,19 @@ function baseCreateRenderer( | ||||
|     isSVG: boolean | ||||
|   ) => { | ||||
|     if (n2.el && hostCloneNode !== undefined) { | ||||
|       hostInsert(hostCloneNode(n2.el), container, anchor) | ||||
|       // static node was already mounted (and reused), or adopted
 | ||||
|       // server-rendered node during hydration (in this case its children can be
 | ||||
|       // stripped by SSR optimizations). Clone the dom nodes instead.
 | ||||
|       let cur: RendererElement | null = n2.el | ||||
|       while (cur && cur !== n2.anchor) { | ||||
|         hostInsert(hostCloneNode(cur), container, anchor) | ||||
|         cur = hostNextSibling(cur) | ||||
|       } | ||||
|       hostInsert(hostCloneNode(n2.anchor!), container, anchor) | ||||
|     } else { | ||||
|       // static nodes are only present when used with compiler-dom/runtime-dom
 | ||||
|       // which guarantees presence of hostInsertStaticContent.
 | ||||
|       n2.el = hostInsertStaticContent!( | ||||
|       ;[n2.el, n2.anchor] = hostInsertStaticContent!( | ||||
|         n2.children as string, | ||||
|         container, | ||||
|         anchor, | ||||
| @ -505,6 +507,64 @@ function baseCreateRenderer( | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Dev / HMR only | ||||
|    */ | ||||
|   const patchStaticNode = ( | ||||
|     n1: VNode, | ||||
|     n2: VNode, | ||||
|     container: RendererElement, | ||||
|     isSVG: boolean | ||||
|   ) => { | ||||
|     // static nodes are only patched during dev for HMR
 | ||||
|     if (n2.children !== n1.children) { | ||||
|       const anchor = hostNextSibling(n1.anchor!) | ||||
|       // remove existing
 | ||||
|       removeStaticNode(n1) | ||||
|       // insert new
 | ||||
|       ;[n2.el, n2.anchor] = hostInsertStaticContent!( | ||||
|         n2.children as string, | ||||
|         container, | ||||
|         anchor, | ||||
|         isSVG | ||||
|       ) | ||||
|     } else { | ||||
|       n2.el = n1.el | ||||
|       n2.anchor = n1.anchor | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Dev / HMR only | ||||
|    */ | ||||
|   const moveStaticNode = ( | ||||
|     vnode: VNode, | ||||
|     container: RendererElement, | ||||
|     anchor: RendererNode | null | ||||
|   ) => { | ||||
|     let cur = vnode.el | ||||
|     const end = vnode.anchor! | ||||
|     while (cur && cur !== end) { | ||||
|       const next = hostNextSibling(cur) | ||||
|       hostInsert(cur, container, anchor) | ||||
|       cur = next | ||||
|     } | ||||
|     hostInsert(end, container, anchor) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Dev / HMR only | ||||
|    */ | ||||
|   const removeStaticNode = (vnode: VNode) => { | ||||
|     let cur = vnode.el | ||||
|     while (cur && cur !== vnode.anchor) { | ||||
|       const next = hostNextSibling(cur) | ||||
|       hostRemove(cur) | ||||
|       cur = next | ||||
|     } | ||||
|     hostRemove(vnode.anchor!) | ||||
|   } | ||||
| 
 | ||||
|   const processElement = ( | ||||
|     n1: VNode | null, | ||||
|     n2: VNode, | ||||
| @ -1456,7 +1516,7 @@ function baseCreateRenderer( | ||||
|           n1, | ||||
|           n2, | ||||
|           container, | ||||
|           parentAnchor, | ||||
|           null, | ||||
|           parentComponent, | ||||
|           parentSuspense, | ||||
|           isSVG, | ||||
| @ -1481,7 +1541,7 @@ function baseCreateRenderer( | ||||
|           n1, | ||||
|           n2, | ||||
|           container, | ||||
|           parentAnchor, | ||||
|           null, | ||||
|           parentComponent, | ||||
|           parentSuspense, | ||||
|           isSVG, | ||||
| @ -1692,6 +1752,12 @@ function baseCreateRenderer( | ||||
|       return | ||||
|     } | ||||
| 
 | ||||
|     // static node move can only happen when force updating HMR
 | ||||
|     if (__DEV__ && type === Static) { | ||||
|       moveStaticNode(vnode, container, anchor) | ||||
|       return | ||||
|     } | ||||
| 
 | ||||
|     // single nodes
 | ||||
|     const needTransition = | ||||
|       moveType !== MoveType.REORDER && | ||||
| @ -1808,6 +1874,11 @@ function baseCreateRenderer( | ||||
|       return | ||||
|     } | ||||
| 
 | ||||
|     if (__DEV__ && type === Static) { | ||||
|       removeStaticNode(vnode) | ||||
|       return | ||||
|     } | ||||
| 
 | ||||
|     const performRemove = () => { | ||||
|       hostRemove(el!) | ||||
|       if (transition && !transition.persisted && transition.afterLeave) { | ||||
|  | ||||
| @ -127,6 +127,7 @@ export interface VNode<HostNode = RendererNode, HostElement = RendererElement> { | ||||
|   anchor: HostNode | null // fragment anchor
 | ||||
|   target: HostElement | null // teleport target
 | ||||
|   targetAnchor: HostNode | null // teleport target anchor
 | ||||
|   staticCount: number // number of elements contained in a static vnode
 | ||||
| 
 | ||||
|   // optimization only
 | ||||
|   shapeFlag: number | ||||
| @ -368,6 +369,7 @@ function _createVNode( | ||||
|     anchor: null, | ||||
|     target: null, | ||||
|     targetAnchor: null, | ||||
|     staticCount: 0, | ||||
|     shapeFlag, | ||||
|     patchFlag, | ||||
|     dynamicProps, | ||||
| @ -422,6 +424,7 @@ export function cloneVNode<T, U>( | ||||
|     children: vnode.children, | ||||
|     target: vnode.target, | ||||
|     targetAnchor: vnode.targetAnchor, | ||||
|     staticCount: vnode.staticCount, | ||||
|     shapeFlag: vnode.shapeFlag, | ||||
|     // if the vnode is cloned with extra props, we can no longer assume its
 | ||||
|     // existing patch flag to be reliable and need to bail out of optimized mode.
 | ||||
| @ -459,8 +462,15 @@ export function createTextVNode(text: string = ' ', flag: number = 0): VNode { | ||||
| /** | ||||
|  * @internal | ||||
|  */ | ||||
| export function createStaticVNode(content: string): VNode { | ||||
|   return createVNode(Static, null, content) | ||||
| export function createStaticVNode( | ||||
|   content: string, | ||||
|   numberOfNodes: number | ||||
| ): VNode { | ||||
|   // A static vnode can contain multiple stringified elements, and the number
 | ||||
|   // of elements is necessary for hydration.
 | ||||
|   const vnode = createVNode(Static, null, content) | ||||
|   vnode.staticCount = numberOfNodes | ||||
|   return vnode | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  | ||||
| @ -64,17 +64,14 @@ export const nodeOps: Omit<RendererOptions<Node, Element>, 'patchProp'> = { | ||||
|         (tempSVGContainer = doc.createElementNS(svgNS, 'svg')) | ||||
|       : tempContainer || (tempContainer = doc.createElement('div')) | ||||
|     temp.innerHTML = content | ||||
|     const node = temp.children[0] | ||||
|     nodeOps.insert(node, parent, anchor) | ||||
|     return node | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| if (__DEV__) { | ||||
|   // __UNSAFE__
 | ||||
|   // Reason: innerHTML.
 | ||||
|   // same as `insertStaticContent`, but this is also dev only (for HMR).
 | ||||
|   nodeOps.setStaticContent = (el, content) => { | ||||
|     el.innerHTML = content | ||||
|     const first = temp.firstChild as Element | ||||
|     let node: Element | null = first | ||||
|     let last: Element = node | ||||
|     while (node) { | ||||
|       last = node | ||||
|       nodeOps.insert(node, parent, anchor) | ||||
|       node = temp.firstChild as Element | ||||
|     } | ||||
|     return [first, last] | ||||
|   } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user