下一代web工具-vite实践
vite 为什么出现
在很久以前,浏览器上是没有 ESM 的,缺少对于模块化的支持,从页面中引入 js 的方案一般都是通过 script 进行加载,后来出现了比如 requirejs,commonjs 等相关的方案,之后随着技术的不断进步,我们又出现了 bundle 的方案进行一些模块化的引入,通过 webpack,rollup 等工具的 plugins 进行模块化打包。当然随着我们的项目的日益壮大,业务需求的不断完善,导致我们的工程越来越大,工程的启动时间和 热更新时间越来越长,每更新一次我都会去饮水机那接杯水喝一口,回来之后都不一定能够 OK。很大程度上影响了我们的开发效率和体验,性子比较着急的人当时可能内心就存了一团火,然后发泄给了 PM,出的这是什么需求,但是并不是需求的问题,当然也可能就是需求不爽。
那 vite 就是基于这样一个背景,来解决上述难题,当然不能解决需求问题,这点我认为它做的不够好。
vite 为什么能够解决上述问题
要了解上述问题,我们首先要知道 webpack 的 dev server 是怎么启动的。
Bundle-Based Dev Server
首先来说它们都有一个对应的 js 入口,然后通过入口 js 进行扫描应用的子模块,当这些模块被解析的时候,当然一些动态的模块也会被解析,当这些模块被 bundle 之后,它会把这些 bundlejs 注入到 html 当中,然后才会启动 dev server,等待页面的访问。从这之中我们就能看到整个过程存在的一些问题。首先他会找到整个应用所依赖的所有模块,这也正是导致我们项目变大之后启动就会变的很卡的一个主要原因。虽然有很多模块都是动态加载的,但是要进行对应的 chunk 到 bundle 的操作,其实并不是真正意义上的动态加载。其必须等待所有模块构建完成,即使是分片的模块也需要构建。
ESM-Based Dev Server
ESM 是 es6 提出的概念,也就是可以原生支持 import,当然你得在 script 标签上增加一个 type=’moudle’的属性。当你 import 某一个模块的时候,浏览器会发一个对应的请求,举个例子
这个页面中 html 在 import 的时候,会相应的发一个请求,请求到对应的依赖包之后执行对应的 js 文件
vite 启动的服务是以 index.html 为入口,然后进行对应的解析,找到我们的入口 js 文件,然后再加载下面的子模块,首屏只有少量的 js 模块,其它的一些动态的 js 模块是不需要被加载的,所以不需要 bundle 也能提升我们的页面性能
当然除了上述的不需要 bundle 提高性能以外,ESM 也存在其对应的一些问题,因为 vite 是动态转换的,所以实时转换的一个性能是 vite 面临的一个最大的问题
- transform 的性能问题
- 尽可能使用性能高的工具
- 缓存 transform 的结果(浏览器缓存+HTTP 缓存)
- 非 ESM 模块的一个兼容性问题(TS/JSX…)
- esbuild 进行转换,代替 TSC/Babel
- es-module-lexer 扫描 import 语法
- magic-string 重写 Node 模块的引用路径
- Broswer ESM 不能加载 Node 模块
- Node CJS 模块的兼容,转换成 ESM 模块
- 模块请求数量较多
- 依赖预优化,将 Node 模块打包成一个文件,缓存,根据模块元信息对其进行对应的转换,支持 CJS 模块的 Named import
- 整个操作很耗性能,存在.vite 文件夹,下次再启动的时候检测版本进行复用
- Node 模块一些其它问题
- 依赖预优化工具
- v1: Rollup + @rollup/plugin-commonjs
- v2: esbuild 依赖扫描,包装
ESM HMR
- 构建模块依赖图
- 如果模块含有 import.meta.hot.accept 则将模块标记成 boundary
- 当文件变更时,根据模块依赖图寻找 boundaries
- boundaries 重新加载变更模块并执行更新
- 如果没有查找到 boundaries,页面重新加载
上线方式
但是由于浏览器一些加载性能的问题,我们真正在上线代码的时候还是需要做对应的打包。可以做代码分片,压缩等一系列的事情。使用现有的 rollup 的方式
优势:
- 基于 ESM bundler,和 vite 契合
- 打包产物体积小,执行速度快
- plugin API 灵活,比 webpack 更灵活
- ESM Tree-Shaking
- Bundle Code Size
- 成熟稳定的生态
- 常用来打包 library
劣势:
- 配置 web app 比较复杂
- framework 支持度
vite:
- 内置了并简化了这些配置
- 提供了 framework 模版(vue3,react,svelte,preact)
- 内置常用 plugins(TS/JSX,PostCSS,CSS Modules,Assets…)
- 继承 rollup plugins,并进行了扩展,SSR 支持等
- dev 支持 rollup plugins
- Node 环境加载 ESM,支持 HMR+Plugin
如有疑问,欢迎留言
前端构建工具剖析
- 收集依赖
- es6 转 es5 transform
- 替换 require 和 exports