fix(runtime-core): disable block tracking when calling compiled slot function in tempalte expressions
fix #1745, fix #1918
This commit is contained in:
		
							parent
							
								
									c0411b3745
								
							
						
					
					
						commit
						f02e2f99d9
					
				| @ -10,7 +10,7 @@ import { | |||||||
| import { PatchFlags, SlotFlags } from '@vue/shared' | import { PatchFlags, SlotFlags } from '@vue/shared' | ||||||
| import { warn } from '../warning' | import { warn } from '../warning' | ||||||
| 
 | 
 | ||||||
| export let shouldTrackInSlotRendering = 0 | export let isRenderingCompiledSlot = 0 | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Compiler runtime helper for rendering `<slot/>` |  * Compiler runtime helper for rendering `<slot/>` | ||||||
| @ -39,7 +39,7 @@ export function renderSlot( | |||||||
|   // invocation interfering with template-based block tracking, but in
 |   // invocation interfering with template-based block tracking, but in
 | ||||||
|   // `renderSlot` we can be sure that it's template-based so we can force
 |   // `renderSlot` we can be sure that it's template-based so we can force
 | ||||||
|   // enable it.
 |   // enable it.
 | ||||||
|   shouldTrackInSlotRendering++ |   isRenderingCompiledSlot++ | ||||||
|   const rendered = (openBlock(), |   const rendered = (openBlock(), | ||||||
|   createBlock( |   createBlock( | ||||||
|     Fragment, |     Fragment, | ||||||
| @ -49,6 +49,6 @@ export function renderSlot( | |||||||
|       ? PatchFlags.STABLE_FRAGMENT |       ? PatchFlags.STABLE_FRAGMENT | ||||||
|       : PatchFlags.BAIL |       : PatchFlags.BAIL | ||||||
|   )) |   )) | ||||||
|   shouldTrackInSlotRendering-- |   isRenderingCompiledSlot-- | ||||||
|   return rendered |   return rendered | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,7 +4,8 @@ import { | |||||||
|   currentRenderingInstance |   currentRenderingInstance | ||||||
| } from '../componentRenderUtils' | } from '../componentRenderUtils' | ||||||
| import { ComponentInternalInstance } from '../component' | import { ComponentInternalInstance } from '../component' | ||||||
| import { setBlockTracking } from '../vnode' | import { isRenderingCompiledSlot } from './renderSlot' | ||||||
|  | import { closeBlock, openBlock } from '../vnode' | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Wrap a slot function to memoize current rendering instance |  * Wrap a slot function to memoize current rendering instance | ||||||
| @ -16,15 +17,19 @@ export function withCtx( | |||||||
| ) { | ) { | ||||||
|   if (!ctx) return fn |   if (!ctx) return fn | ||||||
|   return function renderFnWithContext() { |   return function renderFnWithContext() { | ||||||
|     // By default, compiled slots disables block tracking since the user may
 |     // If a user calls a compiled slot inside a template expression (#1745), it
 | ||||||
|     // call it inside a template expression (#1745). It should only track when
 |     // can mess up block tracking, so by default we need to push a null block to
 | ||||||
|     // it's called by a template `<slot>`.
 |     // avoid that. This isn't necessary if rendering a compiled `<slot>`.
 | ||||||
|     setBlockTracking(-1) |     if (!isRenderingCompiledSlot) { | ||||||
|  |       openBlock(true /* null block that disables tracking */) | ||||||
|  |     } | ||||||
|     const owner = currentRenderingInstance |     const owner = currentRenderingInstance | ||||||
|     setCurrentRenderingInstance(ctx) |     setCurrentRenderingInstance(ctx) | ||||||
|     const res = fn.apply(null, arguments as any) |     const res = fn.apply(null, arguments as any) | ||||||
|     setCurrentRenderingInstance(owner) |     setCurrentRenderingInstance(owner) | ||||||
|     setBlockTracking(1) |     if (!isRenderingCompiledSlot) { | ||||||
|  |       closeBlock() | ||||||
|  |     } | ||||||
|     return res |     return res | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -36,7 +36,6 @@ import { currentRenderingInstance } from './componentRenderUtils' | |||||||
| import { RendererNode, RendererElement } from './renderer' | import { RendererNode, RendererElement } from './renderer' | ||||||
| import { NULL_DYNAMIC_COMPONENT } from './helpers/resolveAssets' | import { NULL_DYNAMIC_COMPONENT } from './helpers/resolveAssets' | ||||||
| import { hmrDirtyComponents } from './hmr' | import { hmrDirtyComponents } from './hmr' | ||||||
| import { shouldTrackInSlotRendering } from './helpers/renderSlot' |  | ||||||
| 
 | 
 | ||||||
| export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as { | export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as { | ||||||
|   __isFragment: true |   __isFragment: true | ||||||
| @ -153,7 +152,7 @@ export interface VNode< | |||||||
| // can divide a template into nested blocks, and within each block the node
 | // can divide a template into nested blocks, and within each block the node
 | ||||||
| // structure would be stable. This allows us to skip most children diffing
 | // structure would be stable. This allows us to skip most children diffing
 | ||||||
| // and only worry about the dynamic nodes (indicated by patch flags).
 | // and only worry about the dynamic nodes (indicated by patch flags).
 | ||||||
| const blockStack: (VNode[] | null)[] = [] | export const blockStack: (VNode[] | null)[] = [] | ||||||
| let currentBlock: VNode[] | null = null | let currentBlock: VNode[] | null = null | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -176,6 +175,11 @@ export function openBlock(disableTracking = false) { | |||||||
|   blockStack.push((currentBlock = disableTracking ? null : [])) |   blockStack.push((currentBlock = disableTracking ? null : [])) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | export function closeBlock() { | ||||||
|  |   blockStack.pop() | ||||||
|  |   currentBlock = blockStack[blockStack.length - 1] || null | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Whether we should be tracking dynamic child nodes inside a block.
 | // Whether we should be tracking dynamic child nodes inside a block.
 | ||||||
| // Only tracks when this value is > 0
 | // Only tracks when this value is > 0
 | ||||||
| // We are not using a simple boolean because this value may need to be
 | // We are not using a simple boolean because this value may need to be
 | ||||||
| @ -227,8 +231,7 @@ export function createBlock( | |||||||
|   // save current block children on the block vnode
 |   // save current block children on the block vnode
 | ||||||
|   vnode.dynamicChildren = currentBlock || EMPTY_ARR |   vnode.dynamicChildren = currentBlock || EMPTY_ARR | ||||||
|   // close block
 |   // close block
 | ||||||
|   blockStack.pop() |   closeBlock() | ||||||
|   currentBlock = blockStack[blockStack.length - 1] || null |  | ||||||
|   // a block is always going to be patched, so track it as a child of its
 |   // a block is always going to be patched, so track it as a child of its
 | ||||||
|   // parent block
 |   // parent block
 | ||||||
|   if (currentBlock) { |   if (currentBlock) { | ||||||
| @ -403,7 +406,7 @@ function _createVNode( | |||||||
|   normalizeChildren(vnode, children) |   normalizeChildren(vnode, children) | ||||||
| 
 | 
 | ||||||
|   if ( |   if ( | ||||||
|     (shouldTrack > 0 || shouldTrackInSlotRendering > 0) && |     shouldTrack > 0 && | ||||||
|     // avoid a block node from tracking itself
 |     // avoid a block node from tracking itself
 | ||||||
|     !isBlockNode && |     !isBlockNode && | ||||||
|     // has current parent block
 |     // has current parent block
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user