From 1cc05539052a645a2072926e844b9b03fae2c76d Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 24 Oct 2025 08:48:55 +0800 Subject: [PATCH] refactor: rewrite defineVaporAsyncComponent import to defineAsyncComponent in SSR + vapor mode (#14016) This limits the defineVaporAsyncComponent only used in the `.vue` file in SSR revert https://github.com/vuejs/core/pull/13383/commits/897404317ef749c12d59f9731d0e8d94b013c6ac --- .../__tests__/compileScript.spec.ts | 38 ++++++ packages/compiler-sfc/src/compileScript.ts | 17 +++ packages/vue/src/index-with-vapor.ts | 2 +- packages/vue/src/index.ts | 109 +++++++++++++++++- packages/vue/src/indexBase.ts | 107 ----------------- packages/vue/src/runtime-with-vapor.ts | 2 +- packages/vue/src/runtime.ts | 29 ++++- packages/vue/src/runtimeBase.ts | 27 ----- packages/vue/src/vaporAliases.ts | 7 -- 9 files changed, 191 insertions(+), 147 deletions(-) delete mode 100644 packages/vue/src/indexBase.ts delete mode 100644 packages/vue/src/runtimeBase.ts delete mode 100644 packages/vue/src/vaporAliases.ts diff --git a/packages/compiler-sfc/__tests__/compileScript.spec.ts b/packages/compiler-sfc/__tests__/compileScript.spec.ts index 4dd8e2921c..026ff85e70 100644 --- a/packages/compiler-sfc/__tests__/compileScript.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript.spec.ts @@ -1711,3 +1711,41 @@ describe('compileScript', () => { expect(scriptAst).not.toBeDefined() }) }) + +describe('vapor mode + ssr', () => { + test('rewrite defineVaporAsyncComponent import', () => { + const { content } = compile( + ` + + `, + { + templateOptions: { + ssr: true, + }, + }, + ) + expect(content).toContain( + `import { defineAsyncComponent as defineVaporAsyncComponent } from 'vue'`, + ) + }) + + test('rewrite defineVaporAsyncComponent import with local name', () => { + const { content } = compile( + ` + + `, + { + templateOptions: { + ssr: true, + }, + }, + ) + expect(content).toContain( + `import { defineAsyncComponent as def } from 'vue'`, + ) + }) +}) diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index 462fa18fda..022dd2ba02 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -388,6 +388,23 @@ export function compileScript( const local = specifier.local.name const imported = getImportedName(specifier) const source = node.source.value + + // rewrite defineVaporAsyncComponent import to defineAsyncComponent + // in SSR + Vapor mode + if ( + vapor && + ssr && + specifier.type === 'ImportSpecifier' && + source === 'vue' && + imported === 'defineVaporAsyncComponent' + ) { + ctx.s.overwrite( + specifier.start! + startOffset, + specifier.end! + startOffset, + `defineAsyncComponent as ${local}`, + ) + } + const existing = ctx.userImports[local] if (source === 'vue' && MACROS.includes(imported)) { if (local === imported) { diff --git a/packages/vue/src/index-with-vapor.ts b/packages/vue/src/index-with-vapor.ts index 9e139113ce..21f4c8073c 100644 --- a/packages/vue/src/index-with-vapor.ts +++ b/packages/vue/src/index-with-vapor.ts @@ -1,3 +1,3 @@ // for type generation only -export * from './indexBase' +export * from './index' export * from '@vue/runtime-vapor' diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index 8d2de8d301..785f3fd4bb 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -1,2 +1,107 @@ -export * from './indexBase' -export * from './vaporAliases' +// This entry is the "full-build" that includes both the runtime +// and the compiler, and supports on-the-fly compilation of the template option. +import { initDev } from './dev' +import { + type CompilerError, + type CompilerOptions, + compile, +} from '@vue/compiler-dom' +import { + type RenderFunction, + registerRuntimeCompiler, + warn, +} from '@vue/runtime-dom' +import * as runtimeDom from '@vue/runtime-dom' +import { + NOOP, + extend, + genCacheKey, + generateCodeFrame, + isString, +} from '@vue/shared' +import type { InternalRenderFunction } from 'packages/runtime-core/src/component' + +if (__DEV__) { + initDev() +} + +const compileCache: Record = Object.create(null) + +function compileToFunction( + template: string | HTMLElement, + options?: CompilerOptions, +): RenderFunction { + if (!isString(template)) { + if (template.nodeType) { + template = template.innerHTML + } else { + __DEV__ && warn(`invalid template option: `, template) + return NOOP + } + } + + const key = genCacheKey(template, options) + const cached = compileCache[key] + if (cached) { + return cached + } + + if (template[0] === '#') { + const el = document.querySelector(template) + if (__DEV__ && !el) { + warn(`Template element not found or is empty: ${template}`) + } + // __UNSAFE__ + // Reason: potential execution of JS expressions in in-DOM template. + // The user must make sure the in-DOM template is trusted. If it's rendered + // by the server, the template should not contain any user data. + template = el ? el.innerHTML : `` + } + + const opts = extend( + { + hoistStatic: true, + onError: __DEV__ ? onError : undefined, + onWarn: __DEV__ ? e => onError(e, true) : NOOP, + } as CompilerOptions, + options, + ) + + if (!opts.isCustomElement && typeof customElements !== 'undefined') { + opts.isCustomElement = tag => !!customElements.get(tag) + } + + const { code } = compile(template, opts) + + function onError(err: CompilerError, asWarning = false) { + const message = asWarning + ? err.message + : `Template compilation error: ${err.message}` + const codeFrame = + err.loc && + generateCodeFrame( + template as string, + err.loc.start.offset, + err.loc.end.offset, + ) + warn(codeFrame ? `${message}\n${codeFrame}` : message) + } + + // The wildcard import results in a huge object with every export + // with keys that cannot be mangled, and can be quite heavy size-wise. + // In the global build we know `Vue` is available globally so we can avoid + // the wildcard object. + 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) +} + +registerRuntimeCompiler(compileToFunction) + +export { compileToFunction as compile } +export * from '@vue/runtime-dom' diff --git a/packages/vue/src/indexBase.ts b/packages/vue/src/indexBase.ts deleted file mode 100644 index 785f3fd4bb..0000000000 --- a/packages/vue/src/indexBase.ts +++ /dev/null @@ -1,107 +0,0 @@ -// This entry is the "full-build" that includes both the runtime -// and the compiler, and supports on-the-fly compilation of the template option. -import { initDev } from './dev' -import { - type CompilerError, - type CompilerOptions, - compile, -} from '@vue/compiler-dom' -import { - type RenderFunction, - registerRuntimeCompiler, - warn, -} from '@vue/runtime-dom' -import * as runtimeDom from '@vue/runtime-dom' -import { - NOOP, - extend, - genCacheKey, - generateCodeFrame, - isString, -} from '@vue/shared' -import type { InternalRenderFunction } from 'packages/runtime-core/src/component' - -if (__DEV__) { - initDev() -} - -const compileCache: Record = Object.create(null) - -function compileToFunction( - template: string | HTMLElement, - options?: CompilerOptions, -): RenderFunction { - if (!isString(template)) { - if (template.nodeType) { - template = template.innerHTML - } else { - __DEV__ && warn(`invalid template option: `, template) - return NOOP - } - } - - const key = genCacheKey(template, options) - const cached = compileCache[key] - if (cached) { - return cached - } - - if (template[0] === '#') { - const el = document.querySelector(template) - if (__DEV__ && !el) { - warn(`Template element not found or is empty: ${template}`) - } - // __UNSAFE__ - // Reason: potential execution of JS expressions in in-DOM template. - // The user must make sure the in-DOM template is trusted. If it's rendered - // by the server, the template should not contain any user data. - template = el ? el.innerHTML : `` - } - - const opts = extend( - { - hoistStatic: true, - onError: __DEV__ ? onError : undefined, - onWarn: __DEV__ ? e => onError(e, true) : NOOP, - } as CompilerOptions, - options, - ) - - if (!opts.isCustomElement && typeof customElements !== 'undefined') { - opts.isCustomElement = tag => !!customElements.get(tag) - } - - const { code } = compile(template, opts) - - function onError(err: CompilerError, asWarning = false) { - const message = asWarning - ? err.message - : `Template compilation error: ${err.message}` - const codeFrame = - err.loc && - generateCodeFrame( - template as string, - err.loc.start.offset, - err.loc.end.offset, - ) - warn(codeFrame ? `${message}\n${codeFrame}` : message) - } - - // The wildcard import results in a huge object with every export - // with keys that cannot be mangled, and can be quite heavy size-wise. - // In the global build we know `Vue` is available globally so we can avoid - // the wildcard object. - 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) -} - -registerRuntimeCompiler(compileToFunction) - -export { compileToFunction as compile } -export * from '@vue/runtime-dom' diff --git a/packages/vue/src/runtime-with-vapor.ts b/packages/vue/src/runtime-with-vapor.ts index eee717fc13..4f03329ede 100644 --- a/packages/vue/src/runtime-with-vapor.ts +++ b/packages/vue/src/runtime-with-vapor.ts @@ -1,2 +1,2 @@ -export * from './runtimeBase' +export * from './runtime' export * from '@vue/runtime-vapor' diff --git a/packages/vue/src/runtime.ts b/packages/vue/src/runtime.ts index 1c81ab0bad..af1ffe7a12 100644 --- a/packages/vue/src/runtime.ts +++ b/packages/vue/src/runtime.ts @@ -1,2 +1,27 @@ -export * from './runtimeBase' -export * from './vaporAliases' +// This entry exports the runtime only, and is built as +// `dist/vue.esm-bundler.js` which is used by default for bundlers. +import { NOOP } from '@vue/shared' +import { initDev } from './dev' +import { type RenderFunction, warn } from '@vue/runtime-dom' + +if (__DEV__) { + initDev() +} + +export * from '@vue/runtime-dom' + +export const compile = (_template: string): RenderFunction => { + if (__DEV__) { + warn( + `Runtime compilation is not supported in this build of Vue.` + + (__ESM_BUNDLER__ + ? ` Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".` + : __ESM_BROWSER__ + ? ` Use "vue.esm-browser.js" instead.` + : __GLOBAL__ + ? ` Use "vue.global.js" instead.` + : ``) /* should not happen */, + ) + } + return NOOP +} diff --git a/packages/vue/src/runtimeBase.ts b/packages/vue/src/runtimeBase.ts deleted file mode 100644 index af1ffe7a12..0000000000 --- a/packages/vue/src/runtimeBase.ts +++ /dev/null @@ -1,27 +0,0 @@ -// This entry exports the runtime only, and is built as -// `dist/vue.esm-bundler.js` which is used by default for bundlers. -import { NOOP } from '@vue/shared' -import { initDev } from './dev' -import { type RenderFunction, warn } from '@vue/runtime-dom' - -if (__DEV__) { - initDev() -} - -export * from '@vue/runtime-dom' - -export const compile = (_template: string): RenderFunction => { - if (__DEV__) { - warn( - `Runtime compilation is not supported in this build of Vue.` + - (__ESM_BUNDLER__ - ? ` Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".` - : __ESM_BROWSER__ - ? ` Use "vue.esm-browser.js" instead.` - : __GLOBAL__ - ? ` Use "vue.global.js" instead.` - : ``) /* should not happen */, - ) - } - return NOOP -} diff --git a/packages/vue/src/vaporAliases.ts b/packages/vue/src/vaporAliases.ts deleted file mode 100644 index f426d9d664..0000000000 --- a/packages/vue/src/vaporAliases.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Vapor-only APIs do not exist in the standard build, yet SSR executes -// the standard entry. We alias them to the core implementations so SSR -// keeps working without the Vapor runtime. -export { - defineAsyncComponent as defineVaporAsyncComponent, - defineComponent as defineVaporComponent, -} from '@vue/runtime-core' -- 2.47.3