mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 23:22:02 +00:00
perf(nuxt): don't include side-effects from #components
(#19008)
This commit is contained in:
parent
c8b49a3253
commit
1e8b27f36c
@ -68,11 +68,11 @@ export const componentsTemplate: NuxtTemplate<ComponentsTemplateContext> = {
|
|||||||
const identifier = `__nuxt_component_${num}`
|
const identifier = `__nuxt_component_${num}`
|
||||||
imports.add(genImport('#app/components/client-only', [{ name: 'createClientOnly' }]))
|
imports.add(genImport('#app/components/client-only', [{ name: 'createClientOnly' }]))
|
||||||
imports.add(genImport(c.filePath, [{ name: c.export, as: identifier }]))
|
imports.add(genImport(c.filePath, [{ name: c.export, as: identifier }]))
|
||||||
definitions.push(`export const ${c.pascalName} = /*#__PURE__*/ createClientOnly(${identifier})`)
|
definitions.push(`export const ${c.pascalName} = /* #__PURE__ */ createClientOnly(${identifier})`)
|
||||||
} else {
|
} else {
|
||||||
definitions.push(genExport(c.filePath, [{ name: c.export, as: c.pascalName }]))
|
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 definitions
|
||||||
})
|
})
|
||||||
return [
|
return [
|
||||||
@ -96,7 +96,7 @@ export const componentsIslandsTemplate: NuxtTemplate<ComponentsTemplateContext>
|
|||||||
(c) => {
|
(c) => {
|
||||||
const exp = c.export === 'default' ? 'c.default || c' : `c['${c.export}']`
|
const exp = c.export === 'default' ? 'c.default || c' : `c['${c.export}']`
|
||||||
const comment = createImportMagicComments(c)
|
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')
|
).join('\n')
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
"postcss-url": "^10.1.3",
|
"postcss-url": "^10.1.3",
|
||||||
"rollup": "^3.15.0",
|
"rollup": "^3.15.0",
|
||||||
"rollup-plugin-visualizer": "^5.9.0",
|
"rollup-plugin-visualizer": "^5.9.0",
|
||||||
|
"strip-literal": "^1.0.1",
|
||||||
"ufo": "^1.0.1",
|
"ufo": "^1.0.1",
|
||||||
"unplugin": "^1.1.0",
|
"unplugin": "^1.1.0",
|
||||||
"vite": "~4.1.1",
|
"vite": "~4.1.1",
|
||||||
|
@ -15,6 +15,7 @@ import { chunkErrorPlugin } from './plugins/chunk-error'
|
|||||||
import type { ViteBuildContext, ViteOptions } from './vite'
|
import type { ViteBuildContext, ViteOptions } from './vite'
|
||||||
import { devStyleSSRPlugin } from './plugins/dev-ssr-css'
|
import { devStyleSSRPlugin } from './plugins/dev-ssr-css'
|
||||||
import { runtimePathsPlugin } from './plugins/paths'
|
import { runtimePathsPlugin } from './plugins/paths'
|
||||||
|
import { pureAnnotationsPlugin } from './plugins/pure-annotations'
|
||||||
import { viteNodePlugin } from './vite-node'
|
import { viteNodePlugin } from './vite-node'
|
||||||
|
|
||||||
export async function buildClient (ctx: ViteBuildContext) {
|
export async function buildClient (ctx: ViteBuildContext) {
|
||||||
@ -69,7 +70,11 @@ export async function buildClient (ctx: ViteBuildContext) {
|
|||||||
runtimePathsPlugin({
|
runtimePathsPlugin({
|
||||||
sourcemap: ctx.nuxt.options.sourcemap.client
|
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',
|
appType: 'custom',
|
||||||
server: {
|
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 {
|
return {
|
||||||
name: 'ssr-styles',
|
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) {
|
generateBundle (outputOptions) {
|
||||||
const emitted: Record<string, string> = {}
|
const emitted: Record<string, string> = {}
|
||||||
for (const file in cssMap) {
|
for (const file in cssMap) {
|
||||||
|
@ -8,6 +8,7 @@ import type { ViteBuildContext, ViteOptions } from './vite'
|
|||||||
import { cacheDirPlugin } from './plugins/cache-dir'
|
import { cacheDirPlugin } from './plugins/cache-dir'
|
||||||
import { initViteNodeServer } from './vite-node'
|
import { initViteNodeServer } from './vite-node'
|
||||||
import { ssrStylesPlugin } from './plugins/ssr-styles'
|
import { ssrStylesPlugin } from './plugins/ssr-styles'
|
||||||
|
import { pureAnnotationsPlugin } from './plugins/pure-annotations'
|
||||||
import { writeManifest } from './manifest'
|
import { writeManifest } from './manifest'
|
||||||
import { transpile } from './utils/transpile'
|
import { transpile } from './utils/transpile'
|
||||||
|
|
||||||
@ -110,7 +111,11 @@ export async function buildServer (ctx: ViteBuildContext) {
|
|||||||
plugins: [
|
plugins: [
|
||||||
cacheDirPlugin(ctx.nuxt.options.rootDir, 'server'),
|
cacheDirPlugin(ctx.nuxt.options.rootDir, 'server'),
|
||||||
vuePlugin(ctx.config.vue),
|
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)
|
} as ViteOptions)
|
||||||
|
|
||||||
|
@ -614,6 +614,7 @@ importers:
|
|||||||
postcss-url: ^10.1.3
|
postcss-url: ^10.1.3
|
||||||
rollup: ^3.15.0
|
rollup: ^3.15.0
|
||||||
rollup-plugin-visualizer: ^5.9.0
|
rollup-plugin-visualizer: ^5.9.0
|
||||||
|
strip-literal: ^1.0.1
|
||||||
ufo: ^1.0.1
|
ufo: ^1.0.1
|
||||||
unbuild: ^1.1.1
|
unbuild: ^1.1.1
|
||||||
unplugin: ^1.1.0
|
unplugin: ^1.1.0
|
||||||
@ -650,6 +651,7 @@ importers:
|
|||||||
postcss-url: 10.1.3_postcss@8.4.21
|
postcss-url: 10.1.3_postcss@8.4.21
|
||||||
rollup: 3.15.0
|
rollup: 3.15.0
|
||||||
rollup-plugin-visualizer: 5.9.0_rollup@3.15.0
|
rollup-plugin-visualizer: 5.9.0_rollup@3.15.0
|
||||||
|
strip-literal: 1.0.1
|
||||||
ufo: 1.0.1
|
ufo: 1.0.1
|
||||||
unplugin: 1.1.0
|
unplugin: 1.1.0
|
||||||
vite: 4.1.1
|
vite: 4.1.1
|
||||||
|
@ -29,7 +29,7 @@ describe.skipIf(isWindows)('minimal nuxt application', () => {
|
|||||||
expect(stats.client.totalBytes).toBeLessThan(108000)
|
expect(stats.client.totalBytes).toBeLessThan(108000)
|
||||||
expect(stats.client.files.map(f => f.replace(/\..*\.js/, '.js'))).toMatchInlineSnapshot(`
|
expect(stats.client.files.map(f => f.replace(/\..*\.js/, '.js'))).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
"_nuxt/app.js",
|
"_nuxt/_plugin-vue_export-helper.js",
|
||||||
"_nuxt/entry.js",
|
"_nuxt/entry.js",
|
||||||
"_nuxt/error-404.js",
|
"_nuxt/error-404.js",
|
||||||
"_nuxt/error-500.js",
|
"_nuxt/error-500.js",
|
||||||
@ -40,7 +40,7 @@ describe.skipIf(isWindows)('minimal nuxt application', () => {
|
|||||||
|
|
||||||
it('default server bundle size', async () => {
|
it('default server bundle size', async () => {
|
||||||
stats.server = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
|
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)
|
const modules = await analyzeSizes('node_modules/**/*', serverDir)
|
||||||
expect(modules.totalBytes).toBeLessThan(2700000)
|
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>
|
<template>
|
||||||
<div>Hello World!</div>
|
<div>Hello World!</div>
|
||||||
</template>
|
</template>
|
||||||
|
Loading…
Reference in New Issue
Block a user