ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

独立使用 MFSU:把 umi 的依赖预构建加速方案接入你的 webpack 项目

独立使用 MFSU:把 umi 的依赖预构建加速方案接入你的 webpack 项目 独立使用 MFSU把 umi 的依赖预构建加速方案接入你的 webpack 项目【免费下载链接】umiA framework in react community ✨项目地址: https://gitcode.com/GitHub_Trending/um/umi本文面向所有在非 umi 项目如自建 webpack 项目、CRA 改造项目中使用 webpack 的开发者完整讲解如何独立安装、配置和运行umijs/mfsu涵盖实例初始化、devServer 中间件、源码转换器Babel / esbuild 两种方案与 webpack 配置注入的四个关键步骤。读完本文你将能够在自己的 webpack 4 / webpack 5 项目中复现 umi 的 MFSU 体验——用 esbuild 完成依赖的快速预构建并享受二次启动时的缓存加速。MFSUModule Federation Speed Up是 umi 生态中基于 webpack Module Federation 的依赖预构建方案它把node_modules中的第三方依赖预先打包成独立的联邦模块业务代码在开发时只编译自身从而显著缩短冷启动与热重启时间。在 umi 项目中它开箱即用而本文要讲的是它的另一种能力——脱离 umi 独立使用将其接入任何基于 webpack 的工程。示例项目与安装官方仓库在 examples/mfsu-independent 提供了一个可直接运行的独立示例一个使用antdframer-motionreact18的最小项目README 描述其目标是在无缓存的情况下“一秒内启动”项目。示例包含两份 webpack 配置——webpack.config.jsBabel 方案与 webpack.config.esbuild.jsesbuild 方案分别对应下面第 3 步的两种转换器选择。安装只需一个命令把umijs/mfsu作为开发依赖加入项目pnpm add -D umijs/mfsu示例项目的 package.json 中umijs/mfsu与webpack、webpack-dev-server、babel-loader、esbuild并列放在devDependencies并提供了两个启动脚本dev: webpack serve --config webpack.config.js, dev:esbuild: webpack serve --config webpack.config.esbuild.js即pnpm dev走 Babel 转换器、pnpm dev:esbuild走 esbuild 转换器二者共用同一套 MFSU 实例初始化逻辑方便对照两种方案的效果。配置 MFSU完整四步配置 MFSU 一共需要四步操作。有一点需要始终牢记请确保以下所有行为都只在开发环境生效生产构建不应被 MFSU 侵入具体做法见文末“常见问题”。第 1 步初始化实例第一步是初始化一个MFSU实例它是后续所有能力的基础// webpack.config.js const { MFSU } require(umijs/mfsu); const webpack require(webpack); // [mfsu] 1. init instance const mfsu new MFSU({ implementor: webpack, buildDepWithESBuild: true, });从 MFSU 类实现 可以看出构造函数会完成一系列默认值装配mfName默认取常量DEFAULT_MF_NAME值为mf见 constants.ts缓存目录tmpBase默认是${process.cwd()}/.mfsu模式默认development并依据strategy参数选择“编译期收集”或“静态分析”两种依赖收集策略。implementor是必须传的——它指定与项目内实际使用完全一致的webpack 唯一实例通常就是require(webpack)MFSU 会用它来构造依赖侧的联邦构建。第 2 步添加 devServer 中间件第二步把 MFSU 的中间件挂到webpack-dev-server上。这些中间件负责托管 MFSU 打包后产出的远程资源remoteEntry.js与各依赖 chunk。webpack 5// webpack.config.js module.exports { devServer: { // [mfsu] 2. add mfsu middleware setupMiddlewares(middlewares, devServer) { middlewares.unshift(...mfsu.getMiddlewares()); return middlewares; }, }, };webpack 4// webpack.config.js module.exports { devServer: { // [mfsu] 2. add mfsu middleware onBeforeSetupMiddleware(devServer) { for (const middleware of mfsu.getMiddlewares()) { devServer.app.use(middleware); } }, }, };getMiddlewares()的底层实现在 mfsu.ts#L306-L354它会拦截请求路径前缀为mf-va_、mf-dep_、mf-static/对应常量MF_VA_PREFIX、MF_DEP_PREFIX、MF_STATIC_PREFIX的资源请求等待依赖构建完成后从tmpBase目录读取产物并返回非 MFSU 请求则直接放行交给next()。其中remoteEntry.js之外的文件还会被设置cache-control: max-age31536000,immutable长缓存这也是二次启动加速的重要来源中间件末尾还有一个express.static(tmpBase)兜底用于处理依赖构建产物中指定了 chunk 名的场景。第 3 步配置源码转换器转换器的作用是收集并改写业务代码中的依赖导入路径把它们替换为 MFSU 模块联邦的地址也就是第 2 步中间件所提供的资源。官方提供两种方案babel plugins与esbuild handler一般情况下选择babel plugins即可。方案 ABabel Plugins向babel-loader的plugins中展开mfsu.getBabelPlugins()即可// webpack.config.js module.exports { module: { rules: [ // handle javascript source loader { test: /\.[jt]sx?$/, exclude: /node_modules/, use: { loader: babel-loader, options: { plugins: [ // [mfsu] 3. add mfsu babel plugins ...mfsu.getBabelPlugins(), ], }, }, }, ], }, };getBabelPlugins()在 mfsu.ts#L356-L358 中返回[this.strategy.getBabelPlugin()]即当前策略编译期收集或静态分析生成的 babel 插件。对应的插件实现位于 packages/mfsu/src/babelPlugins/awaitImport核心逻辑是把import x from antd这类依赖导入改写为对联邦远端模块的访问。方案 BEsbuild handler另一种方案是用umijs/mfsu内置导出的esbuildLoader处理js/ts资源。该方案仅用于开发环境。info使用这种方案的好处是在开发环境获得比babel更快的编译和启动速度。// webpack.config.js const { esbuildLoader } require(umijs/mfsu); const esbuild require(esbuild); module.exports { module: { rules: [ { test: /\.[jt]sx?$/, exclude: /node_modules/, use: { loader: esbuildLoader, options: { handler: [ // [mfsu] 3. add mfsu esbuild loader handlers ...mfsu.getEsbuildLoaderHandler(), ], target: esnext, implementation: esbuild, }, }, }, ], }, };这里的关键参数语义如下esbuildLoader由 packages/mfsu/src/index.ts 显式导出专供“独立使用”场景其 loader 实现见 packages/mfsu/src/loader/esbuild.ts会根据文件扩展名.js/.jsx/.ts/.tsx等自动映射 esbuild 的 loadertarget未指定时默认es2015handlermfsu.getEsbuildLoaderHandler()返回的处理器数组会在 esbuild 转换完成后拿到code / imports / exports / filePath类型定义见 types.ts 中的IEsbuildLoaderHandlerParams负责执行与 babel 插件等价的导入路径改写implementation传入项目中已安装的esbuild实例避免 loader 内部引入重复版本。warning什么时候我不应该使用 esbuild 方案我有自定义的babel plugins必须在开发环境使用我需要显示css-in-js的开发环境友好类名一般由 babel plugin 提供支持在开发环境多适配一套esbuild-loader的成本大于配置babel plugins的成本。第 4 步注入 webpack 配置第四步调用mfsu.setWebpackConfig来改变你的 webpack 配置。它的行为是增量的——只做添加和改写不会破坏你原有的配置内容。由于这是一个异步方法需要把原始配置抽成对象config再导出调用后的返回值// webpack.config.js const config { // origin webpack config }; const depConfig { // webpack config for dependencies }; // [mfsu] 4. inject mfsu webpack config const getConfig async () { await mfsu.setWebpackConfig({ config, depConfig, }); return config; }; module.exports getConfig();从源码看setWebpackConfigmfsu.ts#L125-L261内部做了这些事收集 alias 与 externals把config.resolve.alias和config.externals快照到 MFSU 实例上供依赖构建与转换器复用改写入口把原始 entry 拆解为虚拟入口mfsu-virtual-entry/目录下的虚拟模块通过await import()异步加载真实入口并透传其具名导出确保业务代码的导出语义不变该虚拟模块机制还依赖 webpack 的experiments.topLevelAwait: true代码中会显式lodash.set打开注入插件追加WebpackVirtualModules、ModuleFederationPluginname 为__remote 指向mf${publicPath}/mf-va_remoteEntry.js其中REMOTE_FILE_FULL mf-va_remoteEntry.js以及依赖构建插件BuildDepPlugin解析 publicPathresolvePublicPath会把auto归一为/作为联邦远端地址的基础。而depConfig是依赖构建侧独立的 webpack 配置。在示例项目中它给出了依赖构建所需的resolve.extensions与babel-loader规则不需要exclude和 MFSU 插件因为依赖构建由 MFSU 自己驱动const depConfig { output: {}, resolve: { extensions: [.ts, .tsx, .js, .jsx], }, module: { rules: [ { test: /\.[jt]sx?$/, use: { loader: babel-loader, options: { presets: [ babel/preset-env, babel/preset-react, babel/preset-typescript, ], }, }, }, ], }, plugins: [], };到此为止MFSU 配置完毕可以启动项目了。一份完整的可运行配置把上述四步串起来并补齐 devServer 端口、mode: development、publicPath: /等常规字段就是示例 webpack.config.js 的完整形态const path require(path); const webpack require(webpack); const { MFSU } require(umijs/mfsu); // [mfsu] 1. init instance const mfsu new MFSU({ implementor: webpack, buildDepWithESBuild: true, }); const config { entry: path.join(__dirname, ./src), mode: development, output: { path: path.join(__dirname, ./dist), filename: bundle.js, publicPath: /, }, devServer: { // [mfsu] 2. add mfsu middleware setupMiddlewares(middlewares, devServer) { middlewares.unshift(...mfsu.getMiddlewares()); return middlewares; }, }, resolve: { extensions: [.ts, .tsx, .js, .jsx], }, module: { rules: [ { test: /\.[jt]sx?$/, exclude: /node_modules/, use: { loader: babel-loader, options: { presets: [ babel/preset-env, babel/preset-react, babel/preset-typescript, ], plugins: [ // [mfsu] 3. add mfsu babel plugins ...mfsu.getBabelPlugins(), ], }, }, }, ], }, plugins: [ new (require(html-webpack-plugin))({ template: path.resolve(__dirname, ./index.html), }), ], stats: { assets: false, moduleAssets: false, runtime: false, runtimeModules: false, modules: false, entrypoints: false, }, }; const depConfig { /* 见上文 */ }; // [mfsu] 4. inject mfsu webpack config const getConfig async () { await mfsu.setWebpackConfig({ config, depConfig }); return config; }; module.exports getConfig();esbuild 版本与它的唯一差别在第 3 步——把babel-loader规则整体替换为esbuildLoader见 webpack.config.esbuild.js其余四步结构完全一致。使用缓存目录与启动验证完成四步配置后启动项目你会在项目根目录看到.mfsu文件夹这就是MFSU 缓存文件夹里面存放着依赖预构建的产物与缓存元数据。这些是构建缓存不应该提交到版本库请在.gitignore中加入# .gitignore .mfsu对应源码中的常量DEFAULT_TMP_DIR_NAME .mfsuconstants.ts——这也是tmpBase选项的默认值来源。符合预期时你可以立刻感受到 MFSU 带来的好处esbuild 对依赖的快速打包依赖构建走DepBuilder.buildWithESBuild见 depBuilder.ts构建完成后会输出[mfsu] compiled with esbuild successfully in X ms的时间日志以及二次热启动的提速.mfsu缓存命中后buildDeps会打印[MFSU] skip buildDeps并直接复用既有产物见 mfsu.ts#L263-L304。其他配置项除了implementor和buildDepWithESBuildMFSU实例还支持以下常用配置const mfsu new MFSU({ cwd: process.cwd(), });完整 Options 说明optiondefaultdescriptioncwdprocess.cwd()项目根目录getCacheDependency() {}用返回值来对比使 MFSU cache 无效的函数tmpBase${process.cwd()}/.mfsuMFSU 缓存存放目录unMatchLibs[]手动排除某些不需要被 MFSU 处理的依赖runtimePublicPathundefined同 umi 的runtimePublicPathimplementorundefinedwebpack 实例需要和项目内使用的唯一实例一致buildDepWithESBuildfalse是否使用esbuild打包依赖onMFSUProgressundefined获取 MFSU 编译进度的回调结合源码对其中几个选项做进一步说明cwd/tmpBase/getCacheDependency三者共同决定缓存的“位置”与“有效性”。构造函数中tmpBase默认取join(process.cwd(), .mfsu)getCacheDependency默认返回{}mfsu.ts#L80-L94。当依赖版本或构建环境变化时你可以通过getCacheDependency返回一个变化的值比如依赖锁文件的 hash来主动使缓存失效、触发重新构建unMatchLibs接受字符串或正则数组命中项将不走 MFSU 处理保持普通依赖的打包方式用于处理与联邦化不兼容的库例如依赖动态require、需要物理隔离的场景。仓库中也有对应的 mfsu-un-match-libs 示例可供参考runtimePublicPath与 umi 的runtimePublicPath语义一致设置为true后MFSU 会改用 promise-based 动态 remote见 mfsu.ts#L212-L241运行时通过window.publicPath拼接mf-va_remoteEntry.js的加载地址并注入script标签。适合部署在 CDN、子路径或 publicPath 运行时才能确定的场景若依赖需要被其他站点引用则需要显式指定publicPath以保证远端入口可被正确访问onMFSUProgress回调会收到形如{ done, ...progress }的进度对象。在 worker 构建路径下构建进度消息通过worker.postMessage回传并汇聚到onProgressdepBuilder.ts#L80-L118可用于在开发界面渲染依赖构建进度条buildDepWithESBuild为true时依赖侧使用 esbuild 打包对应DepBuilder.buildWithESBuild启动更快为false时退化为用implementorwebpack构建依赖DepBuilder.buildWithWebpack。常见问题如何保证我的 MFSU 配置只在开发环境生效使用环境标识隔离避免所有 MFSU 配置侵入生产构建。核心思路是仅在development环境下创建实例并在 loader 的 plugins 中做条件展开const isDev process.env.NODE_ENV development const mfsu isDev ? new MFSU({ implementor: webpack, buildDepWithESBuild: true, }) : undefined // e.g. { test: /\.[jt]sx?$/, exclude: /node_modules/, use: { loader: babel-loader, options: { plugins: [ ...(isDev ? [] : mfsu.getBabelPlugins()) ] } } }注意这里的要点mfsu.getBabelPlugins()只有在isDev时才展开生产构建的 babel 配置完全保持原样。同理devServer 中间件、setWebpackConfig注入等步骤也应包裹在isDev分支内确保 MFSU 只存在于开发链路。示例项目 examples/mfsu-independent 的build脚本webpack生产模式与dev脚本webpack serve开发模式正是按这一约定划分的。如何在 webpack 4 与 webpack 5 之间选择接入方式两者唯一的差异在第 2 步中间件挂载 APIwebpack 5 使用devServer.setupMiddlewares注意要unshift到最前确保先于其他中间件拦截 MFSU 请求webpack 4 使用devServer.onBeforeSetupMiddleware手动app.use。其余三步配置与源码层面的行为完全一致依赖构建同样交由implementor指定的 webpack 实例驱动。冷启动时如何观察 MFSU 是否生效观察两点即可一是项目根目录是否生成.mfsu目录二是终端日志——首次构建会输出[MFSU] buildDeps since ...与 esbuild 编译耗时缓存命中时会输出[MFSU] skip buildDeps。依赖变更或getCacheDependency返回值变化时则会触发依赖重建并写入新缓存。小结独立使用 MFSU 的本质是把 umi 中“依赖预构建 Module Federation 远端模块 开发中间件”这一整套机制通过umijs/mfsu暴露的MFSU类、esbuildLoader、getMiddlewares、getBabelPlugins/getEsbuildLoaderHandler、setWebpackConfig五个 API 移植到任何 webpack 工程。四步配置——初始化实例、挂中间件、配转换器、注入配置——彼此职责清晰且均为增量行为配合.mfsu缓存目录与buildDepWithESBuild选项即可在非 umi 项目中复现依赖快速打包与二次启动提速的开发体验。若需更完整的落地参考可直接对照仓库内的 examples/mfsu-independent 示例项目阅读其两份 webpack 配置。【免费下载链接】umiA framework in react community ✨项目地址: https://gitcode.com/GitHub_Trending/um/umi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表