From eee50956924d7d2c916cdb8b99043da616e53af5 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 31 Mar 2020 10:52:42 -0400 Subject: [PATCH] refactor: rename `` to `` BREAKING CHANGE: `` has been renamed to ``. `target` prop is also renmaed to `to`, so the new usage will be: ```html ``` The primary reason for the renaming is to avoid potential naming conflict with [native portals](https://wicg.github.io/portals/). --- .../transforms/transformElement.spec.ts | 12 +- packages/compiler-core/src/runtimeHelpers.ts | 4 +- .../src/transforms/transformElement.ts | 10 +- packages/compiler-core/src/transforms/vIf.ts | 6 +- packages/compiler-core/src/utils.ts | 6 +- .../compiler-ssr/__tests__/ssrPortal.spec.ts | 24 ++-- packages/compiler-ssr/src/errors.ts | 4 +- packages/compiler-ssr/src/runtimeHelpers.ts | 4 +- .../src/transforms/ssrTransformComponent.ts | 8 +- ...sformPortal.ts => ssrTransformTeleport.ts} | 10 +- .../__tests__/components/Suspense.spec.ts | 2 +- .../{Portal.spec.ts => Teleport.spec.ts} | 82 ++++++------- .../runtime-core/__tests__/hydration.spec.ts | 116 +++++++++--------- packages/runtime-core/src/apiOptions.ts | 2 +- .../src/components/{Portal.ts => Teleport.ts} | 105 ++++++++-------- packages/runtime-core/src/h.ts | 10 +- packages/runtime-core/src/hydration.ts | 6 +- packages/runtime-core/src/index.ts | 2 +- packages/runtime-core/src/renderer.ts | 18 +-- packages/runtime-core/src/vnode.ts | 18 +-- ...{ssrPortal.spec.ts => ssrTeleport.spec.ts} | 58 +++++---- ...srRenderPortal.ts => ssrRenderTeleport.ts} | 24 ++-- packages/server-renderer/src/index.ts | 2 +- .../server-renderer/src/renderToString.ts | 34 ++--- packages/shared/src/shapeFlags.ts | 2 +- packages/vue/examples/transition/modal.html | 4 +- 26 files changed, 290 insertions(+), 283 deletions(-) rename packages/compiler-ssr/src/transforms/{ssrTransformPortal.ts => ssrTransformTeleport.ts} (83%) rename packages/runtime-core/__tests__/components/{Portal.spec.ts => Teleport.spec.ts} (68%) rename packages/runtime-core/src/components/{Portal.ts => Teleport.ts} (73%) rename packages/server-renderer/__tests__/{ssrPortal.spec.ts => ssrTeleport.spec.ts} (51%) rename packages/server-renderer/src/helpers/{ssrRenderPortal.ts => ssrRenderTeleport.ts} (54%) diff --git a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts index 6434b51c..14cbb39e 100644 --- a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts @@ -11,7 +11,7 @@ import { RESOLVE_DIRECTIVE, TO_HANDLERS, helperNameMap, - PORTAL, + TELEPORT, RESOLVE_DYNAMIC_COMPONENT, SUSPENSE, KEEP_ALIVE, @@ -272,16 +272,16 @@ describe('compiler: element transform', () => { }) }) - test('should handle with normal children', () => { + test('should handle with normal children', () => { function assert(tag: string) { const { root, node } = parseWithElementTransform( `<${tag} target="#foo">` ) expect(root.components.length).toBe(0) - expect(root.helpers).toContain(PORTAL) + expect(root.helpers).toContain(TELEPORT) expect(node).toMatchObject({ - tag: PORTAL, + tag: TELEPORT, props: createObjectMatcher({ target: '#foo' }), @@ -298,8 +298,8 @@ describe('compiler: element transform', () => { }) } - assert(`portal`) - assert(`Portal`) + assert(`teleport`) + assert(`Teleport`) }) test('should handle ', () => { diff --git a/packages/compiler-core/src/runtimeHelpers.ts b/packages/compiler-core/src/runtimeHelpers.ts index 39409a6b..85776224 100644 --- a/packages/compiler-core/src/runtimeHelpers.ts +++ b/packages/compiler-core/src/runtimeHelpers.ts @@ -1,5 +1,5 @@ export const FRAGMENT = Symbol(__DEV__ ? `Fragment` : ``) -export const PORTAL = Symbol(__DEV__ ? `Portal` : ``) +export const TELEPORT = Symbol(__DEV__ ? `Teleport` : ``) export const SUSPENSE = Symbol(__DEV__ ? `Suspense` : ``) export const KEEP_ALIVE = Symbol(__DEV__ ? `KeepAlive` : ``) export const BASE_TRANSITION = Symbol(__DEV__ ? `BaseTransition` : ``) @@ -33,7 +33,7 @@ export const WITH_CTX = Symbol(__DEV__ ? `withCtx` : ``) // Using `any` here because TS doesn't allow symbols as index type. export const helperNameMap: any = { [FRAGMENT]: `Fragment`, - [PORTAL]: `Portal`, + [TELEPORT]: `Teleport`, [SUSPENSE]: `Suspense`, [KEEP_ALIVE]: `KeepAlive`, [BASE_TRANSITION]: `BaseTransition`, diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts index ad7a119c..8e2a7ac6 100644 --- a/packages/compiler-core/src/transforms/transformElement.ts +++ b/packages/compiler-core/src/transforms/transformElement.ts @@ -28,7 +28,7 @@ import { RESOLVE_DYNAMIC_COMPONENT, MERGE_PROPS, TO_HANDLERS, - PORTAL, + TELEPORT, KEEP_ALIVE } from '../runtimeHelpers' import { @@ -124,8 +124,8 @@ export const transformElement: NodeTransform = (node, context) => { const shouldBuildAsSlots = isComponent && - // Portal is not a real component and has dedicated runtime handling - vnodeTag !== PORTAL && + // Teleport is not a real component and has dedicated runtime handling + vnodeTag !== TELEPORT && // explained above. vnodeTag !== KEEP_ALIVE @@ -135,7 +135,7 @@ export const transformElement: NodeTransform = (node, context) => { if (hasDynamicSlots) { patchFlag |= PatchFlags.DYNAMIC_SLOTS } - } else if (node.children.length === 1 && vnodeTag !== PORTAL) { + } else if (node.children.length === 1 && vnodeTag !== TELEPORT) { const child = node.children[0] const type = child.type // check for dynamic text children @@ -217,7 +217,7 @@ export function resolveComponentType( } } - // 2. built-in components (Portal, Transition, KeepAlive, Suspense...) + // 2. built-in components (Teleport, Transition, KeepAlive, Suspense...) const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag) if (builtIn) { // built-ins are simply fallthroughs / have special handling during ssr diff --git a/packages/compiler-core/src/transforms/vIf.ts b/packages/compiler-core/src/transforms/vIf.ts index ac60c4cd..61095588 100644 --- a/packages/compiler-core/src/transforms/vIf.ts +++ b/packages/compiler-core/src/transforms/vIf.ts @@ -27,7 +27,7 @@ import { FRAGMENT, CREATE_COMMENT, OPEN_BLOCK, - PORTAL + TELEPORT } from '../runtimeHelpers' import { injectProp } from '../utils' import { PatchFlags, PatchFlagNames } from '@vue/shared' @@ -218,8 +218,8 @@ function createChildrenCodegenNode( // component vnodes are always tracked and its children are // compiled into slots so no need to make it a block ((firstChild as ElementNode).tagType !== ElementTypes.COMPONENT || - // portal has component type but isn't always tracked - vnodeCall.tag === PORTAL) + // teleport has component type but isn't always tracked + vnodeCall.tag === TELEPORT) ) { vnodeCall.isBlock = true helper(OPEN_BLOCK) diff --git a/packages/compiler-core/src/utils.ts b/packages/compiler-core/src/utils.ts index 12e16c59..3555e9a3 100644 --- a/packages/compiler-core/src/utils.ts +++ b/packages/compiler-core/src/utils.ts @@ -25,7 +25,7 @@ import { import { TransformContext } from './transform' import { MERGE_PROPS, - PORTAL, + TELEPORT, SUSPENSE, KEEP_ALIVE, BASE_TRANSITION @@ -38,8 +38,8 @@ export const isBuiltInType = (tag: string, expected: string): boolean => tag === expected || tag === hyphenate(expected) export function isCoreComponent(tag: string): symbol | void { - if (isBuiltInType(tag, 'Portal')) { - return PORTAL + if (isBuiltInType(tag, 'Teleport')) { + return TELEPORT } else if (isBuiltInType(tag, 'Suspense')) { return SUSPENSE } else if (isBuiltInType(tag, 'KeepAlive')) { diff --git a/packages/compiler-ssr/__tests__/ssrPortal.spec.ts b/packages/compiler-ssr/__tests__/ssrPortal.spec.ts index 7f0f448a..36d6138d 100644 --- a/packages/compiler-ssr/__tests__/ssrPortal.spec.ts +++ b/packages/compiler-ssr/__tests__/ssrPortal.spec.ts @@ -1,13 +1,13 @@ import { compile } from '../src' -describe('ssr compile: portal', () => { +describe('ssr compile: teleport', () => { test('should work', () => { - expect(compile(`
`).code) + expect(compile(`
`).code) .toMatchInlineSnapshot(` - "const { ssrRenderPortal: _ssrRenderPortal } = require(\\"@vue/server-renderer\\") + "const { ssrRenderTeleport: _ssrRenderTeleport } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { - _ssrRenderPortal(_push, (_push) => { + _ssrRenderTeleport(_push, (_push) => { _push(\`
\`) }, _ctx.target, false, _parent) }" @@ -15,24 +15,26 @@ describe('ssr compile: portal', () => { }) test('disabled prop handling', () => { - expect(compile(`
`).code) - .toMatchInlineSnapshot(` - "const { ssrRenderPortal: _ssrRenderPortal } = require(\\"@vue/server-renderer\\") + expect( + compile(`
`).code + ).toMatchInlineSnapshot(` + "const { ssrRenderTeleport: _ssrRenderTeleport } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { - _ssrRenderPortal(_push, (_push) => { + _ssrRenderTeleport(_push, (_push) => { _push(\`
\`) }, _ctx.target, true, _parent) }" `) expect( - compile(`
`).code + compile(`
`) + .code ).toMatchInlineSnapshot(` - "const { ssrRenderPortal: _ssrRenderPortal } = require(\\"@vue/server-renderer\\") + "const { ssrRenderTeleport: _ssrRenderTeleport } = require(\\"@vue/server-renderer\\") return function ssrRender(_ctx, _push, _parent) { - _ssrRenderPortal(_push, (_push) => { + _ssrRenderTeleport(_push, (_push) => { _push(\`
\`) }, _ctx.target, _ctx.foo, _parent) }" diff --git a/packages/compiler-ssr/src/errors.ts b/packages/compiler-ssr/src/errors.ts index fdc99160..282c918f 100644 --- a/packages/compiler-ssr/src/errors.ts +++ b/packages/compiler-ssr/src/errors.ts @@ -19,11 +19,11 @@ export function createSSRCompilerError( export const enum SSRErrorCodes { X_SSR_CUSTOM_DIRECTIVE_NO_TRANSFORM = DOMErrorCodes.__EXTEND_POINT__, X_SSR_UNSAFE_ATTR_NAME, - X_SSR_NO_PORTAL_TARGET + X_SSR_NO_TELEPORT_TARGET } export const SSRErrorMessages: { [code: number]: string } = { [SSRErrorCodes.X_SSR_CUSTOM_DIRECTIVE_NO_TRANSFORM]: `Custom directive is missing corresponding SSR transform and will be ignored.`, [SSRErrorCodes.X_SSR_UNSAFE_ATTR_NAME]: `Unsafe attribute name for SSR.`, - [SSRErrorCodes.X_SSR_NO_PORTAL_TARGET]: `No target prop on portal element.` + [SSRErrorCodes.X_SSR_NO_TELEPORT_TARGET]: `No target prop on teleport element.` } diff --git a/packages/compiler-ssr/src/runtimeHelpers.ts b/packages/compiler-ssr/src/runtimeHelpers.ts index bfd83708..c2732d19 100644 --- a/packages/compiler-ssr/src/runtimeHelpers.ts +++ b/packages/compiler-ssr/src/runtimeHelpers.ts @@ -13,7 +13,7 @@ export const SSR_LOOSE_EQUAL = Symbol(`ssrLooseEqual`) export const SSR_LOOSE_CONTAIN = Symbol(`ssrLooseContain`) export const SSR_RENDER_DYNAMIC_MODEL = Symbol(`ssrRenderDynamicModel`) export const SSR_GET_DYNAMIC_MODEL_PROPS = Symbol(`ssrGetDynamicModelProps`) -export const SSR_RENDER_PORTAL = Symbol(`ssrRenderPortal`) +export const SSR_RENDER_TELEPORT = Symbol(`ssrRenderTeleport`) export const SSR_RENDER_SUSPENSE = Symbol(`ssrRenderSuspense`) export const ssrHelpers = { @@ -30,7 +30,7 @@ export const ssrHelpers = { [SSR_LOOSE_CONTAIN]: `ssrLooseContain`, [SSR_RENDER_DYNAMIC_MODEL]: `ssrRenderDynamicModel`, [SSR_GET_DYNAMIC_MODEL_PROPS]: `ssrGetDynamicModelProps`, - [SSR_RENDER_PORTAL]: `ssrRenderPortal`, + [SSR_RENDER_TELEPORT]: `ssrRenderTeleport`, [SSR_RENDER_SUSPENSE]: `ssrRenderSuspense` } diff --git a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts index ff4ea8fa..95eace9a 100644 --- a/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts +++ b/packages/compiler-ssr/src/transforms/ssrTransformComponent.ts @@ -11,7 +11,7 @@ import { buildSlots, FunctionExpression, TemplateChildNode, - PORTAL, + TELEPORT, createIfStatement, createSimpleExpression, getBaseTransformPreset, @@ -39,7 +39,7 @@ import { processChildren, processChildrenAsStatement } from '../ssrCodegenTransform' -import { ssrProcessPortal } from './ssrTransformPortal' +import { ssrProcessTeleport } from './ssrTransformTeleport' import { ssrProcessSuspense, ssrTransformSuspense @@ -146,8 +146,8 @@ export function ssrProcessComponent( if (!node.ssrCodegenNode) { // this is a built-in component that fell-through. const component = componentTypeMap.get(node)! - if (component === PORTAL) { - return ssrProcessPortal(node, context) + if (component === TELEPORT) { + return ssrProcessTeleport(node, context) } else if (component === SUSPENSE) { return ssrProcessSuspense(node, context) } else { diff --git a/packages/compiler-ssr/src/transforms/ssrTransformPortal.ts b/packages/compiler-ssr/src/transforms/ssrTransformTeleport.ts similarity index 83% rename from packages/compiler-ssr/src/transforms/ssrTransformPortal.ts rename to packages/compiler-ssr/src/transforms/ssrTransformTeleport.ts index e67a5852..5a39688d 100644 --- a/packages/compiler-ssr/src/transforms/ssrTransformPortal.ts +++ b/packages/compiler-ssr/src/transforms/ssrTransformTeleport.ts @@ -12,17 +12,17 @@ import { processChildrenAsStatement } from '../ssrCodegenTransform' import { createSSRCompilerError, SSRErrorCodes } from '../errors' -import { SSR_RENDER_PORTAL } from '../runtimeHelpers' +import { SSR_RENDER_TELEPORT } from '../runtimeHelpers' // Note: this is a 2nd-pass codegen transform. -export function ssrProcessPortal( +export function ssrProcessTeleport( node: ComponentNode, context: SSRTransformContext ) { const targetProp = findProp(node, 'target') if (!targetProp) { context.onError( - createSSRCompilerError(SSRErrorCodes.X_SSR_NO_PORTAL_TARGET, node.loc) + createSSRCompilerError(SSRErrorCodes.X_SSR_NO_TELEPORT_TARGET, node.loc) ) return } @@ -37,7 +37,7 @@ export function ssrProcessPortal( if (!target) { context.onError( createSSRCompilerError( - SSRErrorCodes.X_SSR_NO_PORTAL_TARGET, + SSRErrorCodes.X_SSR_NO_TELEPORT_TARGET, targetProp.loc ) ) @@ -60,7 +60,7 @@ export function ssrProcessPortal( ) contentRenderFn.body = processChildrenAsStatement(node.children, context) context.pushStatement( - createCallExpression(context.helper(SSR_RENDER_PORTAL), [ + createCallExpression(context.helper(SSR_RENDER_TELEPORT), [ `_push`, contentRenderFn, target, diff --git a/packages/runtime-core/__tests__/components/Suspense.spec.ts b/packages/runtime-core/__tests__/components/Suspense.spec.ts index 994bd71a..4738c80e 100644 --- a/packages/runtime-core/__tests__/components/Suspense.spec.ts +++ b/packages/runtime-core/__tests__/components/Suspense.spec.ts @@ -731,5 +731,5 @@ describe('Suspense', () => { expect(serializeInner(root)).toBe(`
Child A
Child B
`) }) - test.todo('portal inside suspense') + test.todo('teleport inside suspense') }) diff --git a/packages/runtime-core/__tests__/components/Portal.spec.ts b/packages/runtime-core/__tests__/components/Teleport.spec.ts similarity index 68% rename from packages/runtime-core/__tests__/components/Portal.spec.ts rename to packages/runtime-core/__tests__/components/Teleport.spec.ts index 651c8739..99bea073 100644 --- a/packages/runtime-core/__tests__/components/Portal.spec.ts +++ b/packages/runtime-core/__tests__/components/Teleport.spec.ts @@ -3,28 +3,28 @@ import { serializeInner, render, h, - Portal, + Teleport, Text, ref, nextTick } from '@vue/runtime-test' import { createVNode, Fragment } from '../../src/vnode' -describe('renderer: portal', () => { +describe('renderer: teleport', () => { test('should work', () => { const target = nodeOps.createElement('div') const root = nodeOps.createElement('div') render( h(() => [ - h(Portal, { target }, h('div', 'teleported')), + h(Teleport, { to: target }, h('div', 'teleported')), h('div', 'root') ]), root ) expect(serializeInner(root)).toMatchInlineSnapshot( - `"
root
"` + `"
root
"` ) expect(serializeInner(target)).toMatchInlineSnapshot( `"
teleported
"` @@ -39,14 +39,14 @@ describe('renderer: portal', () => { render( h(() => [ - h(Portal, { target: target.value }, h('div', 'teleported')), + h(Teleport, { to: target.value }, h('div', 'teleported')), h('div', 'root') ]), root ) expect(serializeInner(root)).toMatchInlineSnapshot( - `"
root
"` + `"
root
"` ) expect(serializeInner(targetA)).toMatchInlineSnapshot( `"
teleported
"` @@ -57,7 +57,7 @@ describe('renderer: portal', () => { await nextTick() expect(serializeInner(root)).toMatchInlineSnapshot( - `"
root
"` + `"
root
"` ) expect(serializeInner(targetA)).toMatchInlineSnapshot(`""`) expect(serializeInner(targetB)).toMatchInlineSnapshot( @@ -70,7 +70,7 @@ describe('renderer: portal', () => { const root = nodeOps.createElement('div') const children = ref([h('div', 'teleported')]) - render(h(Portal, { target }, children.value), root) + render(h(Teleport, { to: target }, children.value), root) expect(serializeInner(target)).toMatchInlineSnapshot( `"
teleported
"` ) @@ -96,7 +96,7 @@ describe('renderer: portal', () => { render( h(() => [ - h(Portal, { target }, h('div', 'teleported')), + h(Teleport, { to: target }, h('div', 'teleported')), h('div', 'root') ]), root @@ -109,28 +109,28 @@ describe('renderer: portal', () => { expect(serializeInner(target)).toBe('') }) - test('multiple portal with same target', () => { + test('multiple teleport with same target', () => { const target = nodeOps.createElement('div') const root = nodeOps.createElement('div') render( h('div', [ - h(Portal, { target }, h('div', 'one')), - h(Portal, { target }, 'two') + h(Teleport, { to: target }, h('div', 'one')), + h(Teleport, { to: target }, 'two') ]), root ) expect(serializeInner(root)).toMatchInlineSnapshot( - `"
"` + `"
"` ) expect(serializeInner(target)).toMatchInlineSnapshot(`"
one
two"`) // update existing content render( h('div', [ - h(Portal, { target }, [h('div', 'one'), h('div', 'two')]), - h(Portal, { target }, 'three') + h(Teleport, { to: target }, [h('div', 'one'), h('div', 'two')]), + h(Teleport, { to: target }, 'three') ]), root ) @@ -139,38 +139,38 @@ describe('renderer: portal', () => { ) // toggling - render(h('div', [null, h(Portal, { target }, 'three')]), root) + render(h('div', [null, h(Teleport, { to: target }, 'three')]), root) expect(serializeInner(root)).toMatchInlineSnapshot( - `"
"` + `"
"` ) expect(serializeInner(target)).toMatchInlineSnapshot(`"three"`) // toggle back render( h('div', [ - h(Portal, { target }, [h('div', 'one'), h('div', 'two')]), - h(Portal, { target }, 'three') + h(Teleport, { to: target }, [h('div', 'one'), h('div', 'two')]), + h(Teleport, { to: target }, 'three') ]), root ) expect(serializeInner(root)).toMatchInlineSnapshot( - `"
"` + `"
"` ) // should append expect(serializeInner(target)).toMatchInlineSnapshot( `"three
one
two
"` ) - // toggle the other portal + // toggle the other teleport render( h('div', [ - h(Portal, { target }, [h('div', 'one'), h('div', 'two')]), + h(Teleport, { to: target }, [h('div', 'one'), h('div', 'two')]), null ]), root ) expect(serializeInner(root)).toMatchInlineSnapshot( - `"
"` + `"
"` ) expect(serializeInner(target)).toMatchInlineSnapshot( `"
one
two
"` @@ -183,14 +183,14 @@ describe('renderer: portal', () => { const renderWithDisabled = (disabled: boolean) => { return h(Fragment, [ - h(Portal, { target, disabled }, h('div', 'teleported')), + h(Teleport, { to: target, disabled }, h('div', 'teleported')), h('div', 'root') ]) } render(renderWithDisabled(false), root) expect(serializeInner(root)).toMatchInlineSnapshot( - `"
root
"` + `"
root
"` ) expect(serializeInner(target)).toMatchInlineSnapshot( `"
teleported
"` @@ -198,33 +198,33 @@ describe('renderer: portal', () => { render(renderWithDisabled(true), root) expect(serializeInner(root)).toMatchInlineSnapshot( - `"
teleported
root
"` + `"
teleported
root
"` ) expect(serializeInner(target)).toBe(``) // toggle back render(renderWithDisabled(false), root) expect(serializeInner(root)).toMatchInlineSnapshot( - `"
root
"` + `"
root
"` ) expect(serializeInner(target)).toMatchInlineSnapshot( `"
teleported
"` ) }) - test('moving portal while enabled', () => { + test('moving teleport while enabled', () => { const target = nodeOps.createElement('div') const root = nodeOps.createElement('div') render( h(Fragment, [ - h(Portal, { target }, h('div', 'teleported')), + h(Teleport, { to: target }, h('div', 'teleported')), h('div', 'root') ]), root ) expect(serializeInner(root)).toMatchInlineSnapshot( - `"
root
"` + `"
root
"` ) expect(serializeInner(target)).toMatchInlineSnapshot( `"
teleported
"` @@ -233,12 +233,12 @@ describe('renderer: portal', () => { render( h(Fragment, [ h('div', 'root'), - h(Portal, { target }, h('div', 'teleported')) + h(Teleport, { to: target }, h('div', 'teleported')) ]), root ) expect(serializeInner(root)).toMatchInlineSnapshot( - `"
root
"` + `"
root
"` ) expect(serializeInner(target)).toMatchInlineSnapshot( `"
teleported
"` @@ -246,56 +246,56 @@ describe('renderer: portal', () => { render( h(Fragment, [ - h(Portal, { target }, h('div', 'teleported')), + h(Teleport, { to: target }, h('div', 'teleported')), h('div', 'root') ]), root ) expect(serializeInner(root)).toMatchInlineSnapshot( - `"
root
"` + `"
root
"` ) expect(serializeInner(target)).toMatchInlineSnapshot( `"
teleported
"` ) }) - test('moving portal while disabled', () => { + test('moving teleport while disabled', () => { const target = nodeOps.createElement('div') const root = nodeOps.createElement('div') render( h(Fragment, [ - h(Portal, { target, disabled: true }, h('div', 'teleported')), + h(Teleport, { to: target, disabled: true }, h('div', 'teleported')), h('div', 'root') ]), root ) expect(serializeInner(root)).toMatchInlineSnapshot( - `"
teleported
root
"` + `"
teleported
root
"` ) expect(serializeInner(target)).toBe('') render( h(Fragment, [ h('div', 'root'), - h(Portal, { target, disabled: true }, h('div', 'teleported')) + h(Teleport, { to: target, disabled: true }, h('div', 'teleported')) ]), root ) expect(serializeInner(root)).toMatchInlineSnapshot( - `"
root
teleported
"` + `"
root
teleported
"` ) expect(serializeInner(target)).toBe('') render( h(Fragment, [ - h(Portal, { target, disabled: true }, h('div', 'teleported')), + h(Teleport, { to: target, disabled: true }, h('div', 'teleported')), h('div', 'root') ]), root ) expect(serializeInner(root)).toMatchInlineSnapshot( - `"
teleported
root
"` + `"
teleported
root
"` ) expect(serializeInner(target)).toBe('') }) diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts index 510c14e0..837be3ee 100644 --- a/packages/runtime-core/__tests__/hydration.spec.ts +++ b/packages/runtime-core/__tests__/hydration.spec.ts @@ -4,7 +4,7 @@ import { ref, nextTick, VNode, - Portal, + Teleport, createStaticVNode, Suspense, onMounted, @@ -153,18 +153,18 @@ describe('SSR hydration', () => { ) }) - test('Portal', async () => { + test('Teleport', async () => { const msg = ref('foo') const fn = jest.fn() - const portalContainer = document.createElement('div') - portalContainer.id = 'portal' - portalContainer.innerHTML = `foo` - document.body.appendChild(portalContainer) + const teleportContainer = document.createElement('div') + teleportContainer.id = 'teleport' + teleportContainer.innerHTML = `foo` + document.body.appendChild(teleportContainer) const { vnode, container } = mountWithHydration( - '', + '', () => - h(Portal, { target: '#portal' }, [ + h(Teleport, { to: '#teleport' }, [ h('span', msg.value), h('span', { class: msg.value, onClick: fn }) ]) @@ -173,120 +173,120 @@ describe('SSR hydration', () => { expect(vnode.el).toBe(container.firstChild) expect(vnode.anchor).toBe(container.lastChild) - expect(vnode.target).toBe(portalContainer) + expect(vnode.target).toBe(teleportContainer) expect((vnode.children as VNode[])[0].el).toBe( - portalContainer.childNodes[0] + teleportContainer.childNodes[0] ) expect((vnode.children as VNode[])[1].el).toBe( - portalContainer.childNodes[1] + teleportContainer.childNodes[1] ) - expect(vnode.targetAnchor).toBe(portalContainer.childNodes[2]) + expect(vnode.targetAnchor).toBe(teleportContainer.childNodes[2]) // event handler - triggerEvent('click', portalContainer.querySelector('.foo')!) + triggerEvent('click', teleportContainer.querySelector('.foo')!) expect(fn).toHaveBeenCalled() msg.value = 'bar' await nextTick() - expect(portalContainer.innerHTML).toBe( + expect(teleportContainer.innerHTML).toBe( `bar` ) }) - test('Portal (multiple + integration)', async () => { + test('Teleport (multiple + integration)', async () => { const msg = ref('foo') const fn1 = jest.fn() const fn2 = jest.fn() const Comp = () => [ - h(Portal, { target: '#portal2' }, [ + h(Teleport, { to: '#teleport2' }, [ h('span', msg.value), h('span', { class: msg.value, onClick: fn1 }) ]), - h(Portal, { target: '#portal2' }, [ + h(Teleport, { to: '#teleport2' }, [ h('span', msg.value + '2'), h('span', { class: msg.value + '2', onClick: fn2 }) ]) ] - const portalContainer = document.createElement('div') - portalContainer.id = 'portal2' + const teleportContainer = document.createElement('div') + teleportContainer.id = 'teleport2' const ctx: SSRContext = {} const mainHtml = await renderToString(h(Comp), ctx) expect(mainHtml).toMatchInlineSnapshot( - `""` + `""` ) - const portalHtml = ctx.portals!['#portal2'] - expect(portalHtml).toMatchInlineSnapshot( + const teleportHtml = ctx.teleports!['#teleport2'] + expect(teleportHtml).toMatchInlineSnapshot( `"foofoo2"` ) - portalContainer.innerHTML = portalHtml - document.body.appendChild(portalContainer) + teleportContainer.innerHTML = teleportHtml + document.body.appendChild(teleportContainer) const { vnode, container } = mountWithHydration(mainHtml, Comp) expect(vnode.el).toBe(container.firstChild) - const portalVnode1 = (vnode.children as VNode[])[0] - const portalVnode2 = (vnode.children as VNode[])[1] - expect(portalVnode1.el).toBe(container.childNodes[1]) - expect(portalVnode1.anchor).toBe(container.childNodes[2]) - expect(portalVnode2.el).toBe(container.childNodes[3]) - expect(portalVnode2.anchor).toBe(container.childNodes[4]) + const teleportVnode1 = (vnode.children as VNode[])[0] + const teleportVnode2 = (vnode.children as VNode[])[1] + expect(teleportVnode1.el).toBe(container.childNodes[1]) + expect(teleportVnode1.anchor).toBe(container.childNodes[2]) + expect(teleportVnode2.el).toBe(container.childNodes[3]) + expect(teleportVnode2.anchor).toBe(container.childNodes[4]) - expect(portalVnode1.target).toBe(portalContainer) - expect((portalVnode1 as any).children[0].el).toBe( - portalContainer.childNodes[0] + expect(teleportVnode1.target).toBe(teleportContainer) + expect((teleportVnode1 as any).children[0].el).toBe( + teleportContainer.childNodes[0] ) - expect(portalVnode1.targetAnchor).toBe(portalContainer.childNodes[2]) + expect(teleportVnode1.targetAnchor).toBe(teleportContainer.childNodes[2]) - expect(portalVnode2.target).toBe(portalContainer) - expect((portalVnode2 as any).children[0].el).toBe( - portalContainer.childNodes[3] + expect(teleportVnode2.target).toBe(teleportContainer) + expect((teleportVnode2 as any).children[0].el).toBe( + teleportContainer.childNodes[3] ) - expect(portalVnode2.targetAnchor).toBe(portalContainer.childNodes[5]) + expect(teleportVnode2.targetAnchor).toBe(teleportContainer.childNodes[5]) // // event handler - triggerEvent('click', portalContainer.querySelector('.foo')!) + triggerEvent('click', teleportContainer.querySelector('.foo')!) expect(fn1).toHaveBeenCalled() - triggerEvent('click', portalContainer.querySelector('.foo2')!) + triggerEvent('click', teleportContainer.querySelector('.foo2')!) expect(fn2).toHaveBeenCalled() msg.value = 'bar' await nextTick() - expect(portalContainer.innerHTML).toMatchInlineSnapshot( + expect(teleportContainer.innerHTML).toMatchInlineSnapshot( `"barbar2"` ) }) - test('Portal (disabled)', async () => { + test('Teleport (disabled)', async () => { const msg = ref('foo') const fn1 = jest.fn() const fn2 = jest.fn() const Comp = () => [ h('div', 'foo'), - h(Portal, { target: '#portal3', disabled: true }, [ + h(Teleport, { to: '#teleport3', disabled: true }, [ h('span', msg.value), h('span', { class: msg.value, onClick: fn1 }) ]), h('div', { class: msg.value + '2', onClick: fn2 }, 'bar') ] - const portalContainer = document.createElement('div') - portalContainer.id = 'portal3' + const teleportContainer = document.createElement('div') + teleportContainer.id = 'teleport3' const ctx: SSRContext = {} const mainHtml = await renderToString(h(Comp), ctx) expect(mainHtml).toMatchInlineSnapshot( - `"
foo
foo
bar
"` + `"
foo
foo
bar
"` ) - const portalHtml = ctx.portals!['#portal3'] - expect(portalHtml).toMatchInlineSnapshot(`""`) + const teleportHtml = ctx.teleports!['#teleport3'] + expect(teleportHtml).toMatchInlineSnapshot(`""`) - portalContainer.innerHTML = portalHtml - document.body.appendChild(portalContainer) + teleportContainer.innerHTML = teleportHtml + document.body.appendChild(teleportContainer) const { vnode, container } = mountWithHydration(mainHtml, Comp) expect(vnode.el).toBe(container.firstChild) @@ -294,19 +294,19 @@ describe('SSR hydration', () => { expect(children[0].el).toBe(container.childNodes[1]) - const portalVnode = children[1] - expect(portalVnode.el).toBe(container.childNodes[2]) - expect((portalVnode.children as VNode[])[0].el).toBe( + const teleportVnode = children[1] + expect(teleportVnode.el).toBe(container.childNodes[2]) + expect((teleportVnode.children as VNode[])[0].el).toBe( container.childNodes[3] ) - expect((portalVnode.children as VNode[])[1].el).toBe( + expect((teleportVnode.children as VNode[])[1].el).toBe( container.childNodes[4] ) - expect(portalVnode.anchor).toBe(container.childNodes[5]) + expect(teleportVnode.anchor).toBe(container.childNodes[5]) expect(children[2].el).toBe(container.childNodes[6]) - expect(portalVnode.target).toBe(portalContainer) - expect(portalVnode.targetAnchor).toBe(portalContainer.childNodes[0]) + expect(teleportVnode.target).toBe(teleportContainer) + expect(teleportVnode.targetAnchor).toBe(teleportContainer.childNodes[0]) // // event handler triggerEvent('click', container.querySelector('.foo')!) @@ -318,7 +318,7 @@ describe('SSR hydration', () => { msg.value = 'bar' await nextTick() expect(container.innerHTML).toMatchInlineSnapshot( - `"
foo
bar
bar
"` + `"
foo
bar
bar
"` ) }) diff --git a/packages/runtime-core/src/apiOptions.ts b/packages/runtime-core/src/apiOptions.ts index 8db8b20d..0ed0e5c2 100644 --- a/packages/runtime-core/src/apiOptions.ts +++ b/packages/runtime-core/src/apiOptions.ts @@ -88,7 +88,7 @@ export interface ComponentOptionsBase< call?: never // type-only differentiators for built-in Vnode types __isFragment?: never - __isPortal?: never + __isTeleport?: never __isSuspense?: never } diff --git a/packages/runtime-core/src/components/Portal.ts b/packages/runtime-core/src/components/Teleport.ts similarity index 73% rename from packages/runtime-core/src/components/Portal.ts rename to packages/runtime-core/src/components/Teleport.ts index 9a4241a3..8091f014 100644 --- a/packages/runtime-core/src/components/Portal.ts +++ b/packages/runtime-core/src/components/Teleport.ts @@ -11,26 +11,26 @@ import { VNode, VNodeArrayChildren, VNodeProps } from '../vnode' import { isString, ShapeFlags } from '@vue/shared' import { warn } from '../warning' -export interface PortalProps { - target: string | RendererElement +export interface TeleportProps { + to: string | RendererElement disabled?: boolean } -export const isPortal = (type: any): boolean => type.__isPortal +export const isTeleport = (type: any): boolean => type.__isTeleport -const isPortalDisabled = (props: VNode['props']): boolean => +const isTeleportDisabled = (props: VNode['props']): boolean => props && (props.disabled || props.disabled === '') const resolveTarget = ( - props: PortalProps | null, + props: TeleportProps | null, select: RendererOptions['querySelector'] ): T | null => { - const targetSelector = props && props.target + const targetSelector = props && props.to if (isString(targetSelector)) { if (!select) { __DEV__ && warn( - `Current renderer does not support string target for Portals. ` + + `Current renderer does not support string target for Teleports. ` + `(missing querySelector renderer option)` ) return null @@ -39,21 +39,21 @@ const resolveTarget = ( if (!target) { __DEV__ && warn( - `Failed to locate Portal target with selector "${targetSelector}".` + `Failed to locate Teleport target with selector "${targetSelector}".` ) } return target as any } } else { if (__DEV__ && !targetSelector) { - warn(`Invalid Portal target: ${targetSelector}`) + warn(`Invalid Teleport target: ${targetSelector}`) } return targetSelector as any } } -export const PortalImpl = { - __isPortal: true, +export const TeleportImpl = { + __isTeleport: true, process( n1: VNode | null, n2: VNode, @@ -72,32 +72,32 @@ export const PortalImpl = { o: { insert, querySelector, createText, createComment } } = internals - const disabled = isPortalDisabled(n2.props) + const disabled = isTeleportDisabled(n2.props) const { shapeFlag, children } = n2 if (n1 == null) { // insert anchors in the main view const placeholder = (n2.el = __DEV__ - ? createComment('portal start') + ? createComment('teleport start') : createText('')) const mainAnchor = (n2.anchor = __DEV__ - ? createComment('portal end') + ? createComment('teleport end') : createText('')) insert(placeholder, container, anchor) insert(mainAnchor, container, anchor) const target = (n2.target = resolveTarget( - n2.props as PortalProps, + n2.props as TeleportProps, querySelector )) const targetAnchor = (n2.targetAnchor = createText('')) if (target) { insert(targetAnchor, target) } else if (__DEV__) { - warn('Invalid Portal target on mount:', target, `(${typeof target})`) + warn('Invalid Teleport target on mount:', target, `(${typeof target})`) } const mount = (container: RendererElement, anchor: RendererNode) => { - // Portal *always* has Array children. This is enforced in both the + // Teleport *always* has Array children. This is enforced in both the // compiler and vnode children normalization. if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { mountChildren( @@ -123,12 +123,12 @@ export const PortalImpl = { const mainAnchor = (n2.anchor = n1.anchor)! const target = (n2.target = n1.target)! const targetAnchor = (n2.targetAnchor = n1.targetAnchor)! - const wasDisabled = isPortalDisabled(n1.props) + const wasDisabled = isTeleportDisabled(n1.props) const currentContainer = wasDisabled ? container : target const currentAnchor = wasDisabled ? mainAnchor : targetAnchor if (n2.dynamicChildren) { - // fast path when the portal happens to be a block root + // fast path when the teleport happens to be a block root patchBlockChildren( n1.dynamicChildren!, n2.dynamicChildren, @@ -153,45 +153,45 @@ export const PortalImpl = { if (!wasDisabled) { // enabled -> disabled // move into main container - movePortal( + moveTeleport( n2, container, mainAnchor, internals, - PortalMoveTypes.TOGGLE + TeleportMoveTypes.TOGGLE ) } } else { // target changed - if ((n2.props && n2.props.target) !== (n1.props && n1.props.target)) { + if ((n2.props && n2.props.to) !== (n1.props && n1.props.to)) { const nextTarget = (n2.target = resolveTarget( - n2.props as PortalProps, + n2.props as TeleportProps, querySelector )) if (nextTarget) { - movePortal( + moveTeleport( n2, nextTarget, null, internals, - PortalMoveTypes.TARGET_CHANGE + TeleportMoveTypes.TARGET_CHANGE ) } else if (__DEV__) { warn( - 'Invalid Portal target on update:', + 'Invalid Teleport target on update:', target, `(${typeof target})` ) } } else if (wasDisabled) { // disabled -> enabled - // move into portal target - movePortal( + // move into teleport target + moveTeleport( n2, target, targetAnchor, internals, - PortalMoveTypes.TOGGLE + TeleportMoveTypes.TOGGLE ) } } @@ -211,38 +211,38 @@ export const PortalImpl = { } }, - move: movePortal, - hydrate: hydratePortal + move: moveTeleport, + hydrate: hydrateTeleport } -export const enum PortalMoveTypes { +export const enum TeleportMoveTypes { TARGET_CHANGE, TOGGLE, // enable / disable REORDER // moved in the main view } -function movePortal( +function moveTeleport( vnode: VNode, container: RendererElement, parentAnchor: RendererNode | null, { o: { insert }, m: move }: RendererInternals, - moveType: PortalMoveTypes = PortalMoveTypes.REORDER + moveType: TeleportMoveTypes = TeleportMoveTypes.REORDER ) { // move target anchor if this is a target change. - if (moveType === PortalMoveTypes.TARGET_CHANGE) { + if (moveType === TeleportMoveTypes.TARGET_CHANGE) { insert(vnode.targetAnchor!, container, parentAnchor) } const { el, anchor, shapeFlag, children, props } = vnode - const isReorder = moveType === PortalMoveTypes.REORDER + const isReorder = moveType === TeleportMoveTypes.REORDER // move main view anchor if this is a re-order. if (isReorder) { insert(el!, container, parentAnchor) } - // if this is a re-order and portal is enabled (content is in target) + // if this is a re-order and teleport is enabled (content is in target) // do not move children. So the opposite is: only move children if this - // is not a reorder, or the portal is disabled - if (!isReorder || isPortalDisabled(props)) { - // Portal has either Array children or no children. + // is not a reorder, or the teleport is disabled + if (!isReorder || isTeleportDisabled(props)) { + // Teleport has either Array children or no children. if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { for (let i = 0; i < (children as VNode[]).length; i++) { move( @@ -260,12 +260,12 @@ function movePortal( } } -interface PortalTargetElement extends Element { - // last portal target +interface TeleportTargetElement extends Element { + // last teleport target _lpa?: Node | null } -function hydratePortal( +function hydrateTeleport( node: Node, vnode: VNode, parentComponent: ComponentInternalInstance | null, @@ -284,15 +284,16 @@ function hydratePortal( ) => Node | null ): Node | null { const target = (vnode.target = resolveTarget( - vnode.props as PortalProps, + vnode.props as TeleportProps, querySelector )) if (target) { - // if multiple portals rendered to the same target element, we need to - // pick up from where the last portal finished instead of the first node - const targetNode = (target as PortalTargetElement)._lpa || target.firstChild + // if multiple teleports rendered to the same target element, we need to + // pick up from where the last teleport finished instead of the first node + const targetNode = + (target as TeleportTargetElement)._lpa || target.firstChild if (vnode.shapeFlag & ShapeFlags.ARRAY_CHILDREN) { - if (isPortalDisabled(vnode.props)) { + if (isTeleportDisabled(vnode.props)) { vnode.anchor = hydrateChildren( nextSibling(node), vnode, @@ -313,7 +314,7 @@ function hydratePortal( optimized ) } - ;(target as PortalTargetElement)._lpa = nextSibling( + ;(target as TeleportTargetElement)._lpa = nextSibling( vnode.targetAnchor as Node ) } @@ -322,7 +323,7 @@ function hydratePortal( } // Force-casted public typing for h and TSX props inference -export const Portal = (PortalImpl as any) as { - __isPortal: true - new (): { $props: VNodeProps & PortalProps } +export const Teleport = (TeleportImpl as any) as { + __isTeleport: true + new (): { $props: VNodeProps & TeleportProps } } diff --git a/packages/runtime-core/src/h.ts b/packages/runtime-core/src/h.ts index e6f07fb1..ee091035 100644 --- a/packages/runtime-core/src/h.ts +++ b/packages/runtime-core/src/h.ts @@ -6,7 +6,7 @@ import { Fragment, isVNode } from './vnode' -import { Portal, PortalProps } from './components/Portal' +import { Teleport, TeleportProps } from './components/Teleport' import { Suspense, SuspenseProps } from './components/Suspense' import { isObject, isArray } from '@vue/shared' import { RawSlots } from './componentSlots' @@ -68,7 +68,7 @@ type RawChildren = // fake constructor type returned from `defineComponent` interface Constructor

{ __isFragment?: never - __isPortal?: never + __isTeleport?: never __isSuspense?: never new (): { $props: P } } @@ -92,10 +92,10 @@ export function h( children?: VNodeArrayChildren ): VNode -// portal (target prop is required) +// teleport (target prop is required) export function h( - type: typeof Portal, - props: RawProps & PortalProps, + type: typeof Teleport, + props: RawProps & TeleportProps, children: RawChildren ): VNode diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index 55d6e558..03dc33bb 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -18,7 +18,7 @@ import { SuspenseBoundary, queueEffectWithSuspense } from './components/Suspense' -import { PortalImpl } from './components/Portal' +import { TeleportImpl } from './components/Teleport' export type RootHydrateFunction = ( vnode: VNode, @@ -172,11 +172,11 @@ export function createHydrationFunctions( return isFragmentStart ? locateClosingAsyncAnchor(node) : nextSibling(node) - } else if (shapeFlag & ShapeFlags.PORTAL) { + } else if (shapeFlag & ShapeFlags.TELEPORT) { if (domType !== DOMNodeTypes.COMMENT) { return onMismatch() } - return (vnode.type as typeof PortalImpl).hydrate( + return (vnode.type as typeof TeleportImpl).hydrate( node, vnode, parentComponent, diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 190a30f9..b9abec16 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -53,7 +53,7 @@ export { } from './vnode' // Internal Components export { Text, Comment, Fragment } from './vnode' -export { Portal, PortalProps } from './components/Portal' +export { Teleport, TeleportProps } from './components/Teleport' export { Suspense, SuspenseProps } from './components/Suspense' export { KeepAlive, KeepAliveProps } from './components/KeepAlive' export { diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 6b571358..f28bf250 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -58,7 +58,7 @@ import { queueEffectWithSuspense, SuspenseImpl } from './components/Suspense' -import { PortalImpl } from './components/Portal' +import { TeleportImpl } from './components/Teleport' import { KeepAliveSink, isKeepAlive } from './components/KeepAlive' import { registerHMR, unregisterHMR } from './hmr' import { @@ -412,8 +412,8 @@ function baseCreateRenderer( isSVG, optimized ) - } else if (shapeFlag & ShapeFlags.PORTAL) { - ;(type as typeof PortalImpl).process( + } else if (shapeFlag & ShapeFlags.TELEPORT) { + ;(type as typeof TeleportImpl).process( n1, n2, container, @@ -1207,7 +1207,7 @@ function baseCreateRenderer( patch( prevTree, nextTree, - // parent may have changed if it's in a portal + // parent may have changed if it's in a teleport hostParentNode(prevTree.el!)!, // anchor may have changed if it's in a fragment getNextHostNode(prevTree), @@ -1653,8 +1653,8 @@ function baseCreateRenderer( return } - if (shapeFlag & ShapeFlags.PORTAL) { - ;(type as typeof PortalImpl).move(vnode, container, anchor, internals) + if (shapeFlag & ShapeFlags.TELEPORT) { + ;(type as typeof TeleportImpl).move(vnode, container, anchor, internals) return } @@ -1739,9 +1739,9 @@ function baseCreateRenderer( unmountChildren(children as VNode[], parentComponent, parentSuspense) } - // an unmounted portal should always remove its children - if (shapeFlag & ShapeFlags.PORTAL) { - ;(vnode.type as typeof PortalImpl).remove(vnode, internals) + // an unmounted teleport should always remove its children + if (shapeFlag & ShapeFlags.TELEPORT) { + ;(vnode.type as typeof TeleportImpl).remove(vnode, internals) } if (doRemove) { diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index 0396323c..a05d9d22 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -29,7 +29,7 @@ import { DirectiveBinding } from './directives' import { TransitionHooks } from './components/BaseTransition' import { warn } from './warning' import { currentScopeId } from './helpers/scopeId' -import { PortalImpl, isPortal } from './components/Portal' +import { TeleportImpl, isTeleport } from './components/Teleport' import { currentRenderingInstance } from './componentRenderUtils' import { RendererNode, RendererElement } from './renderer' @@ -50,7 +50,7 @@ export type VNodeTypes = | typeof Static | typeof Comment | typeof Fragment - | typeof PortalImpl + | typeof TeleportImpl | typeof SuspenseImpl export type VNodeRef = @@ -113,8 +113,8 @@ export interface VNode { // DOM el: HostNode | null anchor: HostNode | null // fragment anchor - target: HostElement | null // portal target - targetAnchor: HostNode | null // portal target anchor + target: HostElement | null // teleport target + targetAnchor: HostNode | null // teleport target anchor // optimization only shapeFlag: number @@ -283,8 +283,8 @@ function _createVNode( ? ShapeFlags.ELEMENT : __FEATURE_SUSPENSE__ && isSuspense(type) ? ShapeFlags.SUSPENSE - : isPortal(type) - ? ShapeFlags.PORTAL + : isTeleport(type) + ? ShapeFlags.TELEPORT : isObject(type) ? ShapeFlags.STATEFUL_COMPONENT : isFunction(type) @@ -430,7 +430,7 @@ export function normalizeChildren(vnode: VNode, children: unknown) { } else if (typeof children === 'object') { // Normalize slot to plain children if ( - (shapeFlag & ShapeFlags.ELEMENT || shapeFlag & ShapeFlags.PORTAL) && + (shapeFlag & ShapeFlags.ELEMENT || shapeFlag & ShapeFlags.TELEPORT) && (children as any).default ) { normalizeChildren(vnode, (children as any).default()) @@ -446,8 +446,8 @@ export function normalizeChildren(vnode: VNode, children: unknown) { type = ShapeFlags.SLOTS_CHILDREN } else { children = String(children) - // force portal children to array so it can be moved around - if (shapeFlag & ShapeFlags.PORTAL) { + // force teleport children to array so it can be moved around + if (shapeFlag & ShapeFlags.TELEPORT) { type = ShapeFlags.ARRAY_CHILDREN children = [createTextVNode(children as string)] } else { diff --git a/packages/server-renderer/__tests__/ssrPortal.spec.ts b/packages/server-renderer/__tests__/ssrTeleport.spec.ts similarity index 51% rename from packages/server-renderer/__tests__/ssrPortal.spec.ts rename to packages/server-renderer/__tests__/ssrTeleport.spec.ts index 0a095e07..c990e600 100644 --- a/packages/server-renderer/__tests__/ssrPortal.spec.ts +++ b/packages/server-renderer/__tests__/ssrTeleport.spec.ts @@ -1,9 +1,9 @@ -import { createApp, h, Portal } from 'vue' +import { createApp, h, Teleport } from 'vue' import { renderToString, SSRContext } from '../src/renderToString' -import { ssrRenderPortal } from '../src/helpers/ssrRenderPortal' +import { ssrRenderTeleport } from '../src/helpers/ssrRenderTeleport' -describe('ssrRenderPortal', () => { - test('portal rendering (compiled)', async () => { +describe('ssrRenderTeleport', () => { + test('teleport rendering (compiled)', async () => { const ctx: SSRContext = {} const html = await renderToString( createApp({ @@ -11,7 +11,7 @@ describe('ssrRenderPortal', () => { return { msg: 'hello' } }, ssrRender(_ctx, _push, _parent) { - ssrRenderPortal( + ssrRenderTeleport( _push, _push => { _push(`

content
`) @@ -24,11 +24,11 @@ describe('ssrRenderPortal', () => { }), ctx ) - expect(html).toBe('') - expect(ctx.portals!['#target']).toBe(`
content
`) + expect(html).toBe('') + expect(ctx.teleports!['#target']).toBe(`
content
`) }) - test('portal rendering (compiled + disabled)', async () => { + test('teleport rendering (compiled + disabled)', async () => { const ctx: SSRContext = {} const html = await renderToString( createApp({ @@ -36,7 +36,7 @@ describe('ssrRenderPortal', () => { return { msg: 'hello' } }, ssrRender(_ctx, _push, _parent) { - ssrRenderPortal( + ssrRenderTeleport( _push, _push => { _push(`
content
`) @@ -49,62 +49,66 @@ describe('ssrRenderPortal', () => { }), ctx ) - expect(html).toBe('
content
') - expect(ctx.portals!['#target']).toBe(``) + expect(html).toBe( + '
content
' + ) + expect(ctx.teleports!['#target']).toBe(``) }) - test('portal rendering (vnode)', async () => { + test('teleport rendering (vnode)', async () => { const ctx: SSRContext = {} const html = await renderToString( h( - Portal, + Teleport, { - target: `#target` + to: `#target` }, h('span', 'hello') ), ctx ) - expect(html).toBe('') - expect(ctx.portals!['#target']).toBe('hello') + expect(html).toBe('') + expect(ctx.teleports!['#target']).toBe('hello') }) - test('portal rendering (vnode + disabled)', async () => { + test('teleport rendering (vnode + disabled)', async () => { const ctx: SSRContext = {} const html = await renderToString( h( - Portal, + Teleport, { - target: `#target`, + to: `#target`, disabled: true }, h('span', 'hello') ), ctx ) - expect(html).toBe('hello') - expect(ctx.portals!['#target']).toBe(``) + expect(html).toBe( + 'hello' + ) + expect(ctx.teleports!['#target']).toBe(``) }) - test('multiple portals with same target', async () => { + test('multiple teleports with same target', async () => { const ctx: SSRContext = {} const html = await renderToString( h('div', [ h( - Portal, + Teleport, { - target: `#target` + to: `#target` }, h('span', 'hello') ), - h(Portal, { target: `#target` }, 'world') + h(Teleport, { to: `#target` }, 'world') ]), ctx ) expect(html).toBe( - '
' + '
' ) - expect(ctx.portals!['#target']).toBe( + expect(ctx.teleports!['#target']).toBe( 'helloworld' ) }) diff --git a/packages/server-renderer/src/helpers/ssrRenderPortal.ts b/packages/server-renderer/src/helpers/ssrRenderTeleport.ts similarity index 54% rename from packages/server-renderer/src/helpers/ssrRenderPortal.ts rename to packages/server-renderer/src/helpers/ssrRenderTeleport.ts index 2f694cbd..66772265 100644 --- a/packages/server-renderer/src/helpers/ssrRenderPortal.ts +++ b/packages/server-renderer/src/helpers/ssrRenderTeleport.ts @@ -6,37 +6,37 @@ import { SSRBufferItem } from '../renderToString' -export function ssrRenderPortal( +export function ssrRenderTeleport( parentPush: PushFn, contentRenderFn: (push: PushFn) => void, target: string, disabled: boolean, parentComponent: ComponentInternalInstance ) { - parentPush('') + parentPush('') - let portalContent: SSRBufferItem + let teleportContent: SSRBufferItem if (disabled) { contentRenderFn(parentPush) - portalContent = `` + teleportContent = `` } else { const { getBuffer, push } = createBuffer() contentRenderFn(push) - push(``) // portal end anchor - portalContent = getBuffer() + push(``) // teleport end anchor + teleportContent = getBuffer() } const context = parentComponent.appContext.provides[ ssrContextKey as any ] as SSRContext - const portalBuffers = - context.__portalBuffers || (context.__portalBuffers = {}) - if (portalBuffers[target]) { - portalBuffers[target].push(portalContent) + const teleportBuffers = + context.__teleportBuffers || (context.__teleportBuffers = {}) + if (teleportBuffers[target]) { + teleportBuffers[target].push(teleportContent) } else { - portalBuffers[target] = [portalContent] + teleportBuffers[target] = [teleportContent] } - parentPush('') + parentPush('') } diff --git a/packages/server-renderer/src/index.ts b/packages/server-renderer/src/index.ts index 48915c63..f685295b 100644 --- a/packages/server-renderer/src/index.ts +++ b/packages/server-renderer/src/index.ts @@ -13,7 +13,7 @@ export { } from './helpers/ssrRenderAttrs' export { ssrInterpolate } from './helpers/ssrInterpolate' export { ssrRenderList } from './helpers/ssrRenderList' -export { ssrRenderPortal } from './helpers/ssrRenderPortal' +export { ssrRenderTeleport } from './helpers/ssrRenderTeleport' export { ssrRenderSuspense } from './helpers/ssrRenderSuspense' // v-model helpers diff --git a/packages/server-renderer/src/renderToString.ts b/packages/server-renderer/src/renderToString.ts index 8cb4b392..7f384dd8 100644 --- a/packages/server-renderer/src/renderToString.ts +++ b/packages/server-renderer/src/renderToString.ts @@ -32,7 +32,7 @@ import { compile } from '@vue/compiler-ssr' import { ssrRenderAttrs } from './helpers/ssrRenderAttrs' import { SSRSlots } from './helpers/ssrRenderSlot' import { CompilerError } from '@vue/compiler-dom' -import { ssrRenderPortal } from './helpers/ssrRenderPortal' +import { ssrRenderTeleport } from './helpers/ssrRenderTeleport' const { isVNode, @@ -63,8 +63,8 @@ export type Props = Record export type SSRContext = { [key: string]: any - portals?: Record - __portalBuffers?: Record + teleports?: Record + __teleportBuffers?: Record } export function createBuffer() { @@ -123,7 +123,7 @@ export async function renderToString( input.provide(ssrContextKey, context) const buffer = await renderComponentVNode(vnode) - await resolvePortals(context) + await resolveTeleports(context) return unrollBuffer(buffer) } @@ -256,8 +256,8 @@ function renderVNode( renderElementVNode(push, vnode, parentComponent) } else if (shapeFlag & ShapeFlags.COMPONENT) { push(renderComponentVNode(vnode, parentComponent)) - } else if (shapeFlag & ShapeFlags.PORTAL) { - renderPortalVNode(push, vnode, parentComponent) + } else if (shapeFlag & ShapeFlags.TELEPORT) { + renderTeleportVNode(push, vnode, parentComponent) } else if (shapeFlag & ShapeFlags.SUSPENSE) { renderVNode( push, @@ -360,24 +360,24 @@ function applySSRDirectives( return mergeProps(rawProps || {}, ...toMerge) } -function renderPortalVNode( +function renderTeleportVNode( push: PushFn, vnode: VNode, parentComponent: ComponentInternalInstance ) { - const target = vnode.props && vnode.props.target + const target = vnode.props && vnode.props.to const disabled = vnode.props && vnode.props.disabled if (!target) { - warn(`[@vue/server-renderer] Portal is missing target prop.`) + warn(`[@vue/server-renderer] Teleport is missing target prop.`) return [] } if (!isString(target)) { warn( - `[@vue/server-renderer] Portal target must be a query selector string.` + `[@vue/server-renderer] Teleport target must be a query selector string.` ) return [] } - ssrRenderPortal( + ssrRenderTeleport( push, push => { renderVNodeChildren( @@ -392,14 +392,14 @@ function renderPortalVNode( ) } -async function resolvePortals(context: SSRContext) { - if (context.__portalBuffers) { - context.portals = context.portals || {} - for (const key in context.__portalBuffers) { +async function resolveTeleports(context: SSRContext) { + if (context.__teleportBuffers) { + context.teleports = context.teleports || {} + for (const key in context.__teleportBuffers) { // note: it's OK to await sequentially here because the Promises were // created eagerly in parallel. - context.portals[key] = unrollBuffer( - await Promise.all(context.__portalBuffers[key]) + context.teleports[key] = unrollBuffer( + await Promise.all(context.__teleportBuffers[key]) ) } } diff --git a/packages/shared/src/shapeFlags.ts b/packages/shared/src/shapeFlags.ts index 93c5f782..8defb8a3 100644 --- a/packages/shared/src/shapeFlags.ts +++ b/packages/shared/src/shapeFlags.ts @@ -5,7 +5,7 @@ export const enum ShapeFlags { TEXT_CHILDREN = 1 << 3, ARRAY_CHILDREN = 1 << 4, SLOTS_CHILDREN = 1 << 5, - PORTAL = 1 << 6, + TELEPORT = 1 << 6, SUSPENSE = 1 << 7, COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8, COMPONENT_KEPT_ALIVE = 1 << 9, diff --git a/packages/vue/examples/transition/modal.html b/packages/vue/examples/transition/modal.html index 3c8f1d07..03180539 100644 --- a/packages/vue/examples/transition/modal.html +++ b/packages/vue/examples/transition/modal.html @@ -45,14 +45,14 @@ const Modal = {
- + - +