From b3b65b40582d7fbdc776bfe8a1542b80aebe0aac Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 28 Jul 2020 11:11:26 -0400 Subject: [PATCH 1/5] fix: runtime compilation marker should be applied in exposed compile function --- packages/runtime-core/src/component.ts | 2 -- packages/vue/src/index.ts | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 58e1725e8e9..c0ea6ab3f3d 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -601,8 +601,6 @@ function finishComponentSetup( if (__DEV__) { endMeasure(instance, `compile`) } - // mark the function as runtime compiled - ;(Component.render as InternalRenderFunction)._rc = true } instance.render = (Component.render || NOOP) as InternalRenderFunction diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index a9ccf894ef9..c810d03eb92 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -5,6 +5,7 @@ import { compile, CompilerOptions, CompilerError } from '@vue/compiler-dom' import { registerRuntimeCompiler, RenderFunction, warn } from '@vue/runtime-dom' import * as runtimeDom from '@vue/runtime-dom' import { isString, NOOP, generateCodeFrame, extend } from '@vue/shared' +import { InternalRenderFunction } from 'packages/runtime-core/src/component' __DEV__ && initDev() @@ -74,6 +75,10 @@ function compileToFunction( const render = (__GLOBAL__ ? new Function(code)() : new Function('Vue', code)(runtimeDom)) as RenderFunction + + // mark the function as runtime compiled + ;(render as InternalRenderFunction)._rc = true + return (compileCache[key] = render) } From fbf865d9d4744a0233db1ed6e5543b8f3ef51e8d Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 28 Jul 2020 11:12:43 -0400 Subject: [PATCH 2/5] fix(compiler-core): always compile Teleport and Suspense as blocks --- packages/compiler-core/src/transforms/transformElement.ts | 5 ++++- packages/runtime-core/src/vnode.ts | 6 +----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts index 7b34a79827b..3bb9a83fd20 100644 --- a/packages/compiler-core/src/transforms/transformElement.ts +++ b/packages/compiler-core/src/transforms/transformElement.ts @@ -35,7 +35,8 @@ import { MERGE_PROPS, TO_HANDLERS, TELEPORT, - KEEP_ALIVE + KEEP_ALIVE, + SUSPENSE } from '../runtimeHelpers' import { getInnerRange, @@ -89,6 +90,8 @@ export const transformElement: NodeTransform = (node, context) => { let shouldUseBlock = // dynamic component may resolve to plain elements isDynamicComponent || + vnodeTag === TELEPORT || + vnodeTag === SUSPENSE || (!isComponent && // and must be forced into blocks so that block // updates inside get proper isSVG flag at runtime. (#639, #643) diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index 07dafcebac1..353ed863b05 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -411,11 +411,7 @@ function _createVNode( // the EVENTS flag is only for hydration and if it is the only flag, the // vnode should not be considered dynamic due to handler caching. patchFlag !== PatchFlags.HYDRATE_EVENTS && - (patchFlag > 0 || - shapeFlag & ShapeFlags.SUSPENSE || - shapeFlag & ShapeFlags.TELEPORT || - shapeFlag & ShapeFlags.STATEFUL_COMPONENT || - shapeFlag & ShapeFlags.FUNCTIONAL_COMPONENT) + (patchFlag > 0 || shapeFlag & ShapeFlags.COMPONENT) ) { currentBlock.push(vnode) } From 151c413f9208c2d0ec86cf22342c70742b8615e9 Mon Sep 17 00:00:00 2001 From: likui <2218301630@qq.com> Date: Sat, 25 Jul 2020 10:40:43 +0800 Subject: [PATCH 3/5] fix(runtime-core): use correct container for patch `Teleport` inside block tree fix #1690 --- packages/runtime-core/src/renderer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 09f2ab242b4..df26f87ac71 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -960,7 +960,8 @@ function baseCreateRenderer( // which also requires the correct parent container !isSameVNodeType(oldVNode, newVNode) || // - In the case of a component, it could contain anything. - oldVNode.shapeFlag & ShapeFlags.COMPONENT + oldVNode.shapeFlag & ShapeFlags.COMPONENT || + oldVNode.shapeFlag & ShapeFlags.TELEPORT ? hostParentNode(oldVNode.el!)! : // In other cases, the parent container is not actually used so we // just pass the block element here to avoid a DOM parentNode call. From a0ae1fad5616a891863cf022ad5d56a3e6051b34 Mon Sep 17 00:00:00 2001 From: likui <2218301630@qq.com> Date: Sat, 25 Jul 2020 20:41:24 +0800 Subject: [PATCH 4/5] fix: add test --- .../__tests__/components/Teleport.spec.ts | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/__tests__/components/Teleport.spec.ts b/packages/runtime-core/__tests__/components/Teleport.spec.ts index 99bea073072..c3d02244c8f 100644 --- a/packages/runtime-core/__tests__/components/Teleport.spec.ts +++ b/packages/runtime-core/__tests__/components/Teleport.spec.ts @@ -6,9 +6,12 @@ import { Teleport, Text, ref, - nextTick + nextTick, + openBlock, + createBlock } from '@vue/runtime-test' import { createVNode, Fragment } from '../../src/vnode' +import { PatchFlags } from '@vue/shared' describe('renderer: teleport', () => { test('should work', () => { @@ -299,4 +302,55 @@ describe('renderer: teleport', () => { ) expect(serializeInner(target)).toBe('') }) + + test('should work with block tree', async () => { + const target = nodeOps.createElement('div') + const root = nodeOps.createElement('div') + const disabled = ref(false) + + const App = { + render() { + return ( + openBlock(), + createBlock( + Fragment, + null, + [ + h( + Teleport, + { to: target, disabled: disabled.value }, + h('div', 'teleported') + ), + h('div', 'root') + ], + PatchFlags.STABLE_FRAGMENT + ) + ) + } + } + render(h(App), root) + expect(serializeInner(root)).toMatchInlineSnapshot( + `"
root
"` + ) + expect(serializeInner(target)).toMatchInlineSnapshot( + `"
teleported
"` + ) + + disabled.value = true + await nextTick() + expect(serializeInner(root)).toMatchInlineSnapshot( + `"
teleported
root
"` + ) + expect(serializeInner(target)).toBe(``) + + // toggle back + disabled.value = false + await nextTick() + expect(serializeInner(root)).toMatchInlineSnapshot( + `"
root
"` + ) + expect(serializeInner(target)).toMatchInlineSnapshot( + `"
teleported
"` + ) + }) }) From 15c7e5a12edf9c830a6fb8a15bcd76906aca47c8 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 28 Jul 2020 11:14:06 -0400 Subject: [PATCH 5/5] chore: fix test --- .../__tests__/components/Teleport.spec.ts | 41 ++++++++----------- .../runtime-core/src/components/Teleport.ts | 7 ++++ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/runtime-core/__tests__/components/Teleport.spec.ts b/packages/runtime-core/__tests__/components/Teleport.spec.ts index c3d02244c8f..eaa6e227ee3 100644 --- a/packages/runtime-core/__tests__/components/Teleport.spec.ts +++ b/packages/runtime-core/__tests__/components/Teleport.spec.ts @@ -7,11 +7,10 @@ import { Text, ref, nextTick, - openBlock, - createBlock + markRaw } from '@vue/runtime-test' import { createVNode, Fragment } from '../../src/vnode' -import { PatchFlags } from '@vue/shared' +import { compile } from 'vue' describe('renderer: teleport', () => { test('should work', () => { @@ -309,37 +308,31 @@ describe('renderer: teleport', () => { const disabled = ref(false) const App = { - render() { - return ( - openBlock(), - createBlock( - Fragment, - null, - [ - h( - Teleport, - { to: target, disabled: disabled.value }, - h('div', 'teleported') - ), - h('div', 'root') - ], - PatchFlags.STABLE_FRAGMENT - ) - ) - } + setup() { + return { + target: markRaw(target), + disabled + } + }, + render: compile(` + +
teleported
{{ disabled }} +
+
root
+ `) } render(h(App), root) expect(serializeInner(root)).toMatchInlineSnapshot( `"
root
"` ) expect(serializeInner(target)).toMatchInlineSnapshot( - `"
teleported
"` + `"
teleported
false"` ) disabled.value = true await nextTick() expect(serializeInner(root)).toMatchInlineSnapshot( - `"
teleported
root
"` + `"
teleported
true
root
"` ) expect(serializeInner(target)).toBe(``) @@ -350,7 +343,7 @@ describe('renderer: teleport', () => { `"
root
"` ) expect(serializeInner(target)).toMatchInlineSnapshot( - `"
teleported
"` + `"
teleported
false"` ) }) }) diff --git a/packages/runtime-core/src/components/Teleport.ts b/packages/runtime-core/src/components/Teleport.ts index a4fdd4b505f..8c588df128b 100644 --- a/packages/runtime-core/src/components/Teleport.ts +++ b/packages/runtime-core/src/components/Teleport.ts @@ -139,6 +139,13 @@ export const TeleportImpl = { parentSuspense, isSVG ) + if (n2.patchFlag > 0 && n2.shapeFlag & ShapeFlags.ARRAY_CHILDREN) { + const oldChildren = n1.children as VNode[] + const children = n2.children as VNode[] + for (let i = 0; i < children.length; i++) { + children[i].el = oldChildren[i].el + } + } } else if (!optimized) { patchChildren( n1,