前言:参考 Vite 官网
一. 指引
1. 为什么选择 Vite
1.1 现实问题
问题:当项目中 JS 代码量变多,就会导致:1. 启动开发服务慢(几分钟)。2,模块热替换(HMR)慢(几秒钟)。极大影响开发效率和体验。
解决:1. Vite 将应用模块分为依赖和源码,加快了开发服务器启动时间。Vite 使用 esbuild 预构建依赖,esbuild 使用 go 编写,比 JS 编写的打包器预构建依赖块 10-100 倍。Vite 以原生 ESM 方式提供源码。这实际上是让浏览器接管了打包程序的部分工作:Vite 只需要在浏览器请求源码时进行转换并按需提供源码。2. Vite 使已编辑的模块与其最近的 HMR 边界之间的链失活,始终保持快速更新。
1.2 为什么生产环境仍需打包
为了最佳的加载性能,最好将代码进行 tree-shaking、懒加载和 chunk 分割(以获得更好的缓存)。为什么不用 ESBuild 打包,因为 Vite 的插件 API 与 esbuild 打包器并不兼容。尽管 esbuild 速度更快,但 Vite 采用了 Rollup 打包。
2. 开始
2.1 总览
Vite 是一款前端构建工具,由两部分组成:1. 一个开发服务器。2. 一套构建指令。在线试用 vite
2.2 搭建 Vite 项目
兼容性注意:Vite 需要 Node.js 版本 18+,20+。
npm create vite@latest # or yarn create vite # or pnpm create vite
2.3 index.html 与项目根目录
在 Vite 项目中,index.html 在项目最外层。在开发期间 Vite 是一个服务器,而 index.html 是该 Vite 项目的入口文件。Vite 解析 指向 JavaScript 源码。index.html 中 URL 将被自动转换,不再需要 %PUBLIC_URL% 占位符了。
2.4 命令行界面
在通过脚手架创建的 Vite 项目中默认的 npm scripts:
{ "scripts": { "dev": "vite", // 启动开发服务器,别名:`vite dev`,`vite serve` "build": "vite build", // 为生产环境构建产物 "preview": "vite preview" // 本地预览生产构建产物 } }
3. 功能
Vite 通过原生 ESM 导入提供了许多主要用于打包场景的增强功能。
3.1 NPM 依赖解析和预构建
原生 ES 导入不支持下面这样的裸模块导入,Vite 将会检测到所有裸模块导入,并进行: 1. 预构建。2. 重写导入为合法的 URL
import { someMethod } from "my-dep";
3.2 TS
Vite 天然支持引入 .ts 文件。Vite 使用 esbuild 将 TS 转译为 JS,Vite 为了将类型定义用在客户端需要设置 d.ts 文件,或者添加到 tsconfig.ts 中的 compilerOptions.types 中。设置后会提供:资源导入、import.meta.env、import.meta.hot 的类型定义补充:
// d.ts /// <reference types="vite/client" />
// tsconfig.ts { "compilerOptions": { "types": ["vite/client"] } }
要覆盖默认类型定义,新建一个包含你所定义类型的文件,然后在 d.ts 注释前引入:
// vite-env-override.d.ts (the file that contains your typings): declare module "*.svg" { const content: React.FC<React.SVGProps<SVGElement>>; export default content; }
// The file containing the reference to vite/client /// <reference types="./vite-env-override.d.ts" /> /// <reference types="vite/client" />
3.3 CSS
以 .module.css 为后缀的 CSS 被认为是 CSS modules 文件。导入这样的文件会返回一个相应的模块对象:
/* example.module.css */ .red { color: red; }
import classes from "./example.module.css"; document.getElementById("foo").className = classes.red;
通过 ?inline 关闭 css 注入页面,使 css 样式失效。
import "../src/assets/css/index.scss?inline";
3.4 JSON
JSON 可以被直接导入 —— 同样支持具名导入:
// 导入整个对象 import json from "./example.json"; // 根字段具名导入 —— 有效帮助 treeshaking!(只打包用到的依赖) import { field } from "./example.json";
3.5 Glob 导入
import.meta.glob 函数从文件系统导入多个模块,默认是懒加载的,过动态导入实现,传入 { eager: true } 转化为直接引入。传入{ as: "raw", eager: true }转化为字符串形式:
const modules = import.meta.glob("./dir/*.js"); // 转译为 const modules = { "./dir/foo.js": () => import("./dir/foo.js"), "./dir/bar.js": () => import("./dir/bar.js"), }; const modules = import.meta.glob("./dir/*.js", { eager: true }); // 转译为 import * as __glob__0_0 from "./dir/foo.js"; import * as __glob__0_1 from "./dir/bar.js"; const modules = { "./dir/foo.js": __glob__0_0, "./dir/bar.js": __glob__0_1, }; const modules = import.meta.glob("./dir/*.js", { as: "raw", eager: true }); // 转译为 const modules = { "./dir/foo.js": 'export default "foo"\n', "./dir/bar.js": 'export default "bar"\n', };
使用数组多个匹配文件:
const modules = import.meta.glob(["./dir/*.js", "./another/*.js"]);
使用 !排除匹配中的一些文件:
const modules = import.meta.glob(["./dir/*.js", "!**/bar.js"]); // 转译为 const modules = { "./dir/foo.js": () => import("./dir/foo.js"), };
3.6 构建优化
CSS 代码分割
Vite 会根据异步 chunk 模块生成单独的 CSS 文件。在异步 chunk 加载完成时通过 标签载入,该异步 chunk 只在 CSS 加载完毕后执行,避免发生 FOUC (无样式内容闪烁)。通过设置 build.cssCodeSplit 为 false 来禁用 CSS 代码分割。将所有 CSS 抽取到一个文件。
异步 Chunk 加载优化
问题:在无优化的情境下,当异步 chunk A 被导入时,浏览器将必须请求和解析 A,弄清楚它也需要共用 chunk C。这会导致额外的网络往返:Entry ---> A ---> C
解决:Vite 使用预加载步骤自动重写代码,来分割动态导入调用,以实现当 A 被请求时,C 也将 同时 被请求:Entry ---> (A + C)
4. 命令行界面
# 启动 Vite 开发服务器 vite [root] # 构建生产版本 vite build [root] # 预构建依赖 vite optimize [root] # 本地预览构建产物 vite preview [root]
5. 使用插件
5.1 添加插件
添加后,在 vite.config.js 配置文件中的 plugins 数组中引入它。例如,要想为传统浏览器提供支持:
$ npm add -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"], }), ], });
5.2 按需应用
使用 apply 属性指明插件在 'build' 或 'serve' 哪种模式时调用:
// vite.config.js import typescript2 from "rollup-plugin-typescript2"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [ { ...typescript2(), apply: "build", }, ], });
6. 依赖预构建
首次启动 vite 时,Vite 会预构建项目依赖。目的在于:1. 将 CommonJS 或 UMD 转化为 ES 模块。2. 将许多内部模块的 ESM 依赖项转换为单个模块。
7. 构建生产版本
打包运行 vite build 命令。将 /index.html 作为构建入口点,生成静态部署的应用包。
8. 部署静态站点
默认打包文件 dist,可通过 build.outDir 修改。vite preview 只作预览本地构建,不应作为生产服务器。Vite 构建的项目已经配置好了 npm scripts:
{ "scripts": { "build": "vite build", "preview": "vite preview" } }
8.1 构建应用
运行 npm run build 打包,默认打包到 dist 文件夹,可以将 dist 文件夹部署到你喜欢的平台。构建完成后通过 npm run preview 在本地启动一个静态 wbe 服务器,将 dist 文件夹运行在 http://localhost:4173,这样可以在本地查看构建产物是否正常运行。可以通过 --port 配置端口:
# 打包 npm run build # 预览 npm run preview # or npm run preview --port 8080
8.2 GitHub Pages
在 vite.config.js 中配置 base,设置部署根目录。比如部署到 https://.github.io/,base 设置为'/',如果部署到 https://.github.io//,base 设置为'//'。
<!-- base: 'admin' --> <script type="module" crossorigin src="/admin/assets/index-qcvr7oK4.js" ></script> <link rel="stylesheet" crossorigin href="/admin/assets/index-ICKpZ_al.css" /> <!-- base: '/' --> <script type="module" crossorigin src="/assets/index-qcvr7oK4.js"></script> <link rel="stylesheet" crossorigin href="/assets/index-ICKpZ_al.css" />
9. 环境变量与模式
9.1 内置环境变量
import = { meta: { env: { // 运行模式 MODE: 'development', // 部署的基本 URL BASE_URL: '/', // 是否运行在生产环境 PROD: false, // 是否运行在开发环境 DEV: true, // 是否运行在 server 上 SSR: false } } }
9.2 .env 文件
Vite 使用 dotenv 加载额外的环境变量,指定模式的优先级(.env.development)高于通用模式(.env),并通过 import.meta.env 暴露给客户端,变量命名必须以 VITE\_为前缀才会被暴露出去:
.env # 所有情况下都会加载 .env.local # 所有情况下都会加载,但会被 git 忽略 .env.[mode] # 只在指定模式下加载 .env.[mode].local # 只在指定模式下加载,但会被 git 忽略
随着环境变量越来越多,我们希望 TS 可以给出智能提示,可以通过在 src 下创建 env.d.ts,然后定义如下代码实现:
/// <reference types="vite/client" /> interface ImportMetaEnv { readonly VITE_APP_TITLE: string; // 更多环境变量... } interface ImportMeta { readonly env: ImportMetaEnv; }
9.3 在 HTML 中使用
任何命名的环境变量可以在 HTML 中使用,用法 %ENV_NAME%:
<h1>Vite is running in %MODE%</h1> <p>Using data from %VITE_API_URL%</p>
9.4 模式
vite 加载 .env.development 的环境变量,vite build 加载的 .env.prodution 的环境变量,可以通过 --mode 替换加载的环境变量,比如在 staging(预发布)模式下构建应用加载 .env.staing 的环境变量:
vite build --mode staging
10. 性能优化
10.1 减少解析操作
Vite 支持通过 resolve.extensions 选项“猜测”导入路径,该选项默认为 ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json']。当使用 import './Component' 导入 ./Component.jsx 时,Vite 将运行以下步骤来解析它:
| 检查 ./Component 是否存在,不存在。
| 检查 ./Component.mjs 是否存在,不存在。
| 检查 ./Component.js 是否存在,不存在。
| 检查 ./Component.mts 是否存在,不存在。
| 检查 ./Component.ts 是否存在,不存在。
| 检查 ./Component.jsx 是否存在,存在!
如上,导入一个文件需要进行 6 次文件系统检查。隐式导入越多,解析所需的时间就越多。所以明确导入路径,例如:import './Component.jsx' 可以减速解析时间。
10.2 避免使用桶文件
桶文件(barrel files)是重新导出同一目录下其他文件 API 的文件。例如:
// src/utils/index.js export * from "./color.js"; export * from "./dom.js"; export * from "./slash.js";
导入一个单独的 API,import { slash } from './utils',需要获取和转换桶文件中的所有文件。这意味着在初始页面加载时,加载的文件比所需的要更多,导致页面加载速度变慢。所以应避免使用桶文件(barrel files),直接导入单独 API:import { slash } from './utils/slash.js'
Vite 官方文档速通(下)https://developer.aliyun.com/article/1512023?spm=a2c6h.13148508.setting.29.f8774f0euyBLtl