Farm⭐️ 是基于 Rust 语言编写的下一代 Web 构建引擎,是目前最强大、最快、最稳定的 Rust Web 构建工具。Farm 自 2023 年 3 月份开源 0.3 版本以来,经过众多社区开发者一年的开发和贡献,v1.0 版本终于面世!v1.0 版本支持包括懒编译、持久缓存、Rust/Js插件扩展、局部打包等能力在内的海量特性,同时兼容 Vite 插件生态,在完美解决 Vite 等工具的现有痛点的基础上,兼具极致性能和兼容性,是真正意义上的下一代构建引擎!
目前 Farm 已经生产可用,已有不少企业级项目迁移到 Farm,并且效果非常好!
如下图,在 benchmark 测试 1000 react 组件,Farm 比 webpack 快 20 倍 ,比 Vite 快 10 倍 ,在冷启动、热启动、HMR等方面对比其他工具,均有压倒性的性能优势。Farm 支持并默认启用了一系列的性能优化手段,如 懒编译
、持久缓存/增量构建
、多线程并行编译等
等,项目越大,源文件越多,Farm 的性能优势越大。
基于 Linux Mint 21 / i5 / 16GB,取三次平均得到上述数据,详情参考 benchmark 仓库
一、功能特性
- ⚡ 超级快:核心的编译能力均使用 Rust 编译,最大限度并行编译,秒起项目,毫秒级 HMR,极致性能体验!
- 🚀 增量构建 :模块级磁盘持久缓存,未更改的模块最多只编译一次,热启动时间降低 80% ,搭配懒编译实现对任意规模项目 1s 启动以及预览
- ⚙️ 海量特性 :内置支持
Html
,Css
,js/jsx/ts/tsx
,静态资源
, - 🧰 插件化 & Vite兼容:编译能力由插件实现,兼容 Vite 插件生态,支持 Rust、Js 插件。
- ⏱️ 懒编译:非首屏页面,按需编译,访问具体页面时才会编译,极大提速大型项目启动时间
- ✔️ 生产优化:支持 tree shake、压缩、语法降级、polyfill 等完整生产能力,支持降级到 ES5
- 📦 局部打包 :将模块按照依赖关系打包成 20 - 30 个左右的产物,同时
bundle
和bundless
两种模式的极端,在保证加载性能的同时,提升缓存复用率 - 🔒 一致性:开发和生产使用完全一致的策略,开发所见即生产所得
- 🌳 兼容性:同时兼容现代浏览器和老旧浏览器(ES5)
Farm v1.0 支持 React、Vue、Solid、Svelte 等常用框架,后文的项目演示模块详细介绍了使用 Farm 构建真实项目,以及迁移后的性能收益:
- [React + Arco Pro + Farm 中后台项目](#React + Arco Pro + Farm 中后台项目 "#heading-11"):使用 Farm 构建的 React 中后台项目,500ms 完成编译并且看到页面
- [Vue + Element Plus + Farm 中后台项目](#Vue + Element Plus + Farm 中后台项目 "#heading-12"):从 Vite 迁移到 Farm 的 Vue 中后台项目,迁移后 1s 启动并看到页面,比 VIte 快 5 倍
二、快速体验
Farm 提供了官方模板可以快速创建一个 React、Vue、Solid、Svelte 等项目:
bash
pnpm create farm
然后选择创建的项目类型、特性等,项目创建完成后,然后启动项目:
bash
pnpm start
如下图所示,20s 即可创建 + 启动你的第一个 Farm 项目!
更多细节参考 官方文档
三. 编译特性
4.1 基础编译能力
Farm 内置了 TS/TSX/JS/JSX
, CSS/CSS Modules
, HTML
, 静态资源
等常见模块的编译能力,无需任何配置开箱即用。对于一个 web app 项目,可以直接以 html
作为入口,引入 JS/TS
, css
文件,一个最简化的 React 项目示例如下,除需要安装 @farmfe/plugin-react
以支持 React Jsx 语法转换以及 react refresh 外,其他能力均可以开箱即用。
html
<!-- index.html -->
<html>
<head>...</head>
<body>
<div id="root"></div>
<script src="./src/index.tsx"></script>
</body>
</html>
上述 html 中引入了 ./src/index.tsx
:
tsx
// src/index.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Main } from './main';
import './index.css';
const container = document.querySelector('#root');
const root = createRoot(container);
root.render(<Main />);
通过 import './index.css';
引入 css 文件:
css
/* src/index.css */
#root {
color: red;
}
对于 css modules
、png/svg/font 等静态资源
均提供了开箱即用的编译能力支持:
tsx
// src/main.tsx
// 默认通过 url 方式引入图片
import logo from './assets/logo.png';
// 引入 css modules
import styles from './main.module.css';
export function Main() {
return <img src={logo} class={styles.logo} />;
}
Farm 内置了纯 Rust 实现的基础编译能力,无需配置额外插件,对于 html
, ts/tsx
, css/css modules
, png/svg/font 等静态资源
等类型的文件,基础编译能力完全开箱即用。Farm 将各类型的模块html
, css
, js/ts/tsx
等,均视为一等公民,并通过局部打包将各类型的模块打包 成若干可以生产部署的产物 ,包括 js/ts/css
等不同类型的模块,均可以使用同一套打包规则。
查看文档 编译特性
4.2 懒编译
对于任意动态引入的模块(包括 js/ts
、css
等),将延迟编译,仅在资源真正需要被加载的时候才进行编译,这将极大提升 大型项目的启动速度!例如,对于大型项目,往往有多个动态加载的路由:
ts
// 定义路由并且动态引入
export const routes = [
{
path: '/',
component: () => import('./pages/home.vue')
},
{
path: '/about',
component: () => import('./pages/about.vue')
},
// ...
];
当启用 懒编译
后,import('./pages/home.vue')
将仅在访问 /home
路由的时才编译,其他路由如 /about
在被访问前,./pages/about.vue
及其依赖均不会被编译。通过合理使用 懒编译
+ 持久缓存
,大型项目每个路由 的启动时间都可以被压缩到 1s 以内,极大提升大型项目开发的性能体验!
如上图所示,每次切换路由,都会极速懒编译该路由下的所有依赖模块。
通过 compiltion.lazyCompilation
可以配置,默认在 start 时开启。
ts
import { defineConfig } from '@farmfe/core';
export default defineConfig({
compilation: {
// 配置关闭懒编译
lazyCompilation: false
}
});
4.3 增量构建
Farm 支持将模块级别 的编译产物缓存到本地磁盘,以支持增量构建
,任意模块在没有更改前,永远都只会被重复编译!增量构建能够降低 80% 左右的项目启动时间,对于 React + Arco Pro 中后台项目,冷启动和热启动性能对比数据如下:
冷启动(无缓存) | 热启动(有缓存) | 性能差距 | |
---|---|---|---|
start | 1519ms | 371ms | 热启动减少 75% |
build | 3582ms | 562ms | 热构建减少 84% |
通过 compiltion.persistentCache
可以配置,默认开启。
ts
import { defineConfig } from '@farmfe/core';
export default defineConfig({
compilation: {
// 配置关闭增量构建
persistentCache: false
}
});
当增量构建开启时,Farm 会将影响性能的编译动作以模块粒度全部缓存到 node_modules/.farm
目录下,例如,resolve/load/transform/parse AST
,压缩
、代码生成
等,在下一次编译时,将会从 .farm
目录下面读取缓存,去除大量的重复计算以极大提升编译性能。
更多细节参考文档 增量构建
4.4 生产优化/浏览器兼容
Farm 完全支持生产构建,目前已经有不少企业级项目使用或者迁移到 Farm,并且效果相当好!对于生产构建,Farm 默认开启了以下优化:
- Tree Shake :对于 ESM 模块,所有未被使用的
export
和其他语句将被删除,以减少产物体积 - 压缩:产物(Html/JS/CSS)将会被混淆和压缩,以减少产物体积
- 语法降级和Polyfill :默认会降级到支持
ES2017
规范的浏览器,并自动降级源代码中 ES2017 不支持的语法,同时为不支持的 API 自动 注入 Polyfill
Farm 支持产物降级到 ES5
,如果你的项目有强兼容要求,可通过 targetEnv: 'browser-legacy'
配置:
ts
import { defineConfig } from '@farmfe/core';
export default defineConfig({
compilation: {
output: {
targetEnv: 'browser-legacy'
}
}
})
更多细节可以参考文档 Build For Production
4.5 局部打包
局部打包(
Partial Bundling
)是 Farm 用来打包模块的策略。目标是在bundle
和bundless
之间找到平衡,通过打包相关的模块以提高加载性能,同时不尽量丢失缓存颗粒度
目前,构建工具(比如 webpack
、rollup
)处理模块的主要方法有两种:完全打包
或原生ESM
。但它们都有缺点:
- 对于
完全打包
,打包工具旨在将所有内容打包在一起,然后拆分出来进行优化,但拆分通常难以配置,手动平衡资源加载性能和缓存命中率很难。 - 对于
原生ESM
,每个模块都可以单独编译和缓存,但当项目规模大起来,有数百个或数千模块请求时,会严重影响加载性能。
上图表示
bundle
和bundless
两种极端:bundle
将所有TS/JS/TSX
、CSS
打包成一个输出文件,缓存复用率低,如果拆包,配置复杂且困难;bundless
完全不打包,对于大项目,动辄数百数千个模块,每次刷新页面都伴随大量的模块请求,严重影响性能和开发体验。
因此,我一直在思考是否有一种策略可以避免这两种极端情况 - 也许我们可以进行局部打包
?我们可以直接将项目打包成若干个数量有限
、大小均衡
的资源。我将这种思考命名为 局部打包
( Partial Bundling
)- 在全量打包
和 不打包
之间找到平衡,通过打包相关的模块以提高加载性能,同时不尽量丢失缓存颗粒度。
如上图所示,Farm 通过
局部打包
,将有依赖关系的数千个模块,打包成有限的js
、css
产物,消除依赖的同时限制请求数量,在bundle
和bundless
之间取得一个平衡。
基于 局部打包的思想
Farm 设计了一整套打包算法,并给出了实现。Farm 局部打包
与其它打包工具不同,Farm 不会尝试将所有内容打包在一起再使用像 splitChunks
之类的优化策略拆包。相反,Farm 会将项目直接打包成多个产物文件。例如,如果需要数百个模块来启动和渲染一个 html
页面,Farm 将基于 局部打包
算法尝试将它们直接打包成 20 到 30 个输出文件。
Farm 局部打包
的目标是:
- 减少请求数量和请求层次: 将数百上千个模块请求减少到20-30个请求,避免大量并发请求,以及由于依赖层次结构而必须按照拓扑顺序逐层加载模块,从而加快资源的加载。
- 提高缓存命中率: 相关的模块会尽可能打包到一起,当某些模块发生更改时,确保只有少数输出文件受到影响,因此可以为项目提高缓存命中率。
对于传统打包工具,我们可能很难通过复杂的 splitChunks
或 manualChunks
配置来实现上述目标,但是 Farm 通过 局部打包
原生支持。
关于局部打包的更多细节,参考文档 Partial Bundling。
4.6 插件生态
Farm 采用了插件化的设计,完全可拔插,并且设计了一整套完善插件系统来支持插件生态。当前 Farm 支持如下 4 中类型的插件:
Farm 编译插件
:支持 Rust 插件和 Js 插件,采用 rollup 风格的 hooksFarm Rust 插件
:使用 Rust 编写的插件,具有最佳性能Farm Js 插件
:使用 Js 编写的插件,性能差于 Rust,但编写更方便
Farm 运行时插件
:增强 Farm 的运行时模块系统Vite/Rollup/Unplugin 插件
:Farm 开箱即用支持Vite/Rollup/Unplugin
插件Swc 插件
:Farm 开箱即用支持Swc
插件
Farm 插件钩子概览如下:
Vite/Rollup/Unplugin 的插件的兼容,是基于上述 Farm 插件钩子通过适配器实现
Farm 提供了大量的官方插件来支持常用的编译能力,如:
@farmfe/plugin-react
:支持 React Jsx 转换以及 React Refresh@farmfe/js-plugin-sass
:支持 Sass 编译@farmfe/js-plugin-less
:支持 Less 编译@farmfe/js-plugin-postcss
:支持 Postcss 编译- ...
大部分的 Vite/Rollup/Unplugin
插件在 Farm 中开箱即用:
@vitejs/plugin-vue
: 支持 Vue 编译@vitejs/plugin-vue-jsx
: 支持 Vue Jsx 编译unplugin-auto-import
unplugin-auto-component
- ...
关于在 Farm 中使用各类插件的,参考 Using Plugins,关于配置插件的介绍,参考 Plugins
编写自己的 Farm 插件也相当简单,Farm 提供了插件模板可一键创建:
sh
pnpm create farm-plugin
支持 Rust
插件和 JS
插件,详情参考文档 Writing Plugins。
4.7 SSR 支持
Farm 支持 SSR(Server-Side Rendering,服务端渲染),SSR 可以极大提升首屏的加载性能,提升页面的 SEO 等。基于 Farm 可以实现极速 SSR 构建,体验极致 SSR 性能!
一个典型的 Farm SSR 项目结构如下:
text
.
├── index.html
├── farm.config.ts
├── farm.config.server.ts
├── server.js
└── src
├── index-client.tsx
├── index-server.tsx
└── main.tsx
需要分别提供 client
和 server
入口,然后使用 Farm 构建 client
和 server
入口。Farm 为常用框架提供了 SSR 示例:
关于 SSR 的更多细节,参考文档 Server-Side Rendering
四、真实项目演示
Farm 支持 React、Vue、Solid、Svelte 等常见框架,并且已有真实项目从 Webpack/Vite 迁移到 Farm。如下我们给出了最常用的两种框架(React、Vue)的中后台项目示例。
4.1 React 中后台项目
- 技术栈 :
React
+Arco Pro
+Farm
- 项目地址 :github.com/farm-fe/far...
如上图所示 Farm 可以在 500ms 热启动并渲染出一个复杂的中后台项目。
4.2 Vue 项目
- 技术栈 :
Vue
+Element Plus
+Farm
- 项目地址 :github.com/farm-fe/far...
下图中,下方是 Farm,上方是 Vite,明显可以看到,同为 热启动
+ 全 Vite 插件
编译 Vue 项目的条件下,Farm 比 Vite 快 5
倍。该项目是从 Vite 迁移到 Farm,由于 Farm 兼容 Vite 生态,该项目的所有 Vite 插件都可以直接复用,迁移成本很低。
五、小结
Farm 由开源社区共同开发建设,历经近两年的开发,终于发布了 1.0 版本。1.0 版本包含了 web 构建工具需要的全部能力,通过 Rust 多线程 + 懒编译 + 增量编译等手段,极大提升 web 项目构建性能,提供更加极致的构建性能体验!
未来 Farm 将:
- 继续做更多上层框架的建设,例如 SSR 框架等
- 打造 Rust 插件生态,使用 Rust 实现或者重构现有的常用工具,提供更加极致的性能体验
鸣谢:
- brightwu(吴明亮):Farm 作者和 Lead Maintainer,创建、设计并实现了 Farm 的大部分功能
- ErKeLost:Farm 核心维护者,设计并实现了大部分 Node 侧以及 Rust Resolver 的能力
- shulandmimi:Farm 核心维护者,开发、维护了大量 Farm 插件,实现、优化了大量 Rust Compiler 的内置能力,如压缩、Tree Shake等等
- Farm 组织成员:参与了 Farm 各类特性的优化、开发,Bugfix 等等
- 社区贡献者:参与 Farm 的贡献,协助实现了各类特性和问题修复