vite迁移记录
为什么要迁移到vite
当我们开始构建越来越大型的应用时,需要处理的 JavaScript 代码量也呈指数级增长。包含数千个模块的大型项目相当普遍。基于 JavaScript 开发的工具就会开始遇到性能瓶颈:通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用模块热替换(HMR),文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感。
为了提高开发的幸福感和愉悦度,和提高开发效率的情况下,最近对一个项目进行了改造迁移,迁移完成后热更速度让我有了非常舒畅的感觉。
vite为什么这么快
在冷启动的时候,基于打包器的方式启动必须优先抓取并构建你的整个应用,然后才能提供服务。但是vite通过在一开始将应用中的模块区分为 依赖
和 源码
两类。
依赖
大多为在开发时不会变动的纯 JavaScript。一些较大的依赖(例如有上百个模块的组件库)处理的代价也很高。依赖也通常会存在多种模块化格式(例如 ESM 或者 CommonJS)。Vite 将会使用 esbuild 预构建依赖。esbuild 使用 Go 编写,并且比以 JavaScript 编写的打包器预构建依赖快 10-100 倍。
源码
通常包含一些并非直接是 JavaScript 的文件,需要转换(例如 JSX,CSS 或者 Vue 组件),时常会被编辑。同时,并不是所有的源码都需要同时被加载(例如基于路由拆分的代码模块)。
Vite 以 原生 ESM 方式提供源码。这实际上是让浏览器接管了打包程序的部分工作:Vite 只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。
Webpack 式的经典 bundler 示意图
Vite 式的 No-bundler 示意图
Vite能兼容IE吗
使用官方插件 @vitejs/plugin-legacy 支持
环境要求是什么
最低要求Node14.18.0+
开始迁移
必备插件
- 必要的依赖:
vite-plugin-html
,vite
, - Vue 3 单文件组件支持:
@vitejs/plugin-vue
- Vue 3 JSX 支持:
@vitejs/plugin-vue-jsx
- Vue 2.7 支持:
vitejs/vite-plugin-vue2
- Vue <2.7 的支持:
underfin/vite-plugin-vue2
Eslint集成
我们在awesome-vite
库中可能会看到两个Eslint插件,那么他们有什么不同呢?
在这里了推荐使用@nabla/vite-plugin-eslint
,为什么呢?
相较于vite-plugin-eslint
,上面推荐的可以保持HMR迅速,因为linting是异步完成的,不会阻塞编译的进行,而这个插件会先检查linting再编译,这样我们的HMR就变慢了。
环境变量区分
Vite 使用 dotenv 从你的 环境目录 中的下列文件加载额外的环境变量:
.env # 所有情况下都会加载
.env.local # 所有情况下都会加载,但会被 git 忽略
.env.[mode] # 只在指定模式下加载
.env.[mode].local # 只在指定模式下加载,但会被 git 忽略
mode是怎么配置的,可以在启动vite的时候添加--mode ${你的mode}
遇到的问题
我们使用了非阻塞的eslint会导致构建抛出的告警不终止构建,这个时候就可以在构建前进行手动eslint检查
with语法的问题
在vite启动构建依赖的时候esbuild会出现with语法不能兼容的情况,这个时候这个包只能通过使用js去请求一个 script的方法来规避
npm的包没有指定入口文件
由于vite解析需要依赖入口文件,在没有配置的情况下,需要在引入的时候需要指定到具体的文件
其他注意事项
使用 CommonJS 规范语法,例如 require('**');
1.请转换为ESM语法
- 2.使用 Web API new URL
![img_3.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1987892252d444f1a97f969a5daf34d6~tplv-k3u1fbpfcp-watermark.image?)
包含多个别名的导入,如:
@import '~@/styles/global.scss'
,同时包含了别名 ~ 和 @- 您可以添加别名配置
{ find: /^~@/, replacement: path.resolve(__dirname, 'src') }
到resolve.alias
配置中,并且把该项配置移到别名配置中的第一项
- 您可以添加别名配置
对于 Webpack 语法 require.context
- 在vite中使用import.meta.glob替换
Webpack全局变量变更
使用define配置选项
全局环境变量变更
- process.env 使用import.meta.env代替
装新依赖包后重启发现还是旧的包
需要使用force进行重启
构建生产版本
浏览器兼容性
Vite的默认目标是支持原生ESM和import.meta的浏览器,chrome> 87,这个情况下很显然是不能满足我们的,我们需要配置一个build.target进行改变兼容性,由于vite的转换过程是由esbuild进行的,那么只需要查看一下esbuild的选项,比如我现在迁移的项目是要支持WebRTC的,那么我只需要配置一个
build: {
target: 'chrome55'
}
这样就会转换到兼容的选项了
原理解析
npm包依赖解析的预构建
在浏览器中原生 ES 导入不支持下面这样的裸模块导入,
import { someMethod } from 'my-dep'
上面的代码会在浏览器中抛出一个错误。因为浏览器是找到my-dep的路径的,所以Vite 将会检测到所有被加载的源文件中的此类裸模块导入,并执行以下操作:
- 预构建 它们可以提高页面加载速度,并将 CommonJS / UMD 转换为 ESM 格式。预构建这一步由 esbuild 执行,这使得 Vite 的冷启动时间比任何基于 JavaScript 的打包器都要快得多。
- 重写导入为合法的 URL,例如 /node_modules/.vite/deps/my-dep.js?v=f3sf2ebd 以便浏览器能够正确导入它们。
模块热替换
Vite 的热更新原理,其实就是在浏览器与vite服务端建立了一个 websocket 连接,服务端监听代码变动,当代码被修改后,服务端发送socket消息通知客户端去请求修改模块的代码,完成热更新。
TypeScript
Vite 使用 esbuild 将 TypeScript 转译到 JavaScript,约是 tsc 速度的 20~30 倍,同时 HMR 更新反映到浏览器的时间小于 50ms。
速度对比
启动速度对比
项目 | vite | webpack |
---|---|---|
冷启动 | 4.895s | 1:38.585 s |
热启动 | 2.289s |
vite 热启动时间
webpack启动速度时间
构建速度对比
项目 | vite | webpack |
---|---|---|
带sourceMap | 59.66s | 3:24.553s |
不带sourceMap | 42.09s | 1:27.630s |
可以看到构建的速度是大幅度的缩短了的
vite 带sourceMap时间
vite不带sourceMap时间
webpack 带sourceMap时间
webpack不带sourceMap时间
想要npm link进行vite调试?
在开发的时候难免会有一些需要本地调试的一些包,那么我们应该如何进行配置呢,如果是CJS的包,建议使用ESM的来进行调试,不然只能用--force
进行重启。如果使用的是webpakck,建议升级到Webpack5,配置moudule的输出,详情可以查看webpack的文档进行配置。
配置ESM的npm link
我们首先要知道npm link的原理是配置一个软连接指向另外一个目录,那么我们要让vite不对这个包进行依赖预构建,这个时候就要配置optimizeDeps
的exclude
属性,添加对应的npm包,然后通过--force
进行重启,这个时候你只要改被依赖的包,vite就会自动帮你刷新。浏览器中的network也要记得禁用缓存哦。
现存的问题
文件多的时候,第一次需要预编译的文件很多而且请求非常多,这样会导致第一次启动的白屏时间过长,这里有两个方案可以解决。
- 可以启用h2,这样就不会被限制为h1.1的6个请求。
- 可以配置warmup,让启动的时候就直接预处理所有文件。
好的插件推荐
这个插件可以让你启动vite后立刻进行文件预热,不需要等到请求后再编译,可以有效减少白屏时间
总结
这次迁移上线并没有发现什么异常。总体来说,现在vite已经比较成熟了,在商用项目上已经可以胜任了。而且构建和热更速度有了质的飞跃。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。