mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 08:33:53 +00:00
perf(vite): various performance improvements (#27601)
This commit is contained in:
parent
02945b9fa6
commit
ffdc358561
@ -28,6 +28,7 @@
|
||||
"@types/clear": "0.1.4",
|
||||
"@types/estree": "1.0.5",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"rollup": "^4.18.0",
|
||||
"unbuild": "latest",
|
||||
"vue": "3.4.27"
|
||||
},
|
||||
|
@ -163,10 +163,11 @@ export async function buildClient (ctx: ViteBuildContext) {
|
||||
}
|
||||
|
||||
// We want to respect users' own rollup output options
|
||||
const fileNames = withoutLeadingSlash(join(ctx.nuxt.options.app.buildAssetsDir, '[hash].js'))
|
||||
clientConfig.build!.rollupOptions = defu(clientConfig.build!.rollupOptions!, {
|
||||
output: {
|
||||
chunkFileNames: ctx.nuxt.options.dev ? undefined : withoutLeadingSlash(join(ctx.nuxt.options.app.buildAssetsDir, '[hash].js')),
|
||||
entryFileNames: ctx.nuxt.options.dev ? 'entry.js' : withoutLeadingSlash(join(ctx.nuxt.options.app.buildAssetsDir, '[hash].js')),
|
||||
chunkFileNames: ctx.nuxt.options.dev ? undefined : fileNames,
|
||||
entryFileNames: ctx.nuxt.options.dev ? 'entry.js' : fileNames,
|
||||
} satisfies NonNullable<BuildOptions['rollupOptions']>['output'],
|
||||
}) as any
|
||||
|
||||
@ -228,7 +229,13 @@ export async function buildClient (ctx: ViteBuildContext) {
|
||||
})
|
||||
|
||||
const viteMiddleware = defineEventHandler(async (event) => {
|
||||
const viteRoutes = viteServer.middlewares.stack.map(m => m.route).filter(r => r.length > 1)
|
||||
const viteRoutes: string[] = []
|
||||
for (const viteRoute of viteServer.middlewares.stack) {
|
||||
const m = viteRoute.route
|
||||
if (m.length > 1) {
|
||||
viteRoutes.push(m)
|
||||
}
|
||||
}
|
||||
if (!event.path.startsWith(clientConfig.base!) && !viteRoutes.some(route => event.path.startsWith(route))) {
|
||||
// @ts-expect-error _skip_transform is a private property
|
||||
event.node.req._skip_transform = true
|
||||
|
@ -3,6 +3,8 @@ import type { Nuxt } from '@nuxt/schema'
|
||||
import type { InlineConfig as ViteConfig } from 'vite'
|
||||
import { distDir } from './dirs'
|
||||
|
||||
const lastPlugins = ['autoprefixer', 'cssnano']
|
||||
|
||||
export function resolveCSSOptions (nuxt: Nuxt): ViteConfig['css'] {
|
||||
const css: ViteConfig['css'] & { postcss: NonNullable<Exclude<NonNullable<ViteConfig['css']>['postcss'], string>> } = {
|
||||
postcss: {
|
||||
@ -10,19 +12,22 @@ export function resolveCSSOptions (nuxt: Nuxt): ViteConfig['css'] {
|
||||
},
|
||||
}
|
||||
|
||||
const lastPlugins = ['autoprefixer', 'cssnano']
|
||||
css.postcss.plugins = Object.entries(nuxt.options.postcss.plugins)
|
||||
css.postcss.plugins = []
|
||||
|
||||
const plugins = Object.entries(nuxt.options.postcss.plugins)
|
||||
.sort((a, b) => lastPlugins.indexOf(a[0]) - lastPlugins.indexOf(b[0]))
|
||||
.filter(([, opts]) => opts)
|
||||
.map(([name, opts]) => {
|
||||
|
||||
for (const [name, opts] of plugins) {
|
||||
if (opts) {
|
||||
const plugin = requireModule(name, {
|
||||
paths: [
|
||||
...nuxt.options.modulesDir,
|
||||
distDir,
|
||||
],
|
||||
})
|
||||
return plugin(opts)
|
||||
})
|
||||
css.postcss.plugins.push(plugin(opts))
|
||||
}
|
||||
}
|
||||
|
||||
return css
|
||||
}
|
||||
|
@ -238,7 +238,13 @@ export async function initViteDevBundler (ctx: ViteBuildContext, onBuild: () =>
|
||||
const { code, ids } = await bundleRequest(options, ctx.entry)
|
||||
await fse.writeFile(resolve(ctx.nuxt.options.buildDir, 'dist/server/server.mjs'), code, 'utf-8')
|
||||
// Have CSS in the manifest to prevent FOUC on dev SSR
|
||||
await writeManifest(ctx, ids.filter(isCSS).map(i => i.slice(1)))
|
||||
const manifestIds: string[] = []
|
||||
for (const i of ids) {
|
||||
if (isCSS(i)) {
|
||||
manifestIds.push(i.slice(1))
|
||||
}
|
||||
}
|
||||
await writeManifest(ctx, manifestIds)
|
||||
const time = (Date.now() - start)
|
||||
logger.success(`Vite server built in ${time}ms`)
|
||||
await onBuild()
|
||||
|
@ -50,11 +50,14 @@ export async function writeManifest (ctx: ViteBuildContext, css: string[] = [])
|
||||
await fse.mkdirp(serverDist)
|
||||
|
||||
if (ctx.config.build?.cssCodeSplit === false) {
|
||||
const entryCSS = Object.values(clientManifest as Record<string, { file?: string }>).find(val => (val).file?.endsWith('.css'))?.file
|
||||
if (entryCSS) {
|
||||
for (const key in clientManifest as Record<string, { file?: string }>) {
|
||||
const val = clientManifest[key]
|
||||
if (val.file?.endsWith('.css')) {
|
||||
const key = relative(ctx.config.root!, ctx.entry)
|
||||
clientManifest[key].css ||= []
|
||||
clientManifest[key].css!.push(entryCSS)
|
||||
clientManifest[key].css!.push(val.file)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ import { transform } from 'esbuild'
|
||||
import { visualizer } from 'rollup-plugin-visualizer'
|
||||
import defu from 'defu'
|
||||
import type { NuxtOptions } from 'nuxt/schema'
|
||||
import type { RenderedModule } from 'rollup'
|
||||
import type { ViteBuildContext } from '../vite'
|
||||
|
||||
export function analyzePlugin (ctx: ViteBuildContext): Plugin[] {
|
||||
@ -13,14 +14,18 @@ export function analyzePlugin (ctx: ViteBuildContext): Plugin[] {
|
||||
{
|
||||
name: 'nuxt:analyze-minify',
|
||||
async generateBundle (_opts, outputBundle) {
|
||||
for (const [_bundleId, bundle] of Object.entries(outputBundle)) {
|
||||
for (const _bundleId in outputBundle) {
|
||||
const bundle = outputBundle[_bundleId]
|
||||
if (bundle.type !== 'chunk') { continue }
|
||||
const originalEntries = Object.entries(bundle.modules)
|
||||
const minifiedEntries = await Promise.all(originalEntries.map(async ([moduleId, module]) => {
|
||||
const { code } = await transform(module.code || '', { minify: true })
|
||||
return [moduleId, { ...module, code }]
|
||||
}))
|
||||
bundle.modules = Object.fromEntries(minifiedEntries)
|
||||
const minifiedModuleEntryPromises: Array<Promise<[string, RenderedModule]>> = []
|
||||
for (const moduleId in bundle.modules) {
|
||||
const module = bundle.modules[moduleId]
|
||||
minifiedModuleEntryPromises.push(
|
||||
transform(module.code || '', { minify: true })
|
||||
.then(result => [moduleId, { ...module, code: result.code }]),
|
||||
)
|
||||
}
|
||||
bundle.modules = Object.fromEntries(await Promise.all(minifiedModuleEntryPromises))
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -23,12 +23,15 @@ const SUPPORTED_EXT_RE = /\.(?:m?[jt]sx?|vue)/
|
||||
|
||||
export const composableKeysPlugin = createUnplugin((options: ComposableKeysOptions) => {
|
||||
const composableMeta: Record<string, any> = {}
|
||||
const composableLengths = new Set<number>()
|
||||
const keyedFunctions = new Set<string>()
|
||||
for (const { name, ...meta } of options.composables) {
|
||||
composableMeta[name] = meta
|
||||
keyedFunctions.add(name)
|
||||
composableLengths.add(meta.argumentLength)
|
||||
}
|
||||
|
||||
const maxLength = Math.max(...options.composables.map(({ argumentLength }) => argumentLength))
|
||||
const keyedFunctions = new Set(options.composables.map(({ name }) => name))
|
||||
const maxLength = Math.max(...composableLengths)
|
||||
const KEYED_FUNCTIONS_RE = new RegExp(`\\b(${[...keyedFunctions].map(f => escapeRE(f)).join('|')})\\b`)
|
||||
|
||||
return {
|
||||
|
@ -25,7 +25,7 @@ export const VitePublicDirsPlugin = createUnplugin((options: { sourcemap?: boole
|
||||
resolveId: {
|
||||
enforce: 'post',
|
||||
handler (id) {
|
||||
if (id === '/__skip_vite' || !id.startsWith('/') || id.startsWith('/@fs')) { return }
|
||||
if (id === '/__skip_vite' || id[0] !== '/' || id.startsWith('/@fs')) { return }
|
||||
|
||||
if (resolveFromPublicAssets(id)) {
|
||||
return PREFIX + encodeURIComponent(id)
|
||||
|
@ -66,12 +66,12 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
||||
const { files, inBundle } = cssMap[file]
|
||||
// File has been tree-shaken out of build (or there are no styles to inline)
|
||||
if (!files.length || !inBundle) { continue }
|
||||
|
||||
const fileName = filename(file)
|
||||
const base = typeof outputOptions.assetFileNames === 'string'
|
||||
? outputOptions.assetFileNames
|
||||
: outputOptions.assetFileNames({
|
||||
type: 'asset',
|
||||
name: `${filename(file)}-styles.mjs`,
|
||||
name: `${fileName}-styles.mjs`,
|
||||
source: '',
|
||||
})
|
||||
|
||||
@ -79,7 +79,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
||||
|
||||
emitted[file] = this.emitFile({
|
||||
type: 'asset',
|
||||
name: `${filename(file)}-styles.mjs`,
|
||||
name: `${fileName}-styles.mjs`,
|
||||
source: [
|
||||
...files.map((css, i) => `import style_${i} from './${relative(baseDir, this.getFileName(css))}';`),
|
||||
`export default [${files.map((_, i) => `style_${i}`).join(', ')}]`,
|
||||
|
@ -105,7 +105,7 @@ export async function buildServer (ctx: ViteBuildContext) {
|
||||
|
||||
if (!ctx.nuxt.options.dev) {
|
||||
const nitroDependencies = await tryResolveModule('nitropack/package.json', ctx.nuxt.options.modulesDir)
|
||||
.then(r => import(r!)).then(r => Object.keys(r.dependencies || {})).catch(() => [])
|
||||
.then(r => import(r!)).then(r => r.dependencies ? Object.keys(r.dependencies) : []).catch(() => [])
|
||||
if (Array.isArray(serverConfig.ssr!.external)) {
|
||||
serverConfig.ssr!.external.push(
|
||||
// explicit dependencies we use in our ssr renderer - these can be inlined (if necessary) in the nitro build
|
||||
|
@ -10,7 +10,7 @@ interface Envs {
|
||||
|
||||
export function transpile (envs: Envs): Array<string | RegExp> {
|
||||
const nuxt = useNuxt()
|
||||
const transpile = []
|
||||
const transpile: Array<string | RegExp> = []
|
||||
|
||||
for (let pattern of nuxt.options.build.transpile) {
|
||||
if (typeof pattern === 'function') {
|
||||
|
@ -4,6 +4,7 @@ import { dirname, join, normalize, resolve } from 'pathe'
|
||||
import type { Nuxt, NuxtBuilder, ViteConfig } from '@nuxt/schema'
|
||||
import { addVitePlugin, isIgnored, logger, resolvePath } from '@nuxt/kit'
|
||||
import replace from '@rollup/plugin-replace'
|
||||
import type { RollupReplaceOptions } from '@rollup/plugin-replace'
|
||||
import { sanitizeFilePath } from 'mlly'
|
||||
import { withoutLeadingSlash } from 'ufo'
|
||||
import { filename } from 'pathe/utils'
|
||||
@ -102,10 +103,7 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
|
||||
rootDir: nuxt.options.rootDir,
|
||||
composables: nuxt.options.optimization.keyedComposables,
|
||||
}),
|
||||
replace({
|
||||
...Object.fromEntries([';', '(', '{', '}', ' ', '\t', '\n'].map(d => [`${d}global.`, `${d}globalThis.`])),
|
||||
preventAssignment: true,
|
||||
}),
|
||||
replace({ preventAssignment: true, ...globalThisReplacements }),
|
||||
virtual(nuxt.vfs),
|
||||
],
|
||||
server: {
|
||||
@ -164,10 +162,16 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
|
||||
await nuxt.callHook('vite:extend', ctx)
|
||||
|
||||
nuxt.hook('vite:extendConfig', (config) => {
|
||||
config.plugins!.push(replace({
|
||||
preventAssignment: true,
|
||||
...Object.fromEntries(Object.entries(config.define!).filter(([key]) => key.startsWith('import.meta.'))),
|
||||
}))
|
||||
const replaceOptions: RollupReplaceOptions = Object.create(null)
|
||||
replaceOptions.preventAssignment = true
|
||||
|
||||
for (const key in config.define!) {
|
||||
if (key.startsWith('import.meta.')) {
|
||||
replaceOptions[key] = config.define![key]
|
||||
}
|
||||
}
|
||||
|
||||
config.plugins!.push(replace(replaceOptions))
|
||||
})
|
||||
|
||||
if (!ctx.nuxt.options.dev) {
|
||||
@ -224,3 +228,5 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
|
||||
await buildClient(ctx)
|
||||
await buildServer(ctx)
|
||||
}
|
||||
|
||||
const globalThisReplacements = Object.fromEntries([';', '(', '{', '}', ' ', '\t', '\n'].map(d => [`${d}global.`, `${d}globalThis.`]))
|
||||
|
@ -744,6 +744,9 @@ importers:
|
||||
'@types/fs-extra':
|
||||
specifier: 11.0.4
|
||||
version: 11.0.4
|
||||
rollup:
|
||||
specifier: ^4.18.0
|
||||
version: 4.18.0
|
||||
unbuild:
|
||||
specifier: latest
|
||||
version: 2.0.0(sass@1.69.4)(typescript@5.4.5)
|
||||
|
Loading…
Reference in New Issue
Block a user