跪拜 Guibai
← All articles
Frontend · Vue.js · Architecture

How I Made Vite's First Paint 10x Slower by Trying to Optimize It

By 锋行天下 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

This is a concrete, data-backed reminder that frontend build tool defaults are battle-tested across thousands of projects. For Western developers who reflexively tweak Webpack or Vite configs, it's a sharp case study in how well-intentioned optimizations can crater performance — and why measurement should always come before manipulation.

Summary

A Vue3 + Vite project loaded in under a second out of the box. The developer, thinking they could improve caching and structure, added a manualChunks config that merged multiple third-party libraries into a single 2MB vendor file. That file took 6.5 seconds just to download, blocking all other resources and ballooning total load time to 7.5 seconds.

The post breaks down exactly why the optimization backfired: browsers don't handle monolithic JS files well (they block the main thread during parse/compile/execute), the merged vendor destroyed cache granularity (any single dependency update invalidates the whole 2MB chunk), and Vite's built-in splitVendorChunkPlugin already handles per-package splitting optimally for most projects.

The real lesson is a process one: never optimize without data. The developer recommends running Lighthouse first, measuring LCP, and only touching chunk config when there's a proven bottleneck — like a massive library that isn't needed on first screen, or multiple entry points sharing heavy common code.

Takeaways
Vite's default chunk splitting keeps each third-party dependency in its own chunk, enabling parallel loading and fine-grained cache invalidation.
Merging multiple libraries into a single vendor chunk (e.g., vue + vue-router + pinia + axios) can create a 2MB+ file that blocks the main thread for seconds.
A 2MB JS file forces the browser to download, parse, compile, and execute sequentially, occupying the main thread and preventing rendering.
With per-package chunks, a single library update only invalidates that chunk's cache; with a merged vendor, any update invalidates the entire file.
The correct optimization workflow is: user feedback or monitoring alert → Lighthouse/Performance measurement → locate bottleneck → targeted fix → re-measure.
Vite's splitVendorChunkPlugin is a one-line plugin that implements the per-package splitting strategy.
Custom chunk splitting is only warranted when a large library (like ECharts) isn't needed on first screen, or when multiple entry points share substantial common code.
Always validate any chunk config change with real performance data — never optimize based on intuition alone.
Conclusions

The developer's mistake is common: conflating 'structure' with 'performance.' A clean vendor folder in the build output doesn't mean faster loading.

The 10x regression shows that browser loading behavior (parallelism, main-thread blocking) often outweighs the theoretical benefits of cache grouping.

Vite's default strategy is essentially a 'don't make me think' approach that has been validated at scale — overriding it without evidence is a form of premature optimization.

The post implicitly argues that build tool defaults are not just starting points but often the result of extensive real-world tuning by the tool's maintainers.

The most valuable takeaway is meta: the developer learned to distrust their own intuition and to let data drive decisions, a lesson that applies far beyond Vite config.

Concepts & terms
manualChunks
A Rollup configuration option (exposed by Vite) that lets developers manually define how output JavaScript files are split into separate chunks, overriding the default automatic splitting logic.
splitVendorChunkPlugin
A built-in Vite plugin that automatically splits node_modules dependencies into separate chunks per package, enabling parallel loading and granular cache invalidation.
LCP (Largest Contentful Paint)
A Core Web Vital metric that measures the time it takes for the largest visible content element (text, image, video) to render on screen. It's a key indicator of perceived load speed.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗