vite - vue 打包
打包入门
1. 【入门篇】理解 Vite 打包
1.1 Vite 打包的核心:Rollup
这是一个关键认知点:
- 开发环境:Vite 使用 esbuild 进行极速的依赖预构建,并利用浏览器原生的 ES Module (ESM) 支持实现按需编译,所以启动飞快。
- 生产环境:Vite 使用 Rollup 进行打包。
为什么是 Rollup?
Rollup 是一个专注于 JavaScript 库打包的模块打包器,它对 ESM 的支持和 Tree Shaking(摇树优化)能力非常出色,能生成更小、更高效的代码。这比 Webpack 在某些场景下更具优势。
1.2 为什么 Vite 打包快?(开发 vs. 生产)
- 开发快:得益于 No-bundle 开发服务器和 esbuild 的预编译。
- 生产打包快:
- esbuild 预构建:在
vite build开始时,Vite 会先用 esbuild 将node_modules中的依赖打包成 ESM 格式,这个过程非常快。 - Rollup 高效:Rollup 本身就比 Webpack 更快,尤其是在处理纯 JavaScript 项目时。
- 并行处理:Vite 内部会并行处理多个任务,充分利用多核 CPU。
- esbuild 预构建:在
1.3 打包流程概览
- 读取配置:加载
vite.config.js或vite.config.ts。 - 环境变量注入:根据当前模式(
production)加载.env.production等文件,并将变量注入到代码中。 - 依赖预构建:使用 esbuild 将第三方依赖转换为 ESM。
- Rollup 打包:
- 解析入口文件(通常是
index.html)。 - 从入口开始,构建模块依赖图。
- 应用所有配置的插件,对代码进行转换(如 Vue、TS、CSS)。
- 执行 Tree Shaking,移除未使用的代码。
- 进行代码分割,生成多个 chunk。
- 压缩代码(JS, CSS)。
- 解析入口文件(通常是
- 生成产物:将处理后的文件写入到
dist目录(或配置的outDir)。 - 生成 Manifest:如果配置了
build.manifest,会生成一个manifest.json文件,记录了原始文件和打包后文件的映射关系,用于服务端渲染等场景。
2. 【基础篇】上手你的第一次打包
2.1 使用 vite build 命令
在你的 Vite 项目根目录下,打开终端并运行:
# 直接打包,默认为 production 模式
npm run build
# 或者使用 yarn
yarn build
# 或者使用 pnpm
pnpm build
这个命令通常在你的 package.json 中被定义:
{
"scripts": {
"build": "vite build"
}
}
打包完成后,你会看到一个 dist 目录,里面就是最终可以部署到服务器的静态文件。
2.2 理解 vite.config.js 配置文件
这是 Vite 的核心配置文件。一个基础的配置如下:
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
// 其他配置...
})
defineConfig 函数主要为了提供 TypeScript 类型提示,非常推荐使用。
2.3 解析打包产物
打开 dist 目录,你通常会看到:
index.html:你的主入口文件,其中的 JS 和 CSS 引用已经被替换为带有哈希值的文件名。assets/:存放所有打包后的资源文件,如index-a1b2c3d4.js,style-5e6f7g8h.css。- (可选)
manifest.json:如果启用了,会包含文件映射信息。
index.html的变化是关键,它确保了浏览器能正确加载到更新后的资源,解决了缓存问题。
3. 【进阶篇】核心配置与多环境管理
3.1 构建配置详解 (build)
在 vite.config.js 中,build 对象控制着生产构建的所有行为。
// vite.config.js
export default defineConfig({
build: {
// 打包输出目录
outDir: 'dist',
// 静态资源存放目录
assetsDir: 'assets',
// 是否生成 source map 文件
// 'hidden' 会生成 .map 文件,但不在 JS 文件中引用,用于错误上报
sourcemap: false, // true | 'hidden' | 'inline'
// 构建后是否生成 manifest.json
manifest: false, // true | string | object
// chunk 大小警告的限制 (kb)
chunkSizeWarningLimit: 500,
// Rollup 配置,可以覆盖 Vite 内部 Rollup 的默认配置
rollupOptions: {
// 入口文件,可以是字符串、对象或数组
input: 'src/main.js', // 或 { main: 'src/main.js', admin: 'src/admin/main.js' }
// 输出配置
output: {
// JS 文件命名格式
chunkFileNames: 'assets/js/[name]-[hash].js',
entryFileNames: 'assets/js/[name]-[hash].js',
// 资源文件命名格式 (图片, 字体等)
assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
// 手动代码分割
manualChunks: (id) => {
// 将 node_modules 中的依赖单独打包
if (id.includes('node_modules')) {
// 例如,将 vue 和 vue-router 打包到一个 chunk
if (id.toString().includes('vue') || id.toString().includes('vue-router')) {
return 'vendor-vue';
}
// 其他依赖
return 'vendor';
}
}
}
},
// 压缩配置
// Vite 5+ 默认使用 esbuild 压缩 JS,CSS 使用 Lightning CSS
// 你也可以切换回 Terser
minify: 'esbuild', // 'terser' | 'esbuild'
terserOptions: {
// Terser 的具体配置
}
}
})
3.2 环境变量与模式
Vite 内置了三种模式:
development(开发)production(生产)test(测试)
你可以通过.env文件来管理不同环境的变量。.env:所有模式都会加载。.env.local:所有模式都会加载,但会被 git 忽略。.env.development:npm run dev时加载。.env.production:npm run build时加载。
注意:只有以VITE_开头的变量才会暴露给客户端代码。
# .env.production
VITE_APP_TITLE=My Awesome App
VITE_API_BASE_URL=https://api.production.com
在代码中使用:
console.log(import.meta.env.VITE_APP_TITLE); // "My Awesome App"
3.3 代码分割
Vite 默认会进行很好的代码分割,主要有两种策略:
- 动态导入:这是最常用也是最推荐的方式。
// 路由懒加载 const routes = [ { path: '/about', component: () => import('./views/About.vue') // About.vue 会被单独打包成一个 chunk } ] // 条件加载 if (someCondition) { import('./some-heavy-library').then(module => { // 使用模块 }) } - 手动分割:通过
build.rollupOptions.output.manualChunks配置,如上例所示,将node_modules中的不同库拆分成不同的 vendor chunk,可以更好地利用浏览器缓存。
4. 【精通篇】高级优化与工程化
4.1 性能优化
- 资源压缩:
- JS:默认使用
esbuild,速度极快。如果需要更精细的控制(如移除console和debugger),可以切换到terser并配置terserOptions。 - CSS:默认使用
Lightning CSS,非常快。也可以通过插件(如vite-plugin-purgecss)移除未使用的 CSS。
- JS:默认使用
- Tree Shaking:Vite/Rollup 默认开启。确保你的库是 ESM 格式,并且在
package.json中设置了sideEffects: false,以获得最佳效果。 - 预加载:Vite 会自动为入口 chunk 和动态导入的 chunk 生成
<link rel="modulepreload">标签,让浏览器提前加载资源,提升页面加载速度。
4.2 处理不同类型的资源
- SVG:
- 作为图片:直接
import logoUrl from './logo.svg',然后用在img标签的src中。 - 作为组件:使用
vite-plugin-svgr插件,可以直接import { ReactComponent as Logo } from './logo.svg'(在 React 中) 或import Logo from './logo.svg?component'(在 Vue 3 中)。
- 作为图片:直接
- 图片:Vite 内置了对图片资源的处理,可以直接导入获取 URL。小于
assetsInlineLimit(默认 4kb)的图片会被转为 base64 Data URL。 - CSS:
- CSS Modules:
import styles from './style.module.css'。 - 预处理器:安装对应的预处理器(如
sass),Vite 会自动识别.scss/.sass文件。 - PostCSS:Vite 内置,只需安装插件(如
autoprefixer,tailwindcss)并创建postcss.config.js即可。
- CSS Modules:
4.3 多页面应用 配置
MPA 的核心是配置多个入口。
// vite.config.js
import { defineConfig } from 'vite'
import { resolve } from 'path'
export default defineConfig({
build: {
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html'),
admin: resolve(__dirname, 'admin/index.html'),
},
},
},
})
你需要确保每个入口 HTML 文件都正确地引用了它自己的 JS 入口。
4.4 库模式 打包
如果你想用 Vite 打包一个 JS 库供其他项目使用,可以使用库模式。
vite build --mode lib --file my-lib.js
更推荐在 vite.config.js 中配置:
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [vue()],
build: {
lib: {
// 库的入口文件
entry: resolve(__dirname, 'src/index.js'),
// 库的名称
name: 'MyAwesomeLib',
// 打包后的文件名
fileName: (format) => `my-awesome-lib.${format}.js`
},
rollupOptions: {
// 确保外部化处理那些你不想打包进库的依赖
external: ['vue'],
output: {
// 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
globals: {
vue: 'Vue'
}
}
}
}
})
这会生成 es 和 umd 格式的文件,方便不同环境使用。
4.5 插件系统
Vite 的插件 API 简单而强大。一个插件就是一个对象,包含 name 和各种钩子函数(如 config, configureServer, buildStart 等)。
常用插件推荐:
@vitejs/plugin-vue/@vitejs/plugin-react:官方框架支持。vite-plugin-windicss/tailwindcss:原子化 CSS 框架。vite-plugin-pwa:添加 PWA 能力。vite-plugin-compression2:生成.gz/.br压缩文件。vite-plugin-eslint:在构建时进行 ESLint 检查。
4.6 与 CI/CD 集成
Vite 的构建过程非常简单,易于集成到任何 CI/CD 流程中。
GitHub Actions 示例:
name: Build and Deploy
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm' # 或 'yarn', 'pnpm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
5. 【实战篇】常见问题与解决方案
5.1 打包后页面空白?
- 检查控制台错误:这是最重要的第一步!通常是 JS 加载或执行错误。
- 路由模式:如果你使用了
history模式的路由(如 Vue Router 的createWebHistory),服务器需要配置回退到index.html。否则刷新页面会 404。 - 资源路径:检查
base配置是否正确。如果部署在子目录,必须设置base。
5.2 资源路径 404?
- 绝对路径 vs. 相对路径:
base: '/'(默认):生成绝对路径,如/assets/logo.png。适合部署在根域名。base: './':生成相对路径,如./assets/logo.png。适合部署在任何子目录,但可能不适用于所有场景。
- 部署子目录:如果部署在
https://example.com/my-app/,base必须设置为/my-app/。
5.3 内存溢出 (OOM)?
对于超大型项目,Node.js 的默认内存可能不够用。
# 增加 Node.js 内存限制
export NODE_OPTIONS="--max-old-space-size=8192"
npm run build
或者在 package.json 中修改:
{
"scripts": {
"build": "node --max-old-space-size=8192 ./node_modules/.bin/vite build"
}
}
5.4 如何兼容旧版浏览器?
Vite 默认目标是支持原生 ESM 的现代浏览器。要兼容旧版浏览器(如 IE11),你需要:
- 官方插件
@vitejs/plugin-legacy:npm install -D @vitejs/plugin-legacy - 配置插件:
这个插件会:// vite.config.js import legacy from '@vitejs/plugin-legacy' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ legacy({ targets: ['defaults', 'not IE 11'] // 设置目标浏览器 }) ] })- 为现代浏览器生成 ESM 格式的 chunk。
- 为旧版浏览器生成对应的 ES5 格式的 chunk,并注入必要的 polyfills(如
core-js)。 - 在
index.html中添加type="module"和nomodule的script标签,让浏览器自动选择合适的版本。
6. 总结
Vite 的打包系统凭借其 Rollup 核心、开箱即用的优化 和 极致的开发体验,已经成为现代前端工程化的首选工具之一。
- 入门:掌握
vite build和基本的vite.config.js配置。 - 进阶:深入理解
build配置项,学会使用环境变量和手动代码分割。 - 精通:能够进行性能调优、处理复杂资源、配置 MPA/库模式,并能通过插件和 CI/CD 将 Vite 融入完整的工程化体系。
希望这份指南能帮助你彻底掌握 Vite 打包,构建出高性能、可维护的现代 Web 应用。祝你编码愉快!
打包优化
本文将针对依赖预构建、代码分割、资源压缩、Tree Shaking、缓存策略、图片优化、字体优化七大核心功能,从原理、配置、实战示例、注意事项四个维度进行详细拆解,结合 Vue3 项目实际场景给出可落地的配置方案。
模块一:依赖预构建(优化 node_modules 处理)
核心结论
依赖预构建是 Vite 针对 node_modules 中第三方依赖的性能优化手段,通过将零散的 ESM 依赖打包为单文件、转换非 ESM 依赖为 ESM 格式,解决了依赖加载的网络请求过多、浏览器兼容性问题,是 Vite 开发环境和生产环境打包的基础优化。
原理讲解
-
为什么需要依赖预构建?
- 第三方依赖(如 Vue、axios、lodash)通常会被拆分为多个小的 ESM 模块,直接在浏览器中加载会产生大量网络请求,导致页面加载缓慢;
- 部分依赖仍使用 CommonJS/UMD 格式,浏览器原生 ESM 无法直接识别,需要转换为 ESM 格式。
-
Vite 依赖预构建的执行逻辑
- 检测阶段:Vite 启动时自动扫描项目中导入的依赖(如
import Vue from 'vue'),识别出需要预构建的依赖; - 构建阶段:通过 Esbuild 将依赖打包为单个 ESM 模块文件,输出到
node_modules/.vite/deps目录; - 缓存阶段:预构建产物会被缓存,只有当依赖版本变化、
vite.config.js修改或package.json变动时,才会重新预构建。
- 检测阶段:Vite 启动时自动扫描项目中导入的依赖(如
-
对 Vue3 项目的特殊优化
Vite 会优先预构建 Vue3 及其生态依赖(如vue-router、pinia),确保 Vue 相关代码的加载速度,同时兼容 Vue3 的 ESM 模块格式。
配置示例
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
// 依赖预构建核心配置
optimizeDeps: {
// 1. 指定需要强制预构建的依赖(自动检测不生效时使用)
include: ['vue', 'vue-router', 'pinia', 'axios', 'lodash-es'],
// 2. 指定不需要预构建的依赖(如已为 ESM 格式且体积小的依赖)
exclude: ['small-esm-package'],
// 3. 自定义 Esbuild 选项(如添加插件)
esbuildOptions: {
target: 'es2020' // 指定编译目标,适配更多浏览器
},
// 4. 强制重新预构建(开发环境调试时使用)
force: false // 设为 true 会忽略缓存,每次启动都重新预构建
}
})
注意事项
【重点】node_modules/.vite 目录是预构建产物的缓存目录,若出现依赖加载异常(如依赖版本更新后代码未生效),可删除该目录后重启 Vite。
【避坑提醒】对于通过 CDN 引入的依赖(如在 index.html 中通过 <script> 引入 Vue),需在 optimizeDeps.exclude 中排除,避免重复预构建。
模块二:代码分割(实现按需加载)
核心结论
代码分割是将项目代码拆分为多个小的 chunk(代码块),实现按需加载(首屏只加载必要代码,其他代码在需要时再加载),是降低 Vue3 项目首屏加载时间的核心手段,Vite 基于 Rollup 实现了灵活的代码分割能力。
原理讲解
-
代码分割的核心场景
- 路由级分割:Vue Router 路由懒加载,将不同路由的组件拆分为独立 chunk;
- 依赖级分割:将第三方依赖(如 Vue、Pinia)拆分为独立的 vendor chunk;
- 组件级分割:将大型组件(如弹窗、表格)拆分为独立 chunk,按需加载。
-
Vite 代码分割的实现方式
- 自动分割:Rollup 会自动将被多个模块共享的代码拆分为公共 chunk;
- 手动分割:通过
build.rollupOptions.output.manualChunks自定义分割规则; - 动态导入:通过
import()语法实现动态加载(如路由懒加载)。
-
Vue3 项目的代码分割优势
Vue3 的组合式 API(Composition API)更利于代码分割,Rollup 可更精准地识别未使用的代码,结合代码分割进一步减小包体积。
配置示例
1. 路由级代码分割(Vue Router 懒加载)
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
// 动态导入实现路由懒加载,每个路由组件生成独立 chunk
const Home = () => import(/* webpackChunkName: "home" */ '../views/Home.vue')
const ProductList = () => import(/* webpackChunkName: "product" */ '../views/ProductList.vue')
const Cart = () => import(/* webpackChunkName: "cart" */ '../views/Cart.vue')
const UserCenter = () => import(/* webpackChunkName: "user" */ '../views/UserCenter.vue')
const routes = [
{ path: '/', component: Home },
{ path: '/products', component: ProductList },
{ path: '/cart', component: Cart },
{ path: '/user', component: UserCenter }
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
})
export default router
2. 依赖级与自定义代码分割(vite.config.js)
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build: {
rollupOptions: {
output: {
// 配置 chunk 文件名格式(添加哈希值用于缓存)
chunkFileNames: 'static/js/[name]-[hash:8].js',
entryFileNames: 'static/js/[name]-[hash:8].js',
// 手动代码分割规则
manualChunks: {
// 1. 将 Vue 生态依赖打包为独立 chunk
vueVendor: ['vue', 'vue-router', 'pinia'],
// 2. 将工具类依赖打包为独立 chunk
utilsVendor: ['axios', 'lodash-es', 'dayjs'],
// 3. 将业务通用组件打包为独立 chunk
components: ['src/components/BaseTable.vue', 'src/components/BaseModal.vue'],
// 4. 动态分割 node_modules 中的依赖
...((id) => {
if (id.includes('node_modules')) {
// 将不同的第三方库拆分为不同的 chunk
return id.split('node_modules/')[1].split('/')[0]
}
})
}
}
}
}
})
注意事项
【重点】路由懒加载时可通过 /* webpackChunkName: "xxx" */ 注释自定义 chunk 名称,便于打包后分析。
【避坑提醒】不要过度分割代码,过多的 chunk 会导致浏览器发起更多网络请求,反而降低性能(建议一个 chunk 体积不小于 10KB)。
模块三:资源压缩(启用 Gzip/Brotli 压缩)
核心结论
资源压缩是通过 Gzip 或 Brotli 算法对打包后的 JS、CSS、HTML 等文件进行压缩,可减少文件体积 60%-80%,大幅提升资源加载速度。Vite 可通过插件实现打包时生成压缩文件,也可通过服务器配置实现运行时压缩。
原理讲解
-
Gzip 与 Brotli 对比
特性 Gzip Brotli 压缩率 较高 更高(比 Gzip 高 10%-20%) 压缩速度 较快 稍慢 浏览器支持 全支持 现代浏览器均支持(Chrome 50+、Firefox 44+) 服务器支持 广泛 Nginx 1.13.9+、Apache 2.4.26+ 支持 -
Vite 实现资源压缩的两种方式
- 打包时压缩:通过
vite-plugin-compression插件在npm run build时生成.gz或.br压缩文件,部署时直接提供压缩文件; - 运行时压缩:服务器(如 Nginx)在接收到请求时,实时对文件进行压缩后返回(性能略低于打包时压缩)。
- 打包时压缩:通过
配置示例
1. 打包时生成 Gzip + Brotli 压缩文件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import compression from 'vite-plugin-compression' // 需先安装:npm i vite-plugin-compression -D
export default defineConfig({
plugins: [
vue(),
// Gzip 压缩配置
compression({
verbose: true, // 是否在控制台输出压缩结果
disable: false, // 是否禁用压缩
threshold: 10240, // 仅压缩大于 10KB 的文件
algorithm: 'gzip', // 压缩算法
ext: '.gz', // 压缩文件后缀
deleteOriginFile: false // 是否删除原文件(建议保留,便于回退)
}),
// Brotli 压缩配置
compression({
verbose: true,
disable: false,
threshold: 10240,
algorithm: 'brotliCompress', // Brotli 压缩算法
ext: '.br',
deleteOriginFile: false
})
]
})
2. Nginx 配置运行时压缩(推荐生产环境使用)
server {
listen 80;
server_name your-domain.com;
root /usr/share/nginx/html;
# 启用 Gzip 压缩
gzip on;
gzip_types text/plain text/css application/javascript application/json application/vue+json;
gzip_min_length 10240;
gzip_comp_level 6;
# 启用 Brotli 压缩(需 Nginx 编译时加入 Brotli 模块)
brotli on;
brotli_types text/plain text/css application/javascript application/json application/vue+json;
brotli_min_length 10240;
brotli_comp_level 6;
# 优先返回 Brotli 压缩文件
location ~* \.(js|css|html)$ {
add_header Vary Accept-Encoding;
if ($http_accept_encoding ~* br) {
try_files $uri.br $uri =404;
add_header Content-Encoding br;
default_type application/javascript;
}
if ($http_accept_encoding ~* gzip) {
try_files $uri.gz $uri =404;
add_header Content-Encoding gzip;
default_type application/javascript;
}
}
# 前端路由配置
try_files $uri $uri/ /index.html;
}
注意事项
【重点】建议同时生成 Gzip 和 Brotli 压缩文件,服务器根据浏览器的 Accept-Encoding 请求头返回对应的压缩文件,兼顾兼容性和压缩率。
【避坑提醒】不要对图片、视频等已压缩的资源进行 Gzip/Brotli 压缩,不仅不会减小体积,还可能增大体积并消耗服务器资源。
模块四:Tree Shaking(移除未使用的代码)
核心结论
Tree Shaking 是静态代码分析技术,通过识别并移除项目中未被使用的代码(死代码),可减少 Vue3 项目打包体积 10%-30%。Vite 基于 Rollup 实现了开箱即用的 Tree Shaking,对 Vue3 的 Composition API 支持尤为友好。
原理讲解
-
Tree Shaking 的工作条件
- 代码必须采用 ESM 模块格式(
import/export),CommonJS 模块(require)不支持; - 打包工具需开启生产环境优化(Vite 生产环境默认开启);
- 代码不能有副作用(如修改全局变量、动态导入),否则无法被安全移除。
- 代码必须采用 ESM 模块格式(
-
Vite + Vue3 的 Tree Shaking 优势
- Vue3 采用 ESM 格式编写,且 Composition API 是函数式设计,更易被 Tree Shaking 分析;
- Rollup 可精准识别 Vue 组件中未使用的方法、属性和样式;
- Pinia 的状态管理逻辑会被自动 Tree Shaking,仅打包使用的 store 和 action。
-
如何标记代码无副作用
在package.json中添加sideEffects字段,告诉 Rollup 哪些文件有副作用,哪些没有:{ "sideEffects": [ "*.css", // CSS 文件有副作用(样式会影响全局) "*.scss", "src/utils/global.js" // 全局初始化文件有副作用 ] }
配置示例
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build: {
// Tree Shaking 相关配置(Vite 生产环境默认开启,可显式配置)
target: 'es2020', // 支持 ES 模块的树摇优化
cssCodeSplit: true, // 对 CSS 进行 Tree Shaking,移除未使用的样式
rollupOptions: {
treeshake: {
// 开启严格的 Tree Shaking(默认 true)
moduleSideEffects: false,
// 识别并移除未使用的全局变量
propertyReadSideEffects: false
}
}
}
})
实战示例:Vue3 中 Tree Shaking 的效果
// src/utils/format.js
export const formatDate = (date) => {
return new Date(date).toLocaleDateString()
}
export const formatPrice = (price) => {
return `¥${price.toFixed(2)}`
}
// src/components/Product.vue
import { formatPrice } from '../utils/format.js'
// 仅使用 formatPrice,formatDate 会被 Tree Shaking 移除
export default {
setup() {
const price = formatPrice(99.9)
return { price }
}
}
注意事项
【重点】避免在代码中使用 import * as utils from './utils' 这种整体导入方式,会导致 Tree Shaking 失效,应使用按需导入(import { xxx } from './utils')。
【避坑提醒】对于有副作用的代码(如 import './style.css'),需在 package.json 的 sideEffects 中声明,否则会被 Tree Shaking 误删。
模块五:缓存策略(优化浏览器缓存)
核心结论
缓存策略是通过文件名哈希和HTTP 缓存头的配合,让浏览器缓存静态资源(如 JS、CSS、图片),仅在资源更新时重新请求,大幅减少重复请求,提升页面加载速度。
原理讲解
-
缓存的两个核心维度
- 强缓存:浏览器通过
Cache-Control、Expires头判断资源是否过期,过期前直接从本地缓存读取,不发起请求; - 协商缓存:资源过期后,浏览器发起请求携带
Last-Modified或ETag头,服务器判断资源是否更新,未更新则返回 304,浏览器使用本地缓存。
- 强缓存:浏览器通过
-
Vite 实现缓存的关键手段
- 文件名哈希:为静态资源添加唯一哈希值(如
app-123456.js),资源更新时哈希值变化,浏览器视为新资源; - 分离不变资源:将第三方依赖、公共组件等不常更新的资源拆分为独立 chunk,长期缓存;
- HTML 不缓存:HTML 文件作为入口,设置为不缓存,确保每次请求都能获取最新的资源引用。
- 文件名哈希:为静态资源添加唯一哈希值(如
配置示例
1. Vite 配置文件名哈希
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build: {
rollupOptions: {
output: {
// 为不同类型的资源设置哈希格式
// [name]:模块名,[hash:8]:8位哈希值,[ext]:文件后缀
entryFileNames: 'static/js/[name]-[hash:8].js',
chunkFileNames: 'static/js/[name]-[hash:8].js',
assetFileNames: 'static/[ext]/[name]-[hash:8].[ext]'
}
},
// 关闭生产环境 sourcemap(减少文件体积,避免哈希值因 sourcemap 变化)
sourcemap: false
}
})
2. Nginx 配置 HTTP 缓存头
server {
listen 80;
server_name your-domain.com;
root /usr/share/nginx/html;
# 1. 静态资源(JS、CSS、图片、字体)设置强缓存,缓存1年
location ~* \.(js|css|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
add_header ETag $request_filename; # 添加 ETag 用于协商缓存
}
# 2. HTML 文件设置不缓存,确保每次获取最新
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
}
# 3. 前端路由配置
try_files $uri $uri/ /index.html;
}
注意事项
【重点】immutable 头表示资源在缓存期内不会变化,浏览器无需发起协商请求,进一步提升性能。
【避坑提醒】若项目需要频繁更新,可适当缩短静态资源的缓存时间(如 1 个月),避免用户长时间看到旧版本。
模块六:图片优化(压缩和格式优化)
核心结论
图片是 Vue3 项目中体积占比最高的资源之一,通过压缩图片体积、转换为更优格式(WebP/AVIF)、按需加载图片等手段,可显著减少图片资源的加载时间。Vite 可通过插件实现图片的自动化优化。
原理讲解
-
图片格式优化对比
格式 特点 压缩率 浏览器支持 JPG 有损压缩,适合照片 中等 全支持 PNG 无损压缩,适合图标/插画 较低 全支持 WebP 谷歌推出,兼顾有损/无损 高(比 JPG 小 25%-35%) 现代浏览器支持 AVIF 新一代格式,压缩率更高 极高(比 WebP 小 20%-30%) Chrome 85+、Firefox 91+ -
Vite 图片优化的实现方式
- 压缩:通过
vite-plugin-imagemin插件对图片进行无损/有损压缩; - 格式转换:自动将 JPG/PNG 转换为 WebP/AVIF 格式,通过
<picture>标签实现降级兼容; - 内联小图片:将小于指定体积的图片转换为 Base64 格式,减少网络请求。
- 压缩:通过
配置示例
1. 图片压缩与格式转换(vite.config.js)
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import imagemin from 'vite-plugin-imagemin' // 需先安装:npm i vite-plugin-imagemin -D
export default defineConfig({
plugins: [
vue(),
// 图片优化配置
imagemin({
// 通用配置
verbose: true, // 输出压缩日志
disable: false, // 是否禁用
// 不同图片格式的压缩配置
gifsicle: { optimizationLevel: 7 }, // GIF 压缩级别(1-7)
optipng: { optimizationLevel: 7 }, // PNG 压缩级别(0-7)
mozjpeg: { quality: 80 }, // JPG 质量(0-100)
pngquant: { quality: [0.8, 0.9], speed: 4 }, // PNG 有损压缩
webp: { quality: 80 }, // WebP 质量
avif: { quality: 80 } // AVIF 质量
})
],
build: {
// 内联小图片(小于 4KB 的图片转为 Base64)
assetsInlineLimit: 4096
}
})
2. Vue3 组件中使用 WebP/AVIF 图片(兼容降级)
<template>
<picture>
<!-- 优先加载 AVIF 格式 -->
<source srcset="/static/images/product.avif" type="image/avif">
<!-- 其次加载 WebP 格式 -->
<source srcset="/static/images/product.webp" type="image/webp">
<!-- 降级加载 JPG 格式 -->
<img src="/static/images/product.jpg" alt="产品图片" loading="lazy">
</picture>
</template>
注意事项
【重点】loading="lazy" 可实现图片懒加载,仅当图片进入视口时才加载,提升首屏性能。
【避坑提醒】在 Windows 系统中安装 vite-plugin-imagemin 可能需要安装额外的编译工具(如 Python),若安装失败可使用 vite-plugin-imagemin 的国内镜像源。
模块七:字体优化(字体子集和预加载)
核心结论
字体文件通常体积较大(中文字体可达数 MB),通过提取字体子集(只保留项目中使用的字符)、预加载关键字体、使用现代字体格式(WOFF2) 等手段,可大幅减少字体加载时间,避免页面出现“字体闪烁”(FOUT)问题。
原理讲解
-
字体格式优化
- WOFF2:目前最优的字体格式,压缩率比 WOFF 高 30%,现代浏览器均支持;
- WOFF:兼容旧版浏览器(如 IE 9+);
- TTF/OTF:原始字体格式,体积大,不建议直接使用。
-
字体子集提取
使用工具(如glyphhanger、font-spider)分析项目中使用的字符,只保留这些字符生成精简的字体文件,可将中文字体体积从数 MB 减小到几十 KB。 -
字体预加载
通过<link rel="preload">标签预加载首屏所需的字体文件,确保字体在页面渲染前加载完成,避免字体闪烁。
配置示例
1. Vite 配置字体处理(vite.config.js)
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build: {
rollupOptions: {
output: {
// 字体文件输出目录和命名
assetFileNames: 'static/fonts/[name]-[hash:8].[ext]'
}
}
},
// 配置字体文件的解析
resolve: {
alias: {
'@fonts': '/src/assets/fonts'
}
}
})
2. 提取字体子集(使用 font-spider 工具)
- 安装:
npm i font-spider -g - 创建 HTML 文件(列出项目中使用的字符):
<!DOCTYPE html> <html> <head> <style> @font-face { font-family: 'PingFang SC'; src: url('./src/assets/fonts/PingFangSC-Regular.woff2') format('woff2'); } body { font-family: 'PingFang SC'; } </style> </head> <body> <!-- 项目中使用的所有字符 --> 首页 产品 购物车 我的 登录 注册 价格 数量 加入购物车 </body> </html> - 执行命令:
font-spider index.html,工具会自动提取子集并替换原字体文件。
3. Vue3 项目中预加载字体(index.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Vue3 电商项目</title>
<!-- 预加载首屏关键字体 -->
<link rel="preload" href="/static/fonts/PingFangSC-Regular-12345678.woff2" as="font" type="font/woff2" crossorigin>
<!-- 预获取非首屏字体 -->
<link rel="prefetch" href="/static/fonts/PingFangSC-Bold-87654321.woff2" as="font" type="font/woff2" crossorigin>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
4. 样式中使用优化后的字体
// src/styles/fonts.scss
@font-face {
font-family: 'PingFang SC';
src: url('@fonts/PingFangSC-Regular.woff2') format('woff2'),
url('@fonts/PingFangSC-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap; // 字体加载时使用备用字体,加载完成后替换(避免FOUT)
}
body {
font-family: 'PingFang SC', sans-serif;
}
注意事项
【重点】font-display: swap 是解决字体闪烁的关键,建议所有自定义字体都添加该属性。
【避坑提醒】预加载字体时必须添加 crossorigin 属性,否则浏览器会忽略预加载请求(字体文件属于跨域资源,即使在同一域名下)。
总结:七大功能的协同优化思路
- 基础层:依赖预构建优化第三方依赖加载,为后续打包打下基础;
- 分割层:代码分割实现按需加载,降低首屏体积;
- 精简层:Tree Shaking 移除死代码,进一步减小包体积;
- 压缩层:资源压缩(Gzip/Brotli)、图片优化、字体优化减少传输体积;
- 缓存层:缓存策略确保用户重复访问时无需重新加载资源。
通过这七大功能的协同配置,可将 Vue3 + Vite 项目的首屏加载时间降低 50% 以上,大幅提升用户体验。

浙公网安备 33010602011771号