6.4 Vite 配置解析
Vite 执行构建时可以设置丰富的配置选项。默认情况下,执行 vite 命令时会读取项目根目录下的 vite.config.js 文件,该文件会导出自定义的 Vite 配置。
但 Vite 是开箱即用的,即便没有导出任何配置,也可以直接运行,这是因为 Vite 内置了一套经过优化的默认配置选项。当不指定配置项时,就会读取默认配置项;当指定配置项时,则会覆盖默认配置项。
// vite.config.js
import { defineConfig } from "vite";
export default defineConfig({
// ... 自定义配置项
});
上面的配置文件中,使用 defineConfig 函数可以带来配置项的类型提示。请注意观察,这里使用了 ESM 语法,而不是像 Webpack 的配置文件一样必须使用 CommonJS 语法。这是因为 Vite 在加载配置文件前会先进行预处理,这样我们可以在项目中的任何地方统一使用 ESM。
多环境配置
大多数情况下,开发环境和生产环境的构建配置是不一样的,Webpack 也是如此。Vite 同样支持通过环境,甚至通过命令参数来设定不同的配置,这需要导出一个函数,方法如下。
// vite.config.js
import { defineConfig } from "vite";
export default defineConfig(({ command, mode }) => {
return {};
});
函数接收 command、mode 两个参数,并返回真正的配置对象。command 表示 vite 命令后的第一个参数,mode 表示当前环境。当执行不同的命令时,command 和 mode 对应的值如下。
$ vite
# command:serve,mode:development
$ vite build
# command:build,mode:production
根据不同场景下不同的 command、mode 值,我们可以判断导出不同的配置项,通过这种方式来实现多环境配置。当然你也可以将配置文件拆分成多个,执行不同命令时加载不同的配置文件。我们还是以同一个配置文件中导出不同的配置为例,书写方式如下。
// vite.config.js
import { defineConfig } from "vite";
export default defineConfig(({ command, mode }) => {
let envDir = command == "serve" ? ".env.dev" : ".env.pro";
if (mode == "development") {
return { envDir, clearScreen: true };
} else {
return { envDir, clearScreen: false };
}
});
通用配置
通用配置是指在开发服务器、项目打包、预览构建都适用的配置。Vite 的通用配置比较多,我们只介绍常用的选项,更多选项清参考官方文档。
- base
开发或生产环境的公共基础路径。默认是“/”。这个选项非常重要,它决定了最终项目部署时正确的 URL 是什么。默认情况下,假设使用 http://localhost:8080
可以打开项目,如果将 base 的值修改为 /test,那么正确的 URL 应该是 http://localhost:8080/test
。
当项目部署到服务器上,并分配一个二级域名时,此时必须修改 base 选项和二级域名一致,这样才可以保证项目的正常访问。
- mode
项目的两种模式。'development' 表示开发模式,'production'表示生产模式。这两种模式分别启用了不同的配置项优化,比如开发模式增强了对开发预览的优化,而生产模式则增强了对打包构建的优化。不同的模式还支持加载不同的环境变量。
- plugins
需要用到的插件数组。这部分非常关键,所有导入的插件必须定义到这里才能生效。
- publicDir
静态资源文件目录,默认是 public。该目录下存放的静态文件不会经过任何的编译和转换处理,在打包后会直接被复制到输出目录下,因此这里特别适合存放在 index.html 中直接引用的文件。切记最好不要在源码中引用该目录下的文件,Vite 不推荐这么做,你可以将需要在源码中引用的资源放在 src/assets 目录下。
- cacheDir
存储缓存文件的目录。在 6.3 节我们介绍过,Vite 会将依赖缓存起来以提高构建性能,其中文件系统缓存就存储在 “node_modules/.vite” 目录下,该目录便是 cacheDir 配置项的默认值。
你可以修改缓存目录,如设置 cacheDir: ".vite",那么重新编译时文件系统的缓存就会存储到 “.vite” 目录下。
- resolve
该选项是定义如何解析的选项,值是一个对象,可以定义许多解析规则。我们最常用的子选项有两个,分别是 alias 和 extensions。
(1)resolve.alias:用于定义路径别名,这个非常常用,大部分的项目中会定义别名 “@” 表示 src 目录的地址,在导入模块时就可以使用别名,看起来更方便。下面是定义方法:
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
export default defineConfig({
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
(2)resolve.extensions:定义导入模块时可以省略的扩展名,值是一个数组,默认是['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json']。以这些扩展名为后缀的文件在导入时可以省略后缀,Vite 会自动查找匹配的文件。假设现在有文件 src/demo.ts,结合别名,以下三种导入方式效果是一样的:
import demo from "./src/demo.ts";
import demo from "./src/demo";
import demo from "@/demo";
- css
该选项定义如何解析 CSS,最常用的选项只有一个:css.devSourcemap。表示是否开启 devSourcemap。
devSourcemap 即源代码映射。我们知道 Vite 会将 CSS 编译到 html 文件的 <style>
标签内,我们在浏览器中调试代码时,点击样式也会定位到 <style>
标签下。实际上,我们更想知道样式存在于源代码中的哪个位置,此时开启 devSourcemap 再点击调试,就能看到样式已经定位到源码中的位置了。
devSourcemap 在生产模式下不生效,只是方便于开发模式下调试,所以直接开启即可,不会影响到生产环境的性能。
- esbuild
该选项用于定义 esbuild 的 Transform API,主要的应用场景是自定义 JSX。选项主要包含以下两个属性:jsxFactory、jsxFragment。jsxFactory 表示转换 JSX 的构造函数,jsxFragment 表示批量创建元素时无外部包裹的函数。
一般在 Preact 中需要自定义 JSX,其配置如下:
export default defineConfig({
esbuild: {
jsxFactory: "h",
jsxFragment: "Fragment",
},
});
这表示在 Preact 中使用 JSX 时,其转换规则如下:
const dom = (
<>
<p>1</p>
<span>2</span>
</>
);
// 转换后
const { h, Fragment } = "preact";
const dom = h(Fragment, null, [h("p", null, "1"), h("span", null, "2")]);
- envDir
该选项表示用于加载 .env 文件的目录,默认是项目根目录。.env 用于定义环境变量,当需要按照不同模式加载不同的环境变量时,也会定义像 .env.staging 这样的文件,这些文件的位置都通过 envDir 来设置。当需要在多个项目共享环境变量时,该选项非常有用。
- envPrefix
该选项表示有效的环境变量前缀,默认是 “VITE_”。为了安全,Vite 只认为符合该选项配置前缀的环境变量才是有效的。比如 VITE_BASEURL 是个有效的环境变量,可以通过 import.meta.env.VITE_BASEURL 访问。而 BASEURL 是个无效的环境变量,不能通过 import.meta.env.BASEURL 访问。
开发服务器配置
开发服务器配置项大多数都是制定开发服务器如何运行。在 vite.config.js 中,开发服务器配置被定义在 server 选项下,下面介绍的所有配置都是 server 对象下的属性,我们来看一下具体有哪些配置。
- host
指定开发服务器 IP 地址,默认为 localhost。一般情况下本地项目运行的地址都是 localhost,不过有时候为了在一个局域网内的多台设备互相访问,我们可能需要局域网的 IP 地址,这些 Vite 都已经帮你做好了。运行 “yarn run dev --host”,可以看到以下结果。
这里提供了两个 IP,第二个就是局域网的 IP 地址,同一个局域网内的其他电脑也可以打开,这对协作调试和测试非常有帮助。
- port
指定开发服务器监听端口,默认为 5173。如果 5173 端口已被占用,Vite 会尝试使用下一个端口(默认端口号+1)。当然通过该配置修改默认端口号后,如果端口占用,Vite 还是会继续尝试下一个端口,保证开发服务器可以运行起来。
- strictPort
使用 port 选项设置端口后,如果端口占用,则会尝试下一个端口。但也许你并不想切换端口,此时可以将 strictPort 选项设置为 true,当端口已被占用则会直接退出。
- https
是否启动 https,默认不启动。如果启动 https,还需要一个合法可用的证书,此时可以使用插件 @vitejs/plugin-basic-ssl 来帮我们自动生成一个自签名的证书。不过这种证书只适合在开发环境下使用,当线上部署时,请使用第三方机构颁发的证书。
- open
启动开发服务器时,是否在浏览器中自动打开该网址,默认值为 true。
- proxy
这个选项是开发服务器中比较重要的,用于设置 HTTP 请求代理。在前端开发的过程中,直接调用后端提供的接口,往往会遇到跨域问题,这非常令人头疼。通常跨域在前端的解决方案就是使用代理,将符合规则的本地 IP 转换为真实 IP。
假设真实的服务端接口地址是 http://api.test.com
,我希望在前端请求 /api 时可以自动代理请求到服务端地址,那么配置如下:
defineConfig({
server: {
proxy: {
"/api/*": {
target: "http://api.test.com",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
});
假设本地开发服务器的地址是 http://loclahost:3479
,此时在项目中发起请求,其代理规则如下:
http.get("/api/get-name/1");
// 代理后等于
http.get("http://api.test.com/get-name/1");
- watch
模块热替换使用 chokidar 监听文件变化,监听到变化后才能执行重新构建。当项目工程变大时,监听庞大的文件非常耗费性能,因此可以通过该配置项修改文件监听的范围。
默认情况下,Vite 会忽略对 .git 和 node_modules 目录的监听,我们可以通过该选项的 ignored 属性修改监听目录(包括添加和排除目录)。比如要排除对 index.html 文件修改的监听,添加对 node_modules/dayjs 模块的监听,配置方式如下:
defineConfig({
server: {
watch: {
ignored: ["**/index.html", "!**/node_modules/dayjs/**"],
},
},
});
上面配置中,“**”表示对多层目录和文件的匹配,“*”表示对单层目录和文件的匹配,这样我们就匹配到了项目中对应的文件路径。“!”表示取反,意思是为已经忽略的目录添加监听。
打包构建配置
项目打包时会把打包后的代码输出到一个文件夹,打包构建配置就是用于配置项目打包细节以及文件输出到哪里等。打包配置多数是 rollup 配置,因为打包时不会使用 esbuild,这是与开发服务器配置最大的区别。
- target
该选项表示构建目标,也就是打包后的代码在哪里可以运行,默认值是 “modules”,表示支持 ESM、import() 动态导入和 import.meta 的现代浏览器。另一个值是 “esnext”,表示不支持 import() 的浏览器,选择该选项打包会做降级处理,由 esbuild 执行降级转换。
- outDir
打包后的代码输出路径,默认是 dist 目录(相对于项目根目录)。
- assetsDir
打包后的静态资源的存放路径,默认是 assets 目录(相对于 outDir)。
- assetsInlineLimit
该选项指定一个阈值用于决定是否将导入的资源转换为 base64 编码,从而避免额外的 http 请求,默认值为 4kb。当导入资源小于该值时,会转换成 base64 内置于代码中,反之则生成文件输出到 assets 目录中。设置为 0 可以禁用 base64 转换。
- cssCodeSplit
是否启用 CSS 代码分割,默认为 true。启用时会按照异步导入规则将 CSS 拆分到多个 chunk 中,否则所有样式都会被提取到一个 CSS 文件中。
- cssTarget
CSS 转换目标,默认与 target 选项一致。该选项主要用于处理 CSS 兼容性,对于不支持某些属性的构建目标,比如安卓微信中的 Webview 不支持 #RGBA 颜色,此时可以通过该选项做 CSS 降级,设置为 chrome61 即可。
- sourcemap
该选项很重要,是否启用 sourcemap,默认为 false。生产环境下为了减少构建提体积一般不会启用 sourcemap,缺点是当报错时无法定位源码中的错误位置。如果启用 sourcemap 则会生成单独的 sourcemap 文件,该文件的作用只是为了帮助调试。
一般情况下,我们可以在预发环境启用 sourcemap,生产环境禁用 sourcemap。
- rollupOptions
该选项用于自定义 rollup 打包配置,配置细节请查看 rollup 文档。指定后的配置将与 Vite 默认的 rollup 配置合并,可以大大提高 Vite 的扩展性。
- lib
当要开发一个第三方 npm 库的时候,该选项非常重要,可以指定库的入口文件、模块化系统、全局变量等等。开发普通项目时则不需要使用该选项。
- manifest
是否生成 manifest.json 文件,默认为 false。manifest.json 一般用于为 SPA 提供应用的描述信息,比如应用名称、作者、图标、主题等,这里也可以为服务端框架提供正确的资源引入链接。另一个选项 ssrManifest 与该选项类似,是为服务端渲染生产清单文件。
- ssr
该选项表示是否启用服务端渲染,默认 false。也可以直接设置服务端渲染的入口文件,此时会自动启用服务端渲染。
- minify
打包时压缩代码的工具,默认值为 esbuild。另一个选项是使用 terser,但它的速度要比 esbuild 慢 20-40 倍。
- emptyOutDir
打包时是否会清空输出文件,默认为 true。但如果输出目录不在项目根目录下,则该选项默认为 false,这是为了避免删除项目外的重要文件。可以将此选项设置为 true 强制清空。
- chunkSizeWarningLimit
当打包生成的 chunk 体积大于该选项时,控制台会提示警告,告诉我们项目该优化了。该选项默认值为 500kb,当触发警告时,可以通过拆分组件和异步加载来解决问题。
- watch
是否启用 rollup 监听器,默认为 null。当设置为一个配置对象时,则启用监听器。启用监听器后,当修改源码时会自动重新打包并输出文件,这对一些跨平台框架非常适用。
性能优化配置
Vite 在开发环境下的性能已经非常不错了,在某些情况下可以做依赖优化。生产环境下的优化主要是对 Rollup 打包的优化,比如合适的分包、压缩、Tree Shaking 等。
- 依赖优化
Vite 会根据模块导入自动搜寻依赖,一般情况下不需要修改。然而有时候我们确定某个模块不需要预构建,就可以通过配置手动将该依赖排除,从而提高构建效率。依赖配置通过 optimizeDeps 配置选项实现,该选项可以调整自动搜寻依赖的规则。
默认情况下,Vite 只会从 node_modules 目录下抓取依赖项。使用 optimizeDeps.exclude 可以将 node_modules 目录下的某些依赖排除在外。如果要将一个不在 node_modules 目录下的模块添加为依赖,则可以通过 optimizeDeps.include 设置。exclude 和 include 的值都是一个数组,可以添加多个需要匹配的模块或目录文件。
假设要将 node_modules/dayjs 模块排除在依赖项之外,并将 src/test.js 添加为依赖,则可以这样配置:
defineConfig({
optimizeDeps: {
exclude: ["dayjs"],
include: ["src/test.js"],
},
});
重新运行项目,此时 dayjs 模块会跳过构建被直接导入并使用,而 src/test.js 会被构建并输出到 node_modules/.vite 目录下。
- Tree Shaking
Tree Shaking 也被称为 “摇树优化”。简单来讲,就是在保证代码正常运行的前提下,去除无用的代码,这样可以大幅度的减少构建体积。在 Vue3 中默认开启 Tree Shaking 优化,但有一个前提,必须是 ESM 才可以支持。
比如常用的 dayjs,它是 CommonJS 风格的模块,无法进行 Tree Shaking 优化。在项目中导入该模块并使用其中的某一个函数,打包后 Vite 会将整个模块全都打进去,很显然这会大大增加构建体积。那怎么办呢?最简单的方法就是使用 dayjs-es 模块代替,Tree Shaking 会自然开启。
所以在使用一个第三方模块时,尽量使用 ESM 类型的模块。如果一个常用的模块是 CommonJS 风格,请搜索是否有 “xxx-es” 的包名,这很可能是该模块的 ESM 版的实现。
- 分包策略
默认情况下,当浏览器重复请求相同名称的资源时,会直接使用缓存。利用这个机制我们可以将常用的第三方模块单独打包成一个文件,这样就可以减少 HTTP 请求,提高加载速度。
该分包策略需要在 Rollup 中配置,方法如下:
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: (id) => {
// 将 node_modules 中的代码单独打包成一个 JS 文件
if (id.includes("node_modules")) {
return "vendor";
}
},
},
},
},
});
上述配置中会将所有用到的 node_modules 中的模块单独打包,生成 vendor.xxx.js 文件。如果你只想打包部分模块,直接修改 manualChunks 方法中的判断规则。
服务端渲染配置
Vite 的另一大特点是支持服务端渲染构建。与默认构建不同的是,服务端渲染需要打包产物与服务端框架集成,因此它会打包出多个 html 页面用于与服务端的路由集成,这与 SPA 页面完全不同。Vite 服务端渲染构建对 Node.js 支持最友好,也推荐使用 Node.js 作为渲染服务器。
服务端渲染配置定义在 ssr 选项下,包括的属性如下。
- external
不知道啥意思,待研究。
- noExternal
是否禁用外部依赖。
- target
SSR 服务器构建目标,默认是 Node.js,也推荐 Node.js。
- format
SSR 服务器构建后的语法格式,有 esm 和 cjs 两种。cjs 表示 CommonJS,是 Node.js 的语法风格,从 Vite v3 开始默认值为 esm,不推荐使用 cjs。