惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
D
DataBreaches.Net
F
Fortinet All Blogs
阮一峰的网络日志
阮一峰的网络日志
博客园_首页
Apple Machine Learning Research
Apple Machine Learning Research
H
Help Net Security
M
MIT News - Artificial intelligence
美团技术团队
人人都是产品经理
人人都是产品经理
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
The Cloudflare Blog
有赞技术团队
有赞技术团队
L
LangChain Blog
博客园 - Franky
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 【当耐特】
S
SegmentFault 最新的问题
V
Visual Studio Blog
Blog — PlanetScale
Blog — PlanetScale
Hugging Face - Blog
Hugging Face - Blog
B
Blog
I
InfoQ

博客园 - 【唐】三三

《python编程从入门到实践》15 生成数据 《python编程从入门到实践》10-11章 文件和异常、测试代码 《python编程从入门到实践》8-9章 函数、类 fastcode 代码理解 AI 框架 Oh-My-OpenCode 3.5.6 完整使用指南 AI工具免费模型排名对比 (2026-02-12) ABP演示 - 分布式 Event Bus(使用rabbitmq) ABP演示 - 工作单元 (UOW) ABP演示 - 异常处理 ABP演示 - 缓存 APP - 实体变更监视事件 [EntityCreatedEventData、EntityUpdatedEventData、EntityDeletedEventData] ABP演示 - 授权 ABP演示 - 本地Event Bus ABP演示 - 实现选项 OPtion ABP演示 - 简单的实现DDD LINQ:SelectMany LINQ - Concat、Union、Intersect、Except IEqualityComparer Nginx - 内置变量 vue - 以deifne开头的 API vue - 进阶
vite - vue 打包
【唐】三三 · 2025-12-17 · via 博客园 - 【唐】三三

打包入门

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 的预编译。
  • 生产打包快
    1. esbuild 预构建:在 vite build 开始时,Vite 会先用 esbuild 将 node_modules 中的依赖打包成 ESM 格式,这个过程非常快。
    2. Rollup 高效:Rollup 本身就比 Webpack 更快,尤其是在处理纯 JavaScript 项目时。
    3. 并行处理:Vite 内部会并行处理多个任务,充分利用多核 CPU。

1.3 打包流程概览

  1. 读取配置:加载 vite.config.jsvite.config.ts
  2. 环境变量注入:根据当前模式(production)加载 .env.production 等文件,并将变量注入到代码中。
  3. 依赖预构建:使用 esbuild 将第三方依赖转换为 ESM。
  4. Rollup 打包
    • 解析入口文件(通常是 index.html)。
    • 从入口开始,构建模块依赖图。
    • 应用所有配置的插件,对代码进行转换(如 Vue、TS、CSS)。
    • 执行 Tree Shaking,移除未使用的代码。
    • 进行代码分割,生成多个 chunk。
    • 压缩代码(JS, CSS)。
  5. 生成产物:将处理后的文件写入到 dist 目录(或配置的 outDir)。
  6. 生成 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.developmentnpm run dev 时加载。
  • .env.productionnpm 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 默认会进行很好的代码分割,主要有两种策略:

  1. 动态导入:这是最常用也是最推荐的方式。
    // 路由懒加载
    const routes = [
      {
        path: '/about',
        component: () => import('./views/About.vue') // About.vue 会被单独打包成一个 chunk
      }
    ]
    // 条件加载
    if (someCondition) {
      import('./some-heavy-library').then(module => {
        // 使用模块
      })
    }
    
  2. 手动分割:通过 build.rollupOptions.output.manualChunks 配置,如上例所示,将 node_modules 中的不同库拆分成不同的 vendor chunk,可以更好地利用浏览器缓存。

4. 【精通篇】高级优化与工程化

4.1 性能优化

  • 资源压缩
    • JS:默认使用 esbuild,速度极快。如果需要更精细的控制(如移除 consoledebugger),可以切换到 terser 并配置 terserOptions
    • CSS:默认使用 Lightning CSS,非常快。也可以通过插件(如 vite-plugin-purgecss)移除未使用的 CSS。
  • 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 Modulesimport styles from './style.module.css'
    • 预处理器:安装对应的预处理器(如 sass),Vite 会自动识别 .scss/.sass 文件。
    • PostCSS:Vite 内置,只需安装插件(如 autoprefixer, tailwindcss)并创建 postcss.config.js 即可。

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'
        }
      }
    }
  }
})

这会生成 esumd 格式的文件,方便不同环境使用。

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),你需要:

  1. 官方插件 @vitejs/plugin-legacy
    npm install -D @vitejs/plugin-legacy
    
  2. 配置插件
    // 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"nomodulescript 标签,让浏览器自动选择合适的版本。

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 开发环境和生产环境打包的基础优化。

原理讲解

  1. 为什么需要依赖预构建?

    • 第三方依赖(如 Vue、axios、lodash)通常会被拆分为多个小的 ESM 模块,直接在浏览器中加载会产生大量网络请求,导致页面加载缓慢;
    • 部分依赖仍使用 CommonJS/UMD 格式,浏览器原生 ESM 无法直接识别,需要转换为 ESM 格式。
  2. Vite 依赖预构建的执行逻辑

    • 检测阶段:Vite 启动时自动扫描项目中导入的依赖(如 import Vue from 'vue'),识别出需要预构建的依赖;
    • 构建阶段:通过 Esbuild 将依赖打包为单个 ESM 模块文件,输出到 node_modules/.vite/deps 目录;
    • 缓存阶段:预构建产物会被缓存,只有当依赖版本变化、vite.config.js 修改或 package.json 变动时,才会重新预构建。
  3. 对 Vue3 项目的特殊优化
    Vite 会优先预构建 Vue3 及其生态依赖(如 vue-routerpinia),确保 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 实现了灵活的代码分割能力。

原理讲解

  1. 代码分割的核心场景

    • 路由级分割:Vue Router 路由懒加载,将不同路由的组件拆分为独立 chunk;
    • 依赖级分割:将第三方依赖(如 Vue、Pinia)拆分为独立的 vendor chunk;
    • 组件级分割:将大型组件(如弹窗、表格)拆分为独立 chunk,按需加载。
  2. Vite 代码分割的实现方式

    • 自动分割:Rollup 会自动将被多个模块共享的代码拆分为公共 chunk;
    • 手动分割:通过 build.rollupOptions.output.manualChunks 自定义分割规则;
    • 动态导入:通过 import() 语法实现动态加载(如路由懒加载)。
  3. 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 可通过插件实现打包时生成压缩文件,也可通过服务器配置实现运行时压缩。

原理讲解

  1. Gzip 与 Brotli 对比

    特性 Gzip Brotli
    压缩率 较高 更高(比 Gzip 高 10%-20%)
    压缩速度 较快 稍慢
    浏览器支持 全支持 现代浏览器均支持(Chrome 50+、Firefox 44+)
    服务器支持 广泛 Nginx 1.13.9+、Apache 2.4.26+ 支持
  2. 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 支持尤为友好。

原理讲解

  1. Tree Shaking 的工作条件

    • 代码必须采用 ESM 模块格式(import/export),CommonJS 模块(require)不支持;
    • 打包工具需开启生产环境优化(Vite 生产环境默认开启);
    • 代码不能有副作用(如修改全局变量、动态导入),否则无法被安全移除。
  2. Vite + Vue3 的 Tree Shaking 优势

    • Vue3 采用 ESM 格式编写,且 Composition API 是函数式设计,更易被 Tree Shaking 分析;
    • Rollup 可精准识别 Vue 组件中未使用的方法、属性和样式;
    • Pinia 的状态管理逻辑会被自动 Tree Shaking,仅打包使用的 store 和 action。
  3. 如何标记代码无副作用
    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.jsonsideEffects 中声明,否则会被 Tree Shaking 误删。

模块五:缓存策略(优化浏览器缓存)

核心结论
缓存策略是通过文件名哈希HTTP 缓存头的配合,让浏览器缓存静态资源(如 JS、CSS、图片),仅在资源更新时重新请求,大幅减少重复请求,提升页面加载速度。

原理讲解

  1. 缓存的两个核心维度

    • 强缓存:浏览器通过 Cache-ControlExpires 头判断资源是否过期,过期前直接从本地缓存读取,不发起请求;
    • 协商缓存:资源过期后,浏览器发起请求携带 Last-ModifiedETag 头,服务器判断资源是否更新,未更新则返回 304,浏览器使用本地缓存。
  2. 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 可通过插件实现图片的自动化优化。

原理讲解

  1. 图片格式优化对比

    格式 特点 压缩率 浏览器支持
    JPG 有损压缩,适合照片 中等 全支持
    PNG 无损压缩,适合图标/插画 较低 全支持
    WebP 谷歌推出,兼顾有损/无损 高(比 JPG 小 25%-35%) 现代浏览器支持
    AVIF 新一代格式,压缩率更高 极高(比 WebP 小 20%-30%) Chrome 85+、Firefox 91+
  2. 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)问题。

原理讲解

  1. 字体格式优化

    • WOFF2:目前最优的字体格式,压缩率比 WOFF 高 30%,现代浏览器均支持;
    • WOFF:兼容旧版浏览器(如 IE 9+);
    • TTF/OTF:原始字体格式,体积大,不建议直接使用。
  2. 字体子集提取
    使用工具(如 glyphhangerfont-spider)分析项目中使用的字符,只保留这些字符生成精简的字体文件,可将中文字体体积从数 MB 减小到几十 KB。

  3. 字体预加载
    通过 <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 工具)

  1. 安装:npm i font-spider -g
  2. 创建 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>
    
  3. 执行命令: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 属性,否则浏览器会忽略预加载请求(字体文件属于跨域资源,即使在同一域名下)。

总结:七大功能的协同优化思路

  1. 基础层:依赖预构建优化第三方依赖加载,为后续打包打下基础;
  2. 分割层:代码分割实现按需加载,降低首屏体积;
  3. 精简层:Tree Shaking 移除死代码,进一步减小包体积;
  4. 压缩层:资源压缩(Gzip/Brotli)、图片优化、字体优化减少传输体积;
  5. 缓存层:缓存策略确保用户重复访问时无需重新加载资源。

通过这七大功能的协同配置,可将 Vue3 + Vite 项目的首屏加载时间降低 50% 以上,大幅提升用户体验。

参考网站:

https://b4zjnc2cplzzu.ok.kimi.link/index.html