6

用 Vite 加速你的生产力

 3 years ago
source link: https://segmentfault.com/a/1190000040422503
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

目前组里的业务组件库因为一些历史背景原因,源码和展示站点分为两个独立的项目工程维护,而市面上对于组件、组件库、工具库的源码和站点在一个仓库维护或者采用 MonoRepo 的方式开发和维护,因此在不改变项目架构的前提下(暂时不要纠结为什么不放在一起维护),我们对于效率协同和工程化方面进行了一系列的演进,下面首先介绍下我们当前面临的问题:

面临的问题

npm link

因为源码和站点维护在两个工程下,在本地开发的时候如何关联两个项目是首先面临的问题,最初我们采用了 npm link 的方式,但是因为组件库和站点均是基于 react hook 开发,因此每次进行组件迭代开发都需要经历下面几步:

  • 站点下: cd node_modules/react && npm link 和 cd node_modules/react-dom && npm link
  • 源码下: npm link react && npm link react-dom
  • 源码下: npm link
  • 站点下: npm link 源码

不难发现,这样的方案有以下痛点(每个新参与开发的同学都会吐槽 diss):

  • 操作繁琐易出错;
  • yarn 和 npm 混和使用使得 link 问题频出;
  • link 断链;

站点 & 源码,双编译

这一版我们完全抛弃了 npm link,采取了比较 hack 的方式:

  • 源码 watch 文件变更,实时编译构建 ESM;
  • 源码 watch ESM 变更,实时同步到站点 node_modules 下;
  • 站点 watch node_modules 下组件 ESM 变更,热更新;

不难发现,这样的方案有以下痛点:

  • 监听 node_modules 变更,姿势很 hack;
  • 双边 watch + 编译,严重消耗内存资源;
  • 热更新时间 = 组件 ESM 编译时间 + 同步推送时间(可以忽略不计) + 站点编译时间,10s+妥妥的;

站点单编译

既然站点和源码都具有编译构建能力,为什么不减少一次编译构建呢?为此我们进行了第三次优化升级:

  • 源码 watch 文件变更,实时同步源码到站点缓存目录下 ;
  • 开发环境下,站点 import node_modules 下 ESM 变更为 import 缓存目录下组件源码;
  • 站点 watch 缓存目录下组件源码变更,热更新;
    虽然这次升级后已经解决了大部分的问题,但是还是存在问题。

源码本地开发的优化使得站点编译压力的增加:

  • 热更新慢:改个文案 hotreload 2s +;
  • 冷启动更慢:60s 妥妥的;

其实到现在这一步问题已经一定程度往 webpack 衍生方案的通病上靠了,因此,我们把目标聚焦在了新的构建工具上。

为什么选择 Vite ?

下面我列举了一常见的构建工具以及以及一些选型思考。

首先是 Webpack 以及 Webpack 衍生方案:

  • 基于 CRA 封装的业务脚手架;
  • Webpack 轻量级配置;

基于 Webpack 的方案和目前使用的脚手架差异不大,收益不明显,并且从根本上也解决不了 Webpack 带来的生产力问题。

接下来是目前比较火的基于 ESM 的现代方案:

  • Snowpack

关于比较尤大的文章更有说服力:对比

此外,就我个人而言,我觉得尤大在华人社区以及国内的影响力更大一些,因此尤大开源的工具在国内社区活跃度会更高一些,关注度也会更高,对于后续的可持续发展也比较有利,因此在选型上我们选择了 Vite。

  • 秒级冷启动:60s+ => 3s- (缓存生效后 300ms 左右)
  • 毫秒级热更新:2s+ => 1s- (sync + hotreload)

喜大普奔,收效显著!!!

Vite 为什么快?

首次冷启动:esbuild 预编译,生成缓存

1.png
4.png
2.png

再次冷启动:利用缓存

3.png

首先冷启动为什么快,因为 Vite 基于原生 ESM,也就是说 Vite 将原来 Webpack 前置构建 bundle 的工作交给了性能强悍的现在浏览器来做,所以冷启动时间大幅缩减(毕竟冷启动最耗时的就是分析代码构建 bundle)。

而在 Vite 中,正如官方文档介绍的那样,Vite 将我们的代码氛围了依赖和源码两部分:

  • 依赖:大多为在开发时不会变动的纯 JavaScript。Vite 将会使用 esbuild 预构建依赖。esbuild 使用 Go 编写,并且比以 JavaScript 编写的打包器预构建依赖快 10-100 倍
  • 源码:通常包含一些并非直接是 JavaScript 的文件,需要转换(例如 JSX,CSS 或者 Vue/Svelte 组件),时常会被编辑。同时,并不是所有的源码都需要同时被加载(例如基于路由拆分的代码模块)

bundle based dev server

以 Webpack 为代表的“bundle based dev server”,在冷启动前需要依赖诸如 babel 之类的工具对代码进行分析并编译生成可运行的 bundle,这一构建时的过程造成了冷启动时间过长。

ESM based dev server

而对于“ESM base dev server”来说,dev server 依赖的是我们 ESM,而我们的代码本身就是以 ESM 编写的,因此进去要告知 dev server 目标模块路径加载即可,编译解析交给浏览器的运行时去处理,从而大大加速了我们的冷启动。

基于原生 ESM

在 Vite 中,HMR 是在原生 ESM 上执行的。和冷启动一样的道理,当我们修改了一个文件后,不需要在重新编译了,热更新也就大大减少耗时了。

配合 http 头

Vite 充份利用了 http 缓存,这也是我们在本地开发时候看到的一个和 webpack 比较大的不同,network 中充满了大量模块请求。

  • 源码部分:304 配合 ETag 协商缓存

5.png

  • 依赖模块:强缓存

6.png

Webpack to Vite ?

那么如何平稳的从 Webpack 迁移到 Vite ? 其实,当你按照 Vite 文档落地的时候已经兼容 80%的 Webpack 场景。只需要考虑一些额外 case:

ESM 及 ESM 衍生问题

Vite 利用了 esBuild 去预构建 ESM,因此对包的要求相对严格,对于 esBuild 预编译失败的场景需要 case by case 处理。

esBuild 预编译失败

这里以 react-virtualized 为例,react-virtualized 的 WindowScroll.js 下引入了 flow 的类型文件导致预编译失败。

可以写 esBuild 插件、写 resolutions、拉包本地改。

888.png

9.png

dep-scan 依赖分析失败

以 react-infinite-scroller 为例,他在 package.json 中 ESM 读取目录指向了发包后被忽略的 src 源码目录,导致 dep-scan 插件查找模块失败。

777.png

ESNext bundle

Vite 构建过程由 esBuild 接管,而 esBuild 支持的 target 的最低版本为 es6,因此想要构建兼容性更强的 bundle 需要对 Vite 的产出二次编译或者使用官方给出的基于 SystemJS 的方案。

下面这张图是截取的 Vite 官网的兼容性一节:

loadimage.png

使用官方插件:

  • babel 转译并注册为 System.js 模块;
  • 添加 System.js 运行时;

6666.png

向我们组件库的展示站点或者内部系统的工程项目说用了就用了,但是实际投入到对外的项目时我们也不得不考虑一些问题:

单说从 Webpack 切换到 Vite 成本很低,甚至比 Webpack 的大版本升级还要简单。但是从 Webpack 到 Vite 不仅仅是构建工具的转换而是整个生态的迁移,那么对于历史项目已存在的 babel 插件、Webpack 插件等都需要等量替换,这是一个成本。此外,就像上一节说的那些,对于一些不规范的三方包我们也需要做额外的适配,这也是一个成本。

从目前看,Vite 带来的工程效率收益比较高,但是就我个人而言尝试的仅是简单的展示站点,并不能严格和实际的业务工程划等号,并且企业级项目工程量和复杂度都比较大,Vite 是否能符合预期的带来收益我个人还不敢保证。

最后,因为涉及到了 ESM,就算官方给出了 SystemJs 版本的兼容插件,但是实际投入对外项目的兼容性风险也还是未知的。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK