本文已参与「新人创作礼」活动,一起开启掘金创作之路
本篇主要围绕我们项目根目录下的vite.config.ts文件进行,看看我们一般的项目该如何进行配置。
Vite 配置
**遇到问题时,建议首先阅读一遍官方文档。**这里建议先阅读一遍Vite官方中文文档中的配置 Vite配置文件部分内容,以帮助您更好的了解vite.config.js这个文件应该如何配置。
配置文件
配置文件出口
当以命令行方式运行vite时,Vite会自动解析 项目根目录 下名为vite.config.ts的文件。
最基础的配置文件是这样的:
// vite.config.ts
export default {
// 配置选项
};
我们使用Vite创建的目前是这样的:
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
});
defineConfig,顾名思义,定义配置。我们可以看到vite.config.ts文件暴露出了一个接口 defineConfig,这个接口函数接收一个对象参数,这个对象里面就包含了我们各种各样的vite 配置项。
当然了,我们最好还是去Vite源码中看看这个defineConfig 函数是什么样的。
可以看到,其实就是一个接收UserConfigExport类型参数的函数,然后返回这个类型的参数,所以我们改造一下:
import { UserConfigExport } from "vite";
// vite.config.ts
export default (): UserConfigExport => {
// 导出配置对象之前可以进行一些处理
return {
// 配置项
};
};
注意点:
- 观察上面最基础的配置文件,我们是暴露出一个
object类型 - 上图中我们可以了解到,配置项的内容都来自于
UserConfigExport类型,它抛出object类型的配置数据(UserConfig),或者一个函数UserConfigFn(实际上仍然抛出UserConfig) - 一般来说,我们针对不同的环境(
env)会有不同的配置项,需要用到UserConfigFn,所以我们选择最终返回的数据类型符合UserConfigExport
环境配置
如果配置文件需要基于(dev/serve或build)命令或者不同的模式(开发、预发布 、生产等)来决定配置选项,则可以选择导出如下一个函数。继续关注源码:
import { UserConfigExport, ConfigEnv } from "vite";
// vite.config.ts
export default ({ command, mode }: ConfigEnv): UserConfigExport => {
// 导出配置对象之前可以进行一些处理
return {
// 配置项
};
};
注:在Vite的 API 中,在开发环境下command的值为serve(在 CLI 中,vite dev 和vite serve是vite的别名),而在生产环境下为build(vite build)
环境变量
Vite默认是不加载.env文件的,因为这些文件需要在执行完Vite配置后才能确定加载哪一个。所以当你的配置项中需要用到.env文件中的环境变量时,可以使用Vite导出的 loadEnv 函数来加载指定的.env文件。
import { UserConfigExport, ConfigEnv, loadEnv } from "vite";
// 根据当前工作目录中的 `mode` 加载 .env 文件
const root: string = process.cwd();
export default ({ command, mode }: ConfigEnv): UserConfigExport => {
const env = loadEnv(mode, root);
return {
// 配置项
};
};
Bug:这样我们拿到的环境变量(
env),全部都是string类型,这样对吗?
比如,我们看看.env.development文件中的环境变量
我们的VITE_PORT是number类型!!!实际上还有可能是boolean等其他类型,所以我们需要做类型转换,封装一个Vite环境变量类型转换函数。
build 文件夹
通常我们会在项目根目录下建立一个build文件夹,主要作用是帮助我们进行项目的配置与构建。
在这里我们先在build下建立一个工具函数文件utils.ts,写入我们封装的转换函数:
// Read all environment variable configuration files to process.env
export function wrapperEnv(envConf: Recordable): ViteEnv {
// 这里的默认值的key根据ViteEnv类型来设定
const ret: ViteEnv = {
VITE_PORT: 7755,
VITE_PUBLIC_PATH: "",
VITE_PROXY_DOMAIN: "",
VITE_PROXY_DOMAIN_REAL: "",
VITE_ROUTER_HISTORY: "",
VITE_LEGACY: false,
};
// 当然你也可以简单点这么做
// const ret: any = {};
for (const envName of Object.keys(envConf)) {
let realName = envConf[envName].replace(/\\n/g, "\n");
realName =
realName === "true" ? true : realName === "false" ? false : realName;
if (envName === "VITE_PORT") {
realName = Number(realName);
}
ret[envName] = realName;
if (typeof realName === "string") {
process.env[envName] = realName;
} else if (typeof realName === "object") {
process.env[envName] = JSON.stringify(realName);
}
}
return ret;
}
**!!!!!!**写到这里,出现了一个 Bug(当时也卡了我很久)。上面这个文件里 TS 会提示
Recordable和ViteEnv类型不存在。
为什么呢,我不是已经在global.d.ts中全局声明这两个类型了吗(当时我也查阅了几个参考项目,发现这一部分配置都是一样的,没有问题)?
先聚焦这个与 Bug 可能相关的几个文件
- 罪魁祸首
build/utils.ts - 类型声明文件
global.d.ts - ts 配置文件
tsconfig.json - 需要引入
utils.ts中方法的文件vite.config.ts
试探现象:
- 操作 1:注意这个时候,我们是没有在
vite.config.ts中引入utils.ts中的wrapperEnv方法的。引入一下,你会发现,报错消失了。 - 操作 2:这两个类型我们确实已经全局声明了,但是 TS 还是找不到。别忘了我们还有个 ts 配置文件,里面有个
include选项,只有里面包含的文件才能检索到我们自定义的 TS 类型。注意到里面已经include了vite.config.ts,结合操作 1。是不是相当于utils.ts中的那个方法模块也被include了?所以,我们直接把build文件include一下,发现,报错消失!!!
所以你可以选择把build文件也include进来(省事),否则请记得及时将对应模块引入进include里的文件。
继续配置我们的环境变量
import { wrapperEnv } from "./build/utils";
import { UserConfigExport, ConfigEnv, loadEnv } from "vite";
// 当前执行node命令时的项目根目录
const root: string = process.cwd();
export default ({ command, mode }: ConfigEnv): UserConfigExport => {
// 根据当前工作目录中的 `mode` 加载 .env 文件
const env = loadEnv(mode, root);
// env中所有值都是string类型,处理需要兼容其他类型的情况。并提取需要的环境变量
const {
VITE_PORT,
VITE_LEGACY,
VITE_PUBLIC_PATH,
VITE_PROXY_DOMAIN,
VITE_PROXY_DOMAIN_REAL,
} = wrapperEnv(env);
return {
// 配置项
};
};
配置选项
在官网我们可以看到,配置选项包含了共享配置、开发服务器选项、构建选项、预览选项、依赖优化选项、SSR 选项和 Worker 选项。
这里列出部分项目中使用到的选项,大家可以根据自己的需要进行配置。
base
开发或生产环境服务的公共基础路径,从.env文件中获取,本项目中为环境变量VITE_PUBLIC_PATH(string 类型)。
root
项目根目录,默认为node执行时的process.cwd()。
const root: string = process.cwd();
resolve
一般我们使用resolve.alias,来配置我们文件系统路径的别名。
import { resolve } from "path"; // 引入resolve路径解析方法
// 路径查找
const pathResolve = (dir: string): string => {
return resolve(__dirname, ".", dir);
};
// 设置别名
const alias: Record<string, string> = {
"/@": pathResolve("src"),
"@build": pathResolve("build"),
};
export default ({ command, mode }: ConfigEnv): UserConfigExport => {
// ...
return {
// ...
resolve: {
alias,
},
};
};
注:使用别名一定要用绝对路径,否则可能无法被正常解析。
server
开发服务器选项,一般来说我们会配置如下四种选项
https:是否开启 https
https: false
port指定开发服务器端口
注:如果端口已经被使用,Vite会自动尝试下一个可用的端口,所以这可能不是开发服务器最终监听的实际端口
port: VITE_PORT
host指定服务器监听的 IP 地址
注:设置为 0.0.0.0 或者 true 将监听所有地址
host: "0.0.0.0"
proxy自定义跨域代理
本地开发环境通过代理实现跨域,生产环境由nginx转发或者后台开启cors进行处理。同样的,我们新建一个专门配置跨域的文件build/vite/proxy.ts,封装创建跨域的方法。
export function createProxy(prefix, target) {
return target.length > 0
? {
[prefix]: {
target: target, // 用Url模块解析的Url字符串
// ws: true, // 如果你想代理websockets
changeOrigin: true, // 将主机头的来源更改为目标URL
rewrite: (path: string) => regExps(path, prefix),
},
}
: null;
}
// 处理值为RegExp的情况
const regExps = (value: string, reg: string): string => {
return value.replace(new RegExp(reg, "g"), "");
};
在vite.config.ts中配置,VITE_PROXY_DOMAIN为开发环境代理,VITE_PROXY_DOMAIN_REAL为代理的后端地址
server: {
// ...
proxy: createProxy(VITE_PROXY_DOMAIN, VITE_PROXY_DOMAIN_REAL);
}
plugins
关于插件配置,可阅读官网。
plugins选项接收一个数组,平时我们会使用到非常多的插件,都放在这里会显得很臃肿且不利于维护,所以建议创建专门的文件(build/vite/plugins)进行管理,在plugins文件夹下新建插件的.ts文件,及总出口index.ts文件。
这里以@vitejs/plugin-legacy处理老版本浏览器兼容问的的插件举例。
legacy.ts
import legacy from "@vitejs/plugin-legacy";
export function configLegacyPlugin(VITE_LEGACY) {
return VITE_LEGACY
? legacy({
targets: ["ie >= 11"],
additionalLegacyPolyfills: ["regenerator-runtime/runtime"],
})
: null;
}
index.ts
import { PluginOption } from "vite";
import vue from "@vitejs/plugin-vue";
// 引入其他需要做额外配置的插件
import { configLegacyPlugin } from "./legacy";
export function createVitePlugins(VITE_LEGACY) {
const vitePlugins: (PluginOption | PluginOption[])[] = [
// 直接引入的配置
vue(),
];
// 需要做额外配置的插件
vitePlugins.push(configLegacyPlugin(VITE_LEGACY));
return vitePlugins;
}
最后引入
build
构建这一块的内容就比较多了,可以根据官网进行配置。这里给出本项目的配置示例:
build: {
sourcemap: false, // 根据需要看是否创建源地图文件,false构建更快哦
brotliSize: false, // 不构建brotli压缩大小报告,可提升构建速度
chunkSizeWarningLimit: 4000 // 消除打包大小超过500kb(默认值)警告
},
optimizeDeps
依赖优化选项。有时候某些依赖项不需要进行预构建,我们做强制排除操作。不过通常我们使用这个选项下的include项,帮助我们强制预构建不在node_modules中的包。
这里提一个问题:
Vite首次打开界面,界面加载很慢。不是说好的Vite速度很快吗,怎么还慢起来了?
Vite 的快
首先我们来说Vite的快,快在哪里——项目的启动(注意这里不是指我们的页面首页加载完毕)。
启动完毕也就是下面这种状态:
为什么快呢,因为Vite启动时并不会像Webpack一样对所有代码进行编译、打包、压缩,许多工作都在打开页面后交给浏览器来处理了。
Vite 的慢
打开网页时,如果你项目的依赖比较多,就会发现第一次加载是真滴慢呐 ~ 因为这个时候,Vite需要动态解析依赖、动态打包、动态引入,浏览器需要加载当前页面用到的所有文件, 而第一次加载是没有缓存的。
所以影响这里的主要点就是(Vite已在进行相关开发):依赖与构建、浏览器请求过多
我们可以做什么来为Vite提速呢?
- 代码分割,按需加载
- 使用 http2
- optimizeDeps
配置 optimizeDeps 可以让Vite在启动的时候就对某些资源进行预打包,这样就能避免后续的动态打包等行为。
举例:
optimizeDeps: {
include: ["pinia", "vue-i18n", "lodash-es", "@vueuse/core"],
exclude: []
},
define
定义全局常量替换方式。其中每项在开发环境下会被定义在全局,而在构建时被静态替换。
注:它不经过任何语法分析,直接替换文本。所以建议只对CONSTANTS使用
这里我们需要做一个配置,以解决生产环境打包会报的错误(INTLIFY_PROD_DEVTOOLS is not defined)
define: {
__INTLIFY_PROD_DEVTOOLS__: false
}
最后看一下全部 vite.config.ts 代码
import { resolve } from "path";
import { wrapperEnv } from "./build/utils";
import { UserConfigExport, ConfigEnv, loadEnv } from "vite";
import { createProxy } from "./build/vite/proxy";
import { createVitePlugins } from "./build/vite/plugins";
// 当前执行node命令时文件夹的地址(工作目录)
const root: string = process.cwd();
// 路径查找
const pathResolve = (dir: string): string => {
return resolve(__dirname, ".", dir);
};
// 设置别名
const alias: Record<string, string> = {
"/@": pathResolve("src"),
"@build": pathResolve("build"),
};
export default ({ mode }: ConfigEnv): UserConfigExport => {
// 根据当前工作目录中的 `mode` 加载 .env 文件
const env = loadEnv(mode, root);
// env中所有值都是string类型,处理需要兼容其他类型的情况。并提取需要的环境变量
const {
VITE_PORT,
VITE_LEGACY,
VITE_PUBLIC_PATH,
VITE_PROXY_DOMAIN,
VITE_PROXY_DOMAIN_REAL,
} = wrapperEnv(env);
return {
base: VITE_PUBLIC_PATH,
root,
resolve: {
alias,
},
server: {
https: false,
port: VITE_PORT,
host: "0.0.0.0",
proxy: createProxy(VITE_PROXY_DOMAIN, VITE_PROXY_DOMAIN_REAL),
},
plugins: createVitePlugins(VITE_LEGACY),
optimizeDeps: {
include: [],
exclude: [],
},
build: {
sourcemap: false,
brotliSize: false,
chunkSizeWarningLimit: 4000,
},
define: {
__INTLIFY_PROD_DEVTOOLS__: false,
},
};
};
好啦,关于Vite的基本配置就到这里啦,后面还有更多,继续加油!!!