mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 15:15:19 +00:00
perf(nuxt): don't include side-effects from #components
(#19008)
This commit is contained in:
parent
c8b49a3253
commit
1e8b27f36c
@ -72,7 +72,7 @@ export const componentsTemplate: NuxtTemplate<ComponentsTemplateContext> = {
|
||||
} else {
|
||||
definitions.push(genExport(c.filePath, [{ name: c.export, as: c.pascalName }]))
|
||||
}
|
||||
definitions.push(`export const Lazy${c.pascalName} = defineAsyncComponent(${genDynamicImport(c.filePath, { comment })}.then(c => ${isClient ? `createClientOnly(${exp})` : exp}))`)
|
||||
definitions.push(`export const Lazy${c.pascalName} = /* #__PURE__ */ defineAsyncComponent(${genDynamicImport(c.filePath, { comment })}.then(c => ${isClient ? `createClientOnly(${exp})` : exp}))`)
|
||||
return definitions
|
||||
})
|
||||
return [
|
||||
@ -96,7 +96,7 @@ export const componentsIslandsTemplate: NuxtTemplate<ComponentsTemplateContext>
|
||||
(c) => {
|
||||
const exp = c.export === 'default' ? 'c.default || c' : `c['${c.export}']`
|
||||
const comment = createImportMagicComments(c)
|
||||
return `export const ${c.pascalName} = defineAsyncComponent(${genDynamicImport(c.filePath, { comment })}.then(c => ${exp}))`
|
||||
return `export const ${c.pascalName} = /* #__PURE__ */ defineAsyncComponent(${genDynamicImport(c.filePath, { comment })}.then(c => ${exp}))`
|
||||
}
|
||||
).join('\n')
|
||||
}
|
||||
|
@ -46,6 +46,7 @@
|
||||
"postcss-url": "^10.1.3",
|
||||
"rollup": "^3.15.0",
|
||||
"rollup-plugin-visualizer": "^5.9.0",
|
||||
"strip-literal": "^1.0.1",
|
||||
"ufo": "^1.0.1",
|
||||
"unplugin": "^1.1.0",
|
||||
"vite": "~4.1.1",
|
||||
|
@ -15,6 +15,7 @@ import { chunkErrorPlugin } from './plugins/chunk-error'
|
||||
import type { ViteBuildContext, ViteOptions } from './vite'
|
||||
import { devStyleSSRPlugin } from './plugins/dev-ssr-css'
|
||||
import { runtimePathsPlugin } from './plugins/paths'
|
||||
import { pureAnnotationsPlugin } from './plugins/pure-annotations'
|
||||
import { viteNodePlugin } from './vite-node'
|
||||
|
||||
export async function buildClient (ctx: ViteBuildContext) {
|
||||
@ -69,7 +70,11 @@ export async function buildClient (ctx: ViteBuildContext) {
|
||||
runtimePathsPlugin({
|
||||
sourcemap: ctx.nuxt.options.sourcemap.client
|
||||
}),
|
||||
viteNodePlugin(ctx)
|
||||
viteNodePlugin(ctx),
|
||||
pureAnnotationsPlugin.vite({
|
||||
sourcemap: ctx.nuxt.options.sourcemap.client,
|
||||
functions: ['defineComponent', 'defineAsyncComponent', 'defineNuxtLink', 'createClientOnly']
|
||||
})
|
||||
],
|
||||
appType: 'custom',
|
||||
server: {
|
||||
|
52
packages/vite/src/plugins/pure-annotations.ts
Normal file
52
packages/vite/src/plugins/pure-annotations.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import MagicString from 'magic-string'
|
||||
import { parseQuery, parseURL } from 'ufo'
|
||||
import { createUnplugin } from 'unplugin'
|
||||
import { stripLiteral } from 'strip-literal'
|
||||
|
||||
export interface PureAnnotationsOptions {
|
||||
sourcemap: boolean
|
||||
functions: string[]
|
||||
}
|
||||
|
||||
export const pureAnnotationsPlugin = createUnplugin((options: PureAnnotationsOptions) => {
|
||||
const FUNCTION_RE = new RegExp(`(?<!\\/\\* #__PURE__ \\*\\/ )\\b(${options.functions.join('|')})\\s*\\(`, 'g')
|
||||
const FUNCTION_RE_SINGLE = new RegExp(`(?<!\\/\\* #__PURE__ \\*\\/ )\\b(${options.functions.join('|')})\\s*\\(`)
|
||||
return {
|
||||
name: 'nuxt:pure-annotations',
|
||||
enforce: 'post',
|
||||
transformInclude (id) {
|
||||
const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href))
|
||||
const { type } = parseQuery(search)
|
||||
|
||||
// vue files
|
||||
if (pathname.endsWith('.vue') && (type === 'script' || !search)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// js files
|
||||
if (pathname.match(/\.((c|m)?j|t)sx?$/g)) {
|
||||
return true
|
||||
}
|
||||
},
|
||||
transform (code, id) {
|
||||
if (!FUNCTION_RE_SINGLE.test(code)) { return }
|
||||
|
||||
const s = new MagicString(code)
|
||||
const strippedCode = stripLiteral(code)
|
||||
|
||||
for (const match of strippedCode.matchAll(FUNCTION_RE)) {
|
||||
s.overwrite(match.index!, match.index! + match[0].length, '/* #__PURE__ */ ' + match[0])
|
||||
}
|
||||
|
||||
if (s.hasChanged()) {
|
||||
return {
|
||||
code: s.toString(),
|
||||
map: options.sourcemap
|
||||
? s.generateMap({ source: id, includeContent: true })
|
||||
: undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
@ -22,6 +22,20 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
||||
|
||||
return {
|
||||
name: 'ssr-styles',
|
||||
resolveId: {
|
||||
order: 'pre',
|
||||
async handler (id, importer, options) {
|
||||
if (!id.endsWith('.vue')) { return }
|
||||
|
||||
const res = await this.resolve(id, importer, { ...options, skipSelf: true })
|
||||
if (res) {
|
||||
return {
|
||||
...res,
|
||||
moduleSideEffects: false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
generateBundle (outputOptions) {
|
||||
const emitted: Record<string, string> = {}
|
||||
for (const file in cssMap) {
|
||||
|
@ -8,6 +8,7 @@ import type { ViteBuildContext, ViteOptions } from './vite'
|
||||
import { cacheDirPlugin } from './plugins/cache-dir'
|
||||
import { initViteNodeServer } from './vite-node'
|
||||
import { ssrStylesPlugin } from './plugins/ssr-styles'
|
||||
import { pureAnnotationsPlugin } from './plugins/pure-annotations'
|
||||
import { writeManifest } from './manifest'
|
||||
import { transpile } from './utils/transpile'
|
||||
|
||||
@ -110,7 +111,11 @@ export async function buildServer (ctx: ViteBuildContext) {
|
||||
plugins: [
|
||||
cacheDirPlugin(ctx.nuxt.options.rootDir, 'server'),
|
||||
vuePlugin(ctx.config.vue),
|
||||
viteJsxPlugin(ctx.config.vueJsx)
|
||||
viteJsxPlugin(ctx.config.vueJsx),
|
||||
pureAnnotationsPlugin.vite({
|
||||
sourcemap: ctx.nuxt.options.sourcemap.server,
|
||||
functions: ['defineComponent', 'defineAsyncComponent', 'defineNuxtLink', 'createClientOnly']
|
||||
})
|
||||
]
|
||||
} as ViteOptions)
|
||||
|
||||
|
@ -614,6 +614,7 @@ importers:
|
||||
postcss-url: ^10.1.3
|
||||
rollup: ^3.15.0
|
||||
rollup-plugin-visualizer: ^5.9.0
|
||||
strip-literal: ^1.0.1
|
||||
ufo: ^1.0.1
|
||||
unbuild: ^1.1.1
|
||||
unplugin: ^1.1.0
|
||||
@ -650,6 +651,7 @@ importers:
|
||||
postcss-url: 10.1.3_postcss@8.4.21
|
||||
rollup: 3.15.0
|
||||
rollup-plugin-visualizer: 5.9.0_rollup@3.15.0
|
||||
strip-literal: 1.0.1
|
||||
ufo: 1.0.1
|
||||
unplugin: 1.1.0
|
||||
vite: 4.1.1
|
||||
|
@ -29,7 +29,7 @@ describe.skipIf(isWindows)('minimal nuxt application', () => {
|
||||
expect(stats.client.totalBytes).toBeLessThan(108000)
|
||||
expect(stats.client.files.map(f => f.replace(/\..*\.js/, '.js'))).toMatchInlineSnapshot(`
|
||||
[
|
||||
"_nuxt/app.js",
|
||||
"_nuxt/_plugin-vue_export-helper.js",
|
||||
"_nuxt/entry.js",
|
||||
"_nuxt/error-404.js",
|
||||
"_nuxt/error-500.js",
|
||||
@ -40,7 +40,7 @@ describe.skipIf(isWindows)('minimal nuxt application', () => {
|
||||
|
||||
it('default server bundle size', async () => {
|
||||
stats.server = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
|
||||
expect(stats.server.totalBytes).toBeLessThan(90200)
|
||||
expect(stats.server.totalBytes).toBeLessThan(92000)
|
||||
|
||||
const modules = await analyzeSizes('node_modules/**/*', serverDir)
|
||||
expect(modules.totalBytes).toBeLessThan(2700000)
|
||||
|
5
test/fixtures/minimal/app.vue
vendored
5
test/fixtures/minimal/app.vue
vendored
@ -1,3 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { componentNames } from '#components'
|
||||
console.log(componentNames)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>Hello World!</div>
|
||||
</template>
|
||||
|
Loading…
Reference in New Issue
Block a user