一、Farm及其优势
1. 什么是Farm
Farm 是一个非常快的基于 Rust 的 Web 构建工具,用于构建web和nodejs应用。Fram提供快速的编译和hmr,为大型前端项目提供更加极致的编译效率。
2. Farm与传统构建器的区别
2.1 vite/snowpack
此类工具主要有下面三个特性:
- 使用原生 ESM,在 dev server 启动时不对源文件进行编译和打包,源文件在入口模块执行时才会通过浏览器请求 dev server 编译,编译后的产物返回给浏览器
- HMR 时,只重新编译一个模块,这样 HMR 的时间约等于一个模块的编译时间
- 对外部依赖(如 node_modules 下的依赖)进行预打包
依然存在的弊端:
- 大量的模块请求:对于一个大型项目,使用上述策略 1、2,首次启动可能需要加载数千个模块,使用原生模块系统加载数千个模块会导致浏览器卡住甚至崩溃。虽然 Dev Server 的启动快了,但是请求时的模块的首次编译依然会耗费大量时间,会有数十秒起步的白屏,编译时间只是转移了,并没有消失。
- 开发和生产之间的不一致:出于兼容性和请求数量、请求层级的考虑,原生 ES 模块在大多数情况下无法在生产中使用。 因此 vite 选择在生产环境选择用 rollup 打包。 这就带来了不一致,当这种不一致导致生产错误时,调试起来非常困难且非常痛苦。 而 vite 在 dev 中使用 esbuild,在生产中使用 rollup,这更加就放大了不一致性。
- 拆包优化困难: 受限于 Rollup,拆包配置不够灵活、易用。
2.2 Farm
Farm的设计理念:
- 性能优先:一切都会用 Rust 编写,只有少数不是性能瓶颈的部分会用 JS 编写
- 一致性:默认情况下确保开发和生产完全相同,您在开发中看到的将与您在生产中得到的相同。
- 局部打包:Farm 的打包目标不是将所有东西打包在一起,而是限制资源的请求数量。 Farm 会根据依赖关系和资源大小将项目捆绑成 20-30 个小资源,在不损失缓存粒度的情况下获得最佳的资源加载性能。
- 所有资源视为一等公民:Farm 不再需要将所有内容转换为 Javascript,它将任何内容视为一等公民,如 html、js/jsx/ts/tsx、css/scss、png/svg/...都是 Farm 支持的基础模块,更多类型的模块可以通过插件支持。
- 兼容性:Farm 将适用于旧版 (ES5) 和现代浏览器。
- Rollup 风格的插件系统:轻松创建自己的插件,并轻松从 rollup/vite/webpack 迁移您的插件/项目。兼容 Vite/Rollup/Unplugin 插件
二、Farm的基础配置
1. 配置文件规范
默认情况下,Farm 从项目根目录下的“farm.config.ts|js|mjs”文件中读取配置。
import { defineConfig } from "@farmfe/core";
export default defineConfig({
root: process.cwd(), // // 编译的根目录
// 编译选项
compilation: {
//...
},
// 开发服务器选项
server: {
hmr: true,
//...
},
// 插件配置
plugins: [],
});
2. 编译(complilation)配置
所有与编译相关的配置都在 compilation 配置对象中,这里介绍一些常用的配置,弯针配置参考官方文档 Farm | 编译配置
// ...
export default defineConfig({
compilation: {
//...
}
// ...
});
2. 1 Input、output
Input项目的入口点。 Input 的文件可以是html、ts/js/tsx/jsx、css 或通过插件支持的其他文件。
import { defineConfig } from "@farmfe/core";
export default defineConfig({
compilation: {
input: {
index: "./index.html",
//对于多页面程序,可配置多个页面的入口文件
//about: "./about.html",
},
},
// ..
})
Ouput项目的输出目录。ouput的interface 定义如下:
interface OutputOptions {
// 局部打包后,入口文件所在资源的文件名配置
entryFilename?: string;
// 局部打包后,除入口资源外的其他资源输入文件名配置
filename?: string;
// 输入目录,默认'dist'
path?: string;
// public path:资源加载前缀
publicPath?: string;//publicPath: process.env.NODE_ENV === 'production' ? 'https://cdn.com' : '/'
// 静态资源文件名配置
assetsFilename?: string;
// 目标执行环境,浏览器或者 Node
targetEnv?: "browser" | "node";
// 输出模块格式
format?: "cjs" | "esm";
}
ouput.targetEnv
默认值:
"browser-es2017"
Farm 会自动为您指定的
targetEnv
注入polyfill
和降级语法(对于脚本和 css),支持的targetEnv
如下:
browser:
browser-es2017
:将项目编译到原生支持async wait
的浏览器。browser-es2015
:将项目编译到原生支持es6 features
的浏览器。browser-legacy
:将项目编译为ES5
,例如IE9
。 请注意,这可能会引入大量的填充,从而使生产规模更大。 确保您确实需要支持IE9
等旧版浏览器。browser-esnext
:将项目编译到最新的现代浏览器,不会注入任何polyfill。browser
:browser-es2017
的别名
nodejs:
node16
:将项目编译到Node 16
。node-legacy
:将项目编译到Node 10
。node-next
:将项目编译到最新的 Node 版本,不会注入任何 polyfill。node
:node16
的别名
2.2 resolve
配置解析依赖相关的选项,interface定义如下:
interface ResolveOptions {
extensions?: string[];
alias?: Record<string, string>;
mainFields?: string[];
conditions?: string[];
symlinks?: boolean;
strictExports?: boolean;
}
resolve.extensions
配置解析依赖时的后缀,默认值:
["tsx", "ts", "jsx", "js", "mjs", "json", "html", "css"]
,当解析一个文件时,如./index,未找到时,会按照配置顺序依次匹配,直到找到。resolve.alias
export default defineConfig({ compilation: { resolve: { alias: { "/@": path.join(process.cwd(), "src"), stream$: "readable-stream", "$__farm_regex:^/(utils)$": path.join(process.cwd(), "src/$1"), }, }, }, });
配置解析路径别名。默认值:
{}
。如/@/pages
将会被替换为,/root/src/pages
。
2.3 define
全局变量注入,配置的变量名和值将会在编译时注入到产物中。Farm 默认注入 process.env.NODE_ENV
以及部分 Farm 自身使用的变量比如 FARM_HMR_PORT
export default defineConfig({
compilation: {
define: {
MY_VAR: 123,
},
},
});
2.4 mode
默认值: 对于 start、watch
命令是 development
,对于 build
命令是 production
配置编译模式,为了优化开发时性能,在没有手动配置生产优化相关选项(minify,tree shake 等)时,默认在 development 下会禁用生产环境优化比如压缩和 tree shake,在 production 模式下启用。
2.6 assets
配置额外视为静态资源的文件后缀,通过assets.include
配置。如下面把markdown文件视为静态文件。
export default defineConfig({
compilation: {
assets: {
include: ["md"],
},
},
});
2.7 css、script
css
module.path:
默认值:
["\\.module\\.(css|scss|sass|less)"]
配置哪些路径对应的模块会被视为 CSS 模块。配置使用正则字符串。默认是以
.module.(css|scss|sass|less)
结尾的文件。
module.indentName:
配置生成的 CSS Modules 类名,默认是
[name]-[hash]
,[name]
,[hash]
为占位符(也是目前支持的所有占位符)。[name]
表示原始类名,[hash]
表示该css 文件 id 的 hash。script
target:
配置 Farm 解析js/jsx/ts/tsx
的 AST 以及生成代码时支持的 ES 语法版本。 可选值:es5
,es6
,es2015
-es2023
,esnext
。
2.8 partialBundling
配置 Farm 局部打包的行为。
export interface FarmPartialBundlingConfig {
targetConcurrentRequests?: number;
targetMinSize?: number;
targetMaxSize?: number;
groups?: {
name: string;
test: string[];
groupType?: "mutable" | "immutable";
resourceType?: "all" | "initial" | "async";
}[];
enforceResources?: {
name: string;
test: string[];
}[];
enforceTargetConcurrentRequests?: boolean;
enforceTargetMinSize?: boolean;
immutableModules?: string[];
}
targetConcurrentRequests
默认值:25
。Farm 尝试生成尽可能接近此配置值的资源数量,控制初始资源加载或动态资源加载的并发请求数量。targetMaxSize、targetMinSize
默认值:targetMinSize:20 * 1024 bytes, 20 KB、targetMaxSize:1500 * 1024 bytes, 1500 KB
分别代表minify 和 gzip 之前生成的资源的最大和最小体积。targetMinSize
并不一定保证满足,可以配置enforceTargetMinSize
可用于强制限制最小的大小。
2.9 lazyCompilation、treeShaking、minify
lazyCompilation
默认值: 在开发模式是
true
,构建模式是false
是否启用懒编译,配置为 false 关闭。参考 懒编译。treeShaking
默认值: 在开发模式是
false
,构建模式是true
是否启用 tree shake,配置为 false 关闭。参考 Tree Shake。minify
默认值: 在开发模式是
false
,构建模式是true
类型:
bool | JsMinifyOptions
是否启用压缩,开启后将会对产物进行压缩和混淆。参考 压缩。minify.include
默认:
[],类型string[]
包含需要压缩的模块,默认全部,仅在minify.mode
为minify-module
生效minify.exclude
默认:
["*.min.(js|css|html)"],类型string[]
排除不需要压缩模块,仅在minify.mode
为minify-module
生效minify.mode
默认值:
'minify-module',类型'minify-module' | 'minify-resource-pot'
。minify-module
模块级别minify
,可以通过参数控制需要 minify 哪些模块,压缩的更为精细,效率更好。minify-resource-pot·
ResourcePot
级别minify
,无法通过参数控制具体的模块
2.10 presetEnv
配置对哪些模块注入语法降级。默认在开发环境为false,构建模式为true。presetEnv的接口定义如下:
type FarmPresetEnvConfig =
| boolean
| {
include?: string[];
exclude?: string[];
// TODO using swc's config
options?: any;
assumptions?: any;
};
include
默认值:
[]
配置额外包含哪些模块进行语法降级。exclude
默认值:
['node_modules/']
。默认不对node_modules目录进行polyfill,如果需要请使用include添加。option
默认值:降级到es5,详细参考
2.11 comments
配置如何处理注释,默认值license
:
- true: 保留所有注释
- false: 删除所有注释
- license: 保留所有 LICENSE 注释, 移除所有非 LICENSE 注释
3. 开发服务器(devServer)配置
配置Farm 的运行相关配置项,所有的配置都在server
中进行配置。如下是一个配置示例:
import type { UserConfig } from "@farmfe/core";
function defineConfig(config: UserConfig) {
return config;
}
export default defineConfig({
server: {
port: 9000,
// ...
},
});
3.1 port
dev server的运行监听端口,默认值:9000
3.2 hmr
默认对于 farm start
命令开启,其他命令关闭。
启用 HMR,将会监听编译过程中涉及到的模块的变动,当模块变化时,自动触发重编译并将结果推送给 Farm Runtime 进行更新。端口和主机等配置可在hmr中进行配置,比如:
import type { UserConfig } from "@farmfe/core";
function defineConfig(config: UserConfig) {
return config;
}
export default defineConfig({
server: {
hmr:{
port:9801,//默认值
host:"localhost"//默认值
}
// ...
},
});
3.3 proxy
配置服务器代理。基于 http-proxy 实现,具体选项参考其文档,示例:
import type { UserConfig } from "@farmfe/core";
function defineConfig(config: UserConfig) {
return config;
}
export default defineConfig({
server: {
proxy: {
'/api':{
target:"https://xxzxka.eu.org/app",
changeOrigin:true,
pathRewrite: (path: any) => path.replace(/^\/api/, ""),
}
},
},
});
3.4 open
配置完成编译运行后是否自动打开浏览器到对应的页面。默认值:false
3.5 plugins
配置 Farm 的 Dev Server 插件,通过 Dev Server 插件可以扩展 DevServer 的上下文,添加 middleware 等。
export function hmrPlugin(devServer: DevServer) {
const { config, logger } = devServer;
if (config.hmr) {
devServer.ws = new WebSocketServer({
port: config.hmr.port,
host: config.hmr.host,
});
devServer.app().use(hmr(devServer));
devServer.hmrEngine = new HmrEngine(
devServer.getCompiler(),
devServer,
logger
);
}
}
4. 通用配置
4.1 目录(dir)配置
envdir、envPrefix
配置目录以加载 .env
、.env.development
、.env.Production
文件。 默认情况下它与 root 相同。
import { defineConfig } from '@farmfe/core';
import { resolve } from 'path';
export default defineConfig({
envPrefix: ['FARM_', 'CUSTOM_PREFIX_', 'NEW_'],
envDir: resolve(process.cwd(), './env'),
});
publicDir
默认:public
publicDir
下的文件将始终被视为静态资源。 在 dev 时可以通过 dev server 直接访问,在构建时会将其复制到 output.path
。
比如将字体等静态资源添加到 public
目录,并将它们用作 /xxx.ttf
。
4.2 插件(Plugins)配置
Rust插件
Rust插件使用package name配置使用,默认值[]
,在字段plugins
中声明配置要使用的插件。当需要配置插件项时,使用[packageName,optionsObject]格式。具体如下:
export default defineConfig({
// ...
plugins: [
//基本语法配置
"@farmfe/plugin-sass"
// 使用数组语法来配置 Rust 插件
[
// Rust 插件的名称
"@farmfe/plugin-sass",
// Rust 插件的选项
{
additionalData: '@use "@/global-variables.scss";'
}
],
],
});
Js插件
Js插件本质就是一个函数对象。默认值{}
,在字段plugins
中声明配置要使用的插件。
import farmPostcssPlugin from "@farmfe/js-plugin-postcss";
export default defineConfig({
plugins: [
farmPostcssPlugin({
// ... 配置 postcss 选项
})
],
});
可以通过priority
配置插件的优先级,插件本身返回一个配置对象,可以结合对象的展开和priority
组合,如farmPostcssPlugin:
import farmPostcssPlugin from "@farmfe/js-plugin-postcss";
export default defineConfig({
plugins: [
{
...farmPostcssPlugin(
{
//插件的配置项目
}
),
//内部插件的优先级都是100,如果想让插件先执行,就设置大于100,否则设置小于100。
priority:100
}
],
});
提示
Js插件必须要实现filter,因为Js的速度相较于Rust很慢,通过filter过滤掉不需要处理的类型,可以提高运行速度。
Vite/Rollup/Unplugin插件
(1) 使用vite插件
Farm 兼容 Vite 插件,默认值[]
,在字段vitePlugins
中声明配置要使用的插件。首先安装vite插件:
npm add @vitejs/plugin-vue @vitejs/plugin-vue-jsx vite -D
然后在farm.config.ts
中的vitePlugins
直接配置:
import vue from '@vitejs/plugin-vue',
import vueJsx from '@vitejs/plugin-vue-jsx';
export default defineConfig({
// 配置vite插件
vitePlugins: [
vue(),
vueJsx()
]
});
为了提高 vite 插件的性能,您可以使用返回过滤器
的函数语法
:
import vue from '@vitejs/plugin-vue',
// // 使用Farm 中 Vite 插件的函数语法
function configureVitePluginVue() {
// 返回插件及其过滤器
return {
// 使用 vue 插件
vitePlugin: vue(),
// 为其配置过滤器。 不匹配的模块路径将被跳过。
filters: ['\\.vue$', '\\\\0.+']
};
}
export default defineConfig({
vitePlugins: [
configureVitePluginVue
]
});
(2)使用unplugin插件
在Farm中使用unplugin插件,在字段vitePlugins
中声明配置要使用的插件。,可以实现引入第三方组件库以及AutoImport等功能。
安装:
npm add unplugin-auto-import unplugin-vue-components -D
使用:
import vue from '@vitejs/plugin-vue',
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
vitePlugins: [
vue(),
// ...
AutoImport({
resolvers: [ElementPlusResolver({ importStyle: 'sass' })],
}),
Components({
resolvers: [ElementPlusResolver({ importStyle: 'sass' })],
}),
]
});
本文由 yuin 创作,
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。