比 vite 还快?HMR 仅 2ms?Farm:基于 Rust 的极速构建引擎!

3,341 阅读6分钟

Farm(github.com/farm-fe/far… ⭐) 是一个使用 Rust 编写的极速构建引擎,比 Webpack 快 10+ 倍,比 vite 快 5 倍。对于一个基础的 React 项目,性能对比数据如下:

WebpackViteFarm对比
冷启动时间853ms276ms67ms比 webpack 快 12 倍,比 vite 快 4 倍
HMR 时间43ms23ms2ms比 webpack 快 20 倍, 比 vite 快 10倍
onload 时间83ms310ms57ms比 vite 快 5 倍,与 webpack 接近
项目可访问总时间936ms586ms124ms比 webpack 快 8 倍,比 vite 快 5 倍

性能对比的测试仓库地址:github.com/farm-fe/per…

测试机器(Linux Mint 21.1 Cinnamon, 11th Gen Intel© Core™ i5-11400 @ 2.60GHz × 6, 15.5 GiB)

注:因为 Vite 对于源代码是请求时编译,所以此处加上了 “项目可访问时间”(即 “冷启动时间” + “页面加载时间”),作为另一个指标来综合对比性能。

一、Farm 特性

  • 超级快:所有编译均由 Rust 实现,多线程编译,毫秒级项目启动、2ms HMR。比业界同类工具 Webpack 快 10 倍,比 vite 快 5 倍。
  • 丰富的编译能力支持:默认支持 Html、Css、Js、Jsx、Ts、Tsx、静态资源(图片、字体等)等模块的编译能力,所有 web 资源均作为一等公民编译,所有编译能力开箱即用。
  • 懒编译:默认采用按需编译,除首屏需要的模块外,其他模块均在加载时编译!因首屏能展示的内容有限,理论上对任意规模的项目,Farm 都能支持秒启动!
  • 插件化:Farm 的所有能力均由插件实现,任意功能都可以通过插件定制、扩展。Farm 同时支持 Rust 插件以及 Js 插件。
  • 一致性:Farm 在开发环境和生产环境下的编译行为保持一致,开发时所见即最终所得。
  • 局部 Bundle:Farm 会自动识别模块依赖图,基于依赖关系,产物大小等因素,自动生成若干个小 Bundle,提升资源加载速度以及缓存命中率。

目前(2023.03.04),Farm 已经更新到 0.3 版本,基本上支持了上述所有特性。不过 Farm 的生产环境优化,如压缩,tree shake 等还在紧锣密鼓的开发中,请谨慎在生产环境使用。 Farm 完全开源,欢迎共建。

二、快速体验

项目:github.com/farm-fe/far…

官网:建设中...

注意:Farm 需要 Node 16 及以上,如果使用 linux 系统,需要ubuntu 22 及以上(GLIBC 2.32及以上)

Farm 提供了 CLI 一键新建、开发、构建项目。首先, 使用 @farmfe/cli 创建一个 Farm React 项目:

npx @farmfe/cli@latest create

安装依赖:

cd farm-react && npm i

启动项目

npm start

启动成功后如下图:

image.png

三、Why Farm?

随着前端项目规模的不断增长,传统的 js 构建工具(bundler)如 webpack、rollup 等遇到了严重的性能瓶颈,项目启动时间长达数分钟,一次 HMR 耗费数秒或者数十秒,极大地影响了前端开发的效率。

为了解决性能问题,vite 等号称下一代构建引擎的工具(bundle less)应运而生,不对项目进行全量打包,而是使用 esbuild 进行预打包,并基于原生的 es module 以及 dev server 完成项目的模块加载以及编译功能,极大降低了项目启动时间(冷启动数秒,热启动数百毫秒)以及 HMR 时间。

不过 vite 也并非无懈可击,据笔者之前在字节跳动前端架构团队工作的经验,当项目规模巨大的时候(如首屏 1000+ 模块),vite 的首次页面加载时间可能长达数十秒(因为 vite 对于项目内的模块采取请求时编译的策略,且对于源代码不 bundle),刷新时极其卡顿,并且可能由于巨大的请求量导致浏览器崩溃,非常影响开发体验。而且 vite 在开发环境使用 esbuild 进行预编译,而在生产环境使用传统的 bundler rollup,存在较大的不一致,偶然可能会出现开发环境和线上行为不一致的情况,一旦出现不一致,排查起来非常困难以及煎熬。

因此在笔者看来 vite 并不是完美的解决方案,而且 vite 之所以快,esbuild 功不可没,根据 esbuild 官网的数据,esbuild 比 webpack、rollup 等工具快百倍,而 esbuild 是使用 go 语言编写,远快于 js,笔者认为当前的根本问题其实是 js 的效率问题。

因此,笔者设计并实现了 Farm:基于 Rust 语言编写的极速构建引擎,所有编译能力(html、css、ts/js、sass 等)均由 Rust 实现,能实现毫秒级启动项目,对于大部分情况,能将 hmr 时间控制在 10ms 以内。目前 Farm 已经更新到 0.3 版本,支持了所有编译引擎的基本能力(html/ts/js/css、dev server 以及 HMR、Runtime 、懒编译、动态资源加载等)。对于一个基础的 React 项目,在笔者机器(i5 6核12线程,16G),能够做到 68ms 启动,2ms HMR。

四、Farm 架构设计

4.1 设计理念

  • 性能优先:所有编译能力均由 Rust 实现,多线程编译,极致性能体验。默认启用 lazy compilation,动态加载的模块动态编译。
  • 插件化:所有编译能力由插件实现,提供 Rollup 风格的插件系统,确保插件系统的简单易用性。同时支持 Rust 插件以及 Js 插件,兼顾性能以及生态
  • 一等公民:所有 web 资源均视为一等公民编译并生成相应产物。
  • 一致性,兼容性:确保开发环境以及线上环境一致,兼容老浏览器(如 IE11)
  • 局部 bundle:自动根据依赖关系、资源大小等因素,将项目拆分成所有小 bundle,同时解决 bundle 模式的缓存命中问题以及 unbundle 模式的请求数量问题。

4.2 架构设计

Farm 整体的架构图如下:

image.png

Farm 主要分为两个部分,Js 侧以及 Rust 侧

  • Js侧:实现 Farm CLI、Dev Server 以及运行时能力,并通过 napi 与 Rust 构建核心交互。
  • Rust侧:负责核心的编译流程实现、编译上下文等,所有编译过程在线程池中以最大并发量执行。

整个编译流程分成 Build Stage 以及 Generate Stage(借鉴了 Rollup 中的概念,但是和 Rollup 的实现完全不同)。Build Stage 负责解析和编译所有模块,并生成模块图,Generate Stage 中基于模块依赖图进行局部 bundle、runtime 注入、压缩/treeshake(实现中)、产物生成等。

如果希望了解更多细节,可以看:github.com/farm-fe/rfc…

4.3 插件系统

Farm 的插件系统参考了 Rollup,通过定义钩子来干预、增强编译过程,钩子调用流程如下:

image.png

五、Farm 规划

当前 Farm 已经更新到 0.3 版本,实现了大量特性如:

  • Html, Css, Jsx, Js, Tsx, Ts 等资源的编译和产物生成
  • Lazy Compilation 以及 HMR
  • 局部 Bundle
  • Rust 插件以及 Js 插件的加载和执行
  • 提供了 CLI

后续 Farm 将会实现持续迭代,实现:

  • 生产环境优化:压缩、treeshake 等
  • Source Map 生成
  • Css modules 支持
  • Sass 等预编译器的官方插件支持
  • ...

欢迎感兴趣的同学共同建设。

同时我也会持续更新 Farm 系列文章,本文作为 Farm 系列文章的第一篇,后续会持续推出构建引擎原理以及 Rust 教程系列文章,从 0 到 1 掌握 系统学习 Rust 构建引擎!