https://cn.vitejs.dev/config/
依赖浏览器自动解析 ESM 的能力
Q:为什么 vite.config.js 可以写成 ESM 的形式?
A:因为 vite 在读取该文件时会先用 node 去解析该文件,将其转换为 commonjs 规范,然后再去执行。
Q:vite 是怎么让浏览器识别.vue 文件的?
A: 因为 vite 会将所有的资源文件都视为模块,包括.vue 文件。当浏览器请求一个.vue 文件时,vite 会将其转换为 ESM 规范的模块,然后返回给浏览器。
0 环境变量
vite 内置了第三方库 dotenv,用于加载环境变量。
dotenv 会自动读取项目根目录下的 .env 文件,将.env 文件中定义的环境变量通过配置加载到 process.env 中。
相关配置:root, envDir, envPrefix
1 预构建(开发环境)
1.1 依赖预构建
esbuild
• 绝对路径转换
import _ from 'lodash' 会被转换为 import _ from '/node_modules/.vite/lodash.js'
• cjs 规范统一转换
将 cjs 规范的依赖包转换为 ESM 规范,并缓存在 node_modules/.vite/dep/ 目录下
• 瀑布依赖处理(解决网络多包传输性能问题)
例如 lodash-es 中的方法会循环依赖多个包的其他方法
import { debounce } from 'lodash-es'
vite 会将这些循环的依赖打包到一个文件中,避免网络传输多个包的问题
1.2 处理 css
1. 检测到 css 文件的引用路径 2. 根据该路径读取 css 文件内容 3. 创建 style 标签,将 css 内容插入到标签中 4. 将 style 标签插入到 head 标签中 5. 将 css 文件中的内容替换为 js 脚本,方便后续热更新和 css 模块化,同时设置请求头 Content-Type: javascript,使浏览器按照 js 脚本执行该文件
Q:css 模块化是什么?
约定文件命名 xxx.module.css,将原来的类名附带上哈希值,解决多个文件 class 类命名重复导致的样式覆盖问题。
vite 相关配置:
• css.modules.localsConvention类名单词连接方式• css.modules.scopeBehaviour模块化行为方式• css.modules.generateScopedName生成的类名展示方式• css.modules.hashPrefix生成 hash 的前缀• css.modules.globalModulePaths排除不想参与 css 模块化的路径
css 预处理器全局配置:
• css.preprocessorOptions.less预处理器 less 选项
1.3 配置 postcss
主要解决浏览器兼容性问题
1. 处理 css 变量、函数、嵌套语法: var()2. 前缀补全: --webkit-xxx3. 高级 css 语法降级
预设环境插件: postcss-preset-env
vite 相关配置:
• css.postcss.plugins:[postcssPresetEnv()]
1.4 处理静态资源
• resolve.alias设置别名• build.rollupOptions.output.assetFileNames静态资源文件名配置规则• build.assetsInlineLimit内联资源体积限制,默认 4096 字节(4kb),超过该值的资源会被单独打包• build.outDir构建输出目录,默认 dist• build.assetsDir静态资源输出目录,默认 assets
Q:别名 resolve.alias 的原理是什么?
根据别名配置对文件中使用别名的地方进行字符串替换。
Q:为什么解析路径一定要使用 path.resolve()?
node 端读文件的时候,如果发现是相对路径(./a.css),则会用 process.pwd()来拼接。但我们希望是基于当前文件去生成 a.css 的绝对路径,所以要用__dirname 拼接,还要处理不同系统反斜杠等其他问题,而 path.resolve()可以帮我们处理这些问题。
__dirname 始终返回的是当前文件所在目录的绝对路径。__filename 始终返回的是当前文件的绝对路径。
2 构建打包(生产环境)
rollup
3 vite 插件
Q:什么是插件?
在生命周期不同阶段调用不同的插件实现不同的目的。enforce: pre/post 控制插件的执行时机。
vite 独有钩子:
• config在解析 vite 配置前调用,必须返回配置对象,vite 会自动合并这些配置• configResolved在解析 vite 配置后调用• configureServer用于自定义配置开发服务器• transformIndexHtml转换 index.html,支持异步,返回的内容会更新到 html 模板• configPreviewServer自定义预览相关的配置• handleHotUpdate自定义热更新行为
通用钩子:
• optionsrollupOptions 相关的配置• ...
常用插件:
• vite-aliases检测当前目录下的所有文件,帮我们自动生成别名。等同于 resolve.alias 配置• vite-plugin-html动态控制 html 模板的内容• vite-plugin-mock生成 mock 数据
vite 相关配置:
• plugins: [viteAliases(), createHtmlPlugin(), viteMockServe()]
// 手写vite-aliases插件
import path from "path";
import fs from "fs";
export default function (keyPrefix = "@"): Plugin {
return {
name: "MyViteAliases",
config(config, env) {
// const { mode, command } = env;
// 获取所有目录
const dirs = [];
const result = fs.readdirSync(path.resolve(__dirname, "../src"));
result.forEach((name) => {
const currentFileStat = fs.statSync(
path.resolve(__dirname, "../src", name)
);
const isDir = currentFileStat.isDirectory();
if (isDir) {
dirs.push(name);
}
});
// 生成别名配置
const resolve = {};
dirs.forEach((dir) => {
resolve[`${keyPrefix}/${dir}`] = path.resolve(__dirname, "../src", dir);
});
return {
resolve,
};
},
};
}4 共享选项
root
默认: process.cwd()
项目根目录(index.html 文件所在的位置)
envDir
默认: root
环境变量文件所在目录,默认是项目根目录
envPrefix
默认: VITE_
环境变量前缀,以该前缀开头的变量会通过 import.meta.env 暴露在客户端代码中,也可以通过 loadEnv 函数获取。
5 依赖优化选项(开发环境)
optimizeDeps.exclude
在预构建中强制排除的依赖项。
CommonJS 的依赖不应该排除在优化外。如果一个 ESM 依赖被排除在优化外,但是却有一个嵌套的 CommonJS 依赖,则应该为该 CommonJS 依赖添加 optimizeDeps.include。
export default defineConfig({
optimizeDeps: {
include: ["esm-dep > cjs-dep"],
},
});optimizeDeps.include
在预构建中强制包含的依赖项。
6 构建选项(生产环境)
不想写了,看官网文档吧。。。
7 性能优化
开发相关:
• 构建速度优化:vite 预构建 • 动态导入:按需加载
构建相关:
• 体积优化:gzip 压缩、tree shaking、图片资源压缩、分包策略 • 带宽优化:cdn 加速(vite-plugin-cdn-import,在 head 中动态插入 script)
页面性能指标:
• 首屏渲染时长 fcp: • 懒加载 • http 优化 • 协商缓存:与服务器协商,服务器会查看文件是否发生变化,没变化返回 304 • 强缓存:服务端给响应头设置的一些字段 expires • 页面最大元素渲染时长 lcp
js 逻辑:
• 副作用清除:定时器、事件监听 • 浏览器:重绘、重排、浏览器帧率(requestAnimationFrame、requestIdleCallback) • 写法上:用 lodash 等工具
css:
• 能继承的不要重复写 • 嵌套层级不要过深
结语
框架作为我们大前端人工程化的工具,可一定要学好哦!