这是我参与「第四届青训营 」笔记创作活动的的第10天
官网
为什么需要Vite?(前端工程痛点)
- 模块化:ESM、CommonJS、UMD
- 资源编译:高级语法(>ES6)、TS的转译、CSS预处理
- 产物质量:代码体积(冗余?压缩?)、代码性能
- 开发效率:热更新
- 。。。
前端构建工具的意义
什么是Vite?
定位:新一代前端构建工具
两大组成部分
- No-bundle开发服务,源文件无需打包
- 生产环境基于Rollup的Bundler
核心特征
- 高性能,dev启动速度和热更新速度非常快
- 简单易用,开发体验好
当下问题
缓慢的启动 ⇒ 项目编译等待时间长
缓慢的热更新 ⇒ 修改代码后不能实时更新
瓶颈在何处?
-
bundle 带来的性能开销 ⇒ 不bundle了
-
浏览器原生ESM支持,天然的按需加载
-
无需打包项目源代码
-
可以利用文件级的浏览器缓存
-
-
JavaScript 语言的性能瓶颈 ⇒ 用Rust、Go
基于Esbuild的编译性能优化
Esbuild —— 基于Golang开发的前端工具
- 打包器 Bundler
- 编译器 Transformer
- 压缩器 Minifier
Vite 使用 esbuild 将 TypeScript 转译到 JavaScript,约是 tsc速度的 20~30 倍,同时 HMR 更新反映到浏览器的时间小于 50ms。
内置 Web 构建能力
内置许多开箱即用的功能,大大减少了使用webpack时基础配置的时间。
上手使用
项目初始化
# 安装pnpm
# npm i -g pnpm
# 初始化命令
pnpm create vite
# 安装依赖
pnpm install
# 启动项目
pnpm run dev
使用Sass
使用CSS Module模块化方案,防止ClassName命名冲突。
生产环境Tree-shaking
- 基于ESM的import/export语句依赖关系,与运行时状态无关。
- 在构建阶段将未使用到的代码进行删除。
Vite整体架构
Pre-bundle预打包
-
避免node_modules过多的文件请求:
- 一些包将它们的 ES 模块构建作为许多单独的文件相互导入。例如,
lodash-es有超过 600 个内置模块!当我们执行import { debounce } from 'lodash-es'时,浏览器同时发出 600 多个 HTTP 请求!尽管服务器在处理这些请求时没有问题,但大量的请求会在浏览器端造成网络拥塞,导致页面的加载速度相当慢。 - 依赖预构建仅会在开发模式下应用,并会使用 **
esbuild**将依赖转为 ESM 模块。在生产构建中则会使用@rollup/plugin-commonjs
- 一些包将它们的 ES 模块构建作为许多单独的文件相互导入。例如,
-
将CommonJS / UMD格式转化为ESM格式。
如何实现?
-
服务启动前扫描代码中用到的依赖。
-
用esbuild对依赖代码进行预打包。
-
改写import语句,指定依赖为预构建产物路径。
文件系统缓存
Vite 会将预构建的依赖缓存到 node_modules/.vite。它根据几个源来决定是否需要重新运行预构建步骤:
package.json中的dependencies列表- 包管理器的 lockfile,例如
package-lock.json,yarn.lock,或者pnpm-lock.yaml - 可能在
vite.config.js相关字段中配置过的
如果出于某些原因,你想要强制 Vite 重新构建依赖,你可以用 --force命令行选项启动开发服务器,或者手动删除 node_modules/.vite目录。
HTTP缓存
依赖是强缓存的: Vite 通过 HTTP 头max-age=31536000,immutable来缓存请求得到的依赖,以提高在开发时的页面重载性能。一旦被缓存,这些请求将永远不会再到达开发服务器。如果更换了新的版本的依赖,你可以:
- 通过浏览器调试工具的 Network 选项卡暂时禁用缓存;
- 重启 Vite dev server,并添加
-force命令以重新构建依赖; - 重新载入页面。
使用插件
Vite 可以使用插件进行扩展,这得益于 Rollup 优秀的插件接口设计和一部分 Vite 独有的额外选项。这意味着 Vite 用户可以利用 Rollup 插件的强大生态系统,同时根据需要也能够扩展开发服务器和 SSR 功能。
若要使用一个插件,需要将它添加到项目的 devDependencies并在 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']
})
]
})
语法安全降级
-
上层解决方案:@vitejs/plugin-legacy
-
底层原理
-
借助Babel Transpile(转译)代码
-
提前注入Polyfill(垫片),如core-js、regenerator-runtime
-