From b244a9601d7153f879aa85f296b8b75a65f68a46 Mon Sep 17 00:00:00 2001 From: SoonIter Date: Thu, 12 Dec 2024 15:49:37 +0800 Subject: [PATCH] docs: mf dts --- .../en/guide/advanced/module-federation.mdx | 6 +- website/docs/zh/guide/advanced/dts.mdx | 64 ++++ .../zh/guide/advanced/module-federation.mdx | 329 ++++++++++++++++++ website/docs/zh/guide/start/quick-start.mdx | 4 +- 4 files changed, 398 insertions(+), 5 deletions(-) diff --git a/website/docs/en/guide/advanced/module-federation.mdx b/website/docs/en/guide/advanced/module-federation.mdx index 2be11b3d6..c6b9e6ca3 100644 --- a/website/docs/en/guide/advanced/module-federation.mdx +++ b/website/docs/en/guide/advanced/module-federation.mdx @@ -28,7 +28,7 @@ import { PackageManagerTabs } from '@theme'; -Then add the plugin to the `rslib.config.ts` file: +Then register the plugin to the `rslib.config.ts` file: ```ts title='rslib.config.ts' {8-43} import { pluginModuleFederation } from '@module-federation/rsbuild-plugin'; @@ -82,7 +82,7 @@ export default defineConfig({ }); ``` -In this way, we have completed the integration of Rslib Module as a producer. After the construction is completed, we can see that the mf directory has been added to the product, and consumers can directly consume this package +In this way, we have completed the integration of Rslib Module as a producer. After the construction is completed, we can see that the mf directory has been added to the product, and consumers can directly consume this package. In the above example we added a new `format: 'mf'` , which will help you add an additional Module Federation product, while also configuring the format of `cjs` and `esm` , which does not conflict. @@ -310,7 +310,7 @@ This ensures that modules can be loaded as expected in multiple formats. ## FAQs If the Rslib producer is built with build, this means that the `process.env.NODE_ENV` of the producer is `production` . If the consumer is started in dev mode at this time, -Due to the shared loading policy of Module Federation being `version-first` by default, there may be problems loading into different modes of react and react-dom (e.g. react in development mode, react-dom in production mode). +due to the shared loading strategy of Module Federation being `version-first` by default, there may be problems loading into different modes of react and react-dom (e.g. react in development mode, react-dom in production mode). You can set up [shareStrategy](https://module-federation.io/configure/sharestrategy) at the consumer to solve this problem, but make sure you fully understand this configuration ```ts diff --git a/website/docs/zh/guide/advanced/dts.mdx b/website/docs/zh/guide/advanced/dts.mdx index 889b94684..927d353b1 100644 --- a/website/docs/zh/guide/advanced/dts.mdx +++ b/website/docs/zh/guide/advanced/dts.mdx @@ -1 +1,65 @@ # 类型生成 + +本章介绍什么是 TypeScript 声明文件(DTS)以及如何在 Rslib 中生成 DTS 文件。 + +## 什么是 DTS + +TypeScript 声明文件 (DTS) 提供 JavaScript 代码的类型信息。 DTS 文件通常具有 `.d.ts` 扩展名。它们允许 TypeScript 编译器理解 JavaScript 代码的类型结构,从而实现以下功能: + +1. **类型检查**: 为 JavaScript 代码提供类型信息,帮助开发人员在编译时捕获潜在的类型错误。 +2. **代码补全**: 增强代码编辑器功能,例如自动完成和代码导航。 +3. **文档生成**: 生成 JavaScript 代码文档,提供更好的开发体验。 +4. **IDE 支持**: 改善 Visual Studio Code、WebStorm 等 IDE 中的开发人员体验. +5. **库消费**: 让其他开发人员更容易使用和理解您的库。 + +## 什么是 Bundle DTS 和 Bundleless DTS + +### Bundle DTS + +Bundle DTS 涉及将多个 TypeScript 声明文件 bundle 到一个声明文件中。 + +- **优势:** + + - **简化管理**: 简化类型文件的管理和引用。 + - **容易分发**: 减少用户使用库时需要处理的文件数量。 + +- **劣势:** + - **生成复杂**: 在大型项目中,生成和维护单个 bundle 文件可能会变得复杂. + - **调试困难**: 调试类型问题可能不像各个文件单独输出那样直观。 + +### Bundleless DTS + +Bundleless DTS 涉及为库中的每个模块生成单独的声明文件,就像“tsc”一样。 + +- **优势:** + + - **模块化**: 每个模块都有自己的类型定义,使维护和调试更容易。 + - **灵活**: 适合大型项目,避免单个文件的复杂性。 + +- **劣势:** + - **多文件**: 用户在使用该库时可能需要处理多个声明文件。 + - **管理复杂**: 可能需要额外的配置才能正确引用所有文件。 + +## 怎么在 Rslib 中生成 DTS + +Rslib 默认使用 [TypeScript Compiler API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API) 生成 Bundleless DTS, [API Extractor](https://api-extractor.com/) + +如果您想生成 Bundleless 的 DTS,你可以: + +- 设置 `dts: true` 或者 `dts: { bundle: false }` 在 Rslib 配置文件。 + +如果你想生成 Bundle DTS,你可以: + +1. 安装 `@microsoft/api-extractor` 作为 `dev`, 这是用于 bundle DTS 文件的底层工具。 + +import { PackageManagerTabs } from '@theme'; + + + +2. 设置 `dts: { bundle: true }` 在 Rslib 配置文件中。 + +::: tip + +你可以参考 [lib.dts](/config/lib/dts) 获取更多有关 DTS 配置的详细信息。 + +::: \ No newline at end of file diff --git a/website/docs/zh/guide/advanced/module-federation.mdx b/website/docs/zh/guide/advanced/module-federation.mdx index da97a8b7b..d01cfb0bd 100644 --- a/website/docs/zh/guide/advanced/module-federation.mdx +++ b/website/docs/zh/guide/advanced/module-federation.mdx @@ -1 +1,330 @@ +import MF from '../start/components/MF.mdx'; +import { Tab, Tabs } from 'rspress/theme'; + # 模块联邦 + +本章介绍如何在 Rslib 中构建 [模块联邦](/guide/basic/output-format#mf) 产物。 + +## 使用场景 + +模块联邦有一些典型的使用场景,包括: + +- 允许独立应用程序(微前端架构中称为“微前端”)共享模块,而无需重新编译整个应用。 +- 不同的团队处理同一应用程序的不同部分,而无需重新编译整个应用程序。 +- 运行时中在应用间动态加载和共享代码。 + +模块联盟可以帮助您: + +- 减少代码重复 +- 提高代码可维护性 +- 减小应用程序的整体大小 +- 提高应用性能 + +## 快速开始 + +首先安装 [Module Federation Rsbuild Plugin](https://www.npmjs.com/package/@module-federation/rsbuild-plugin). + +import { PackageManagerTabs } from '@theme'; + + + +然后在 `rslib.config.ts` 注册插件: + +```ts title='rslib.config.ts' {8-43} +import { pluginModuleFederation } from '@module-federation/rsbuild-plugin'; +import { pluginReact } from '@rsbuild/plugin-react'; +import { defineConfig } from '@rslib/core'; + +export default defineConfig({ + lib: [ + // ... 其他 format + { + format: 'mf', + output: { + distPath: { + root: './dist/mf', + }, + // production 时, 在这里使用线上 assetPrefix + assetPrefix: 'http://localhost:3001/mf', + }, + // Storybook 在 dev 下使用 + dev: { + assetPrefix: 'http://localhost:3001/mf', + }, + // Storybook 在 dev 下使用 + server: { + port: 3001, + }, + plugins: [ + pluginModuleFederation({ + name: 'rslib_provider', + exposes: { + // 这里添加 expose + }, + // 此处无法添加 "remote",因为您可能会在一次构建中构建 "esm" 或 "cjs" 产物。 + // 如果您希望 Rslib 包使用远程模块,请参考下面。 + shared: { + react: { + singleton: true, + }, + 'react-dom': { + singleton: true, + }, + }, + }), + ], + }, + ], + output: { + target: 'web', + }, + plugins: [pluginReact()], +}); +``` + +这样,我们就完成了对 Rslib Module 生产者的集成。构建完成后,我们可以看到产物中已经添加了 mf 目录,消费者可以直接消费这个包。 + +在上面的例子中,我们添加了一个新的 `format: 'mf'` ,它将添加一个额外的 Module Federation 产物,同时还配置了 `cjs` 和 `esm` 的格式,它们是不冲突的。 + +但是,如果您希望此 Rslib Module 同时消费其他生产者,请不要使用构建配置 `remote` 参数,因为在其他格式下,这可能会导致错误,请参考下面使用 Module Federation 运行时的示例 + +## 开发 MF 远程模块 + +### 使用宿主应用 + +Rslib 支持宿主应用和 Rslib 模块联邦项目同时开发。 + +#### 1. 启动库中的 MF `dev` + +添加 `dev` 命令在 `package.json` 文件: + +```json title="package.json" +{ + "scripts": { + "dev": "rslib mf dev" + } +} +``` + +然后运行 `dev` 命令即可启动 Module Federation 开发模式,可被 Host App 消费, +同时具有热模块更换(HMR)功能。 + + + +#### 2. 启动 Host App + +设置 Host 消费 Rslib 的模块联邦库。查看[@module-federation/rsbuild-plugin +](https://www.npmjs.com/package/@module-federation/rsbuild-plugin) 获取更多信息。 + +```ts title="rsbuild.config.ts" {8-24} +import { pluginModuleFederation } from '@module-federation/rsbuild-plugin'; +import { defineConfig } from '@rsbuild/core'; +import { pluginReact } from '@rsbuild/plugin-react'; + +export default defineConfig({ + plugins: [ + pluginReact(), + pluginModuleFederation({ + name: 'rsbuild_host', + remotes: { + rslib: 'rslib@http://localhost:3001/mf/mf-manifest.json', + }, + shared: { + react: { + singleton: true, + }, + 'react-dom': { + singleton: true, + }, + }, + // 开启这个当 Rslib 产物为 'production' 模式, 但是宿主应用是 'development' 模式。 + // 参考链接: https://lib.rsbuild.dev/guide/advanced/module-federation#faqs + shareStrategy: 'loaded-first', + }), + ], +}); +``` + +然后通过 `rsbuild dev` 启动 Host app。 + +### 使用 Storybook + +Rslib 支持使用 Storybook 开发 Module Federation Rslib 项目。 + +#### 1. 启动库的 MF `dev` + +添加 `dev` 命令在 `package.json` 文件: + +```json title="package.json" +{ + "scripts": { + "dev": "rslib mf dev" + } +} +``` + +然后运行 `dev` 命令即可启动 Module Federation 开发模式,可被 Storybook 消费, +同时具有热模块更换(HMR)功能。 + + + +#### 2. 创建 Storybook 配置 + +首先,在 Rslib 项目中配置 Storybook。您可以参考 [Storybook 章节](/guide/advanced/storybook)来了解如何执行此操作。在本章中,我们将使用 React 框架作为示例。 + +1. 安装以下 Storybook addon,让 Storybook 与 Rslib Module Federation 一起使用: + + - [storybook-addon-rslib](https://www.npmjs.com/package/storybook-addon-rslib): Storybook addon 会让 Storybook 加载 Rslib 配置. + - [@module-federation/storybook-addon](https://www.npmjs.com/package/@module-federation/rsbuild-plugin): Storybook 插件,为 Storybook 设置模块联邦配置。 + + + +2. 然后创建 Storybook 配置文件 `.storybook/main.ts`,指定 stories 和 addons,并设置 framework 和相应的 framework 集成。 + +```ts title=".storybook/main.ts" {18-38} + import { dirname, join } from 'node:path'; + import type { StorybookConfig } from 'storybook-react-rsbuild'; + + function getAbsolutePath(value: string): any { + return dirname(require.resolve(join(value, 'package.json'))); + } + + const config: StorybookConfig = { + stories: [ + '../stories/**/*.mdx', + '../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)', + ], + framework: { + name: getAbsolutePath('storybook-react-rsbuild'), + options: {}, + }, + addons: [ + { + name: getAbsolutePath('storybook-addon-rslib'), + options: { + rslib: { + include: ['**/stories/**'], + }, + }, + }, + { + name: '@module-federation/storybook-addon/preset', + options: { + // 在添加 rslib module manifest 给 storybook dev + // 我们在上面已经设置了 dev.assetPrefix 和 server.port 到 3001 在 rslib.config.ts + remotes: { + 'rslib-module': + //还可以在这里添加 storybook 的 shared + // shared: {} + 'rslib-module@http://localhost:3001/mf/mf-manifest.json', + }, + }, + }, + ], + }; + + export default config; + ``` + +#### 3. 用远程模块 编写 stories + +从远程模块引入组件 + +```ts title="stories/index.stories.tsx" {2-3} +import React from 'react'; +// 在这里加载远程模块,Storybook 相当于宿主应用. +import { Counter } from 'rslib-module'; + +const Component = () => ; + +export default { + title: 'App Component', + component: Component, +}; + +export const Primary = {}; +``` + +#### 4. 在 `tsconfig.json` 中添加 Module Federation 类型和 stories 文件。 + +```json title="tsconfig.json" +{ + "compilerOptions": { + // ... + "paths": { + "*": ["./@mf-types/*"] + } + }, + "include": ["src/**/*", ".storybook/**/*", "stories/**/*"] +} +``` + +#### 5. 启动 Storybook app + +大功告成,启动 Storybook `npx storybook dev`。 + +## 使用其他模块联合模块 + +由于 Rslib 中有多种格式,如果在构建时配置 `remote` 参数来消耗其他模块,则可能无法在所有格式下正常工作。建议通过以下方式访问 [Module Federation Runtime](https://module-federation.io/guide/basic/runtime.html) + +首先安装运行时依赖 + + + +然后在运行时使用其他模块联邦模块,例如 + +```ts +import { init, loadRemote } from '@module-federation/enhanced/runtime'; +import { Suspense, createElement, lazy } from 'react'; + +init({ + name: 'rslib_provider', + remotes: [ + { + name: 'mf_remote', + entry: 'http://localhost:3002/mf-manifest.json', + }, + ], +}); + +export const Counter: React.FC = () => { + return ( +
+ loading
}> + {createElement( + lazy( + () => + loadRemote('mf_remote') as Promise<{ + default: React.FC; + }>, + ), + )} + + + ); +}; +``` + +这确保了模块可以按预期以多种格式加载。 + +## FAQs + +如果 Rslib 生产者是用 build 构建的, 这意味着生产者中的 `process.env.NODE_ENV` 是 `production` 。如果这时消费者是使用的开发模式启动,由于模块联邦默认使用共享的加载策略,可能会有 react 和 react-dom 加载模式不一致的问题 (比如 react 在 development mode, react-dom 在 production mode)。 +你可以在消费者设置 [shareStrategy](https://module-federation.io/configure/sharestrategy) 来解决这个问题,但是确保你已经完全理解了这个配置。 + +```ts +pluginModuleFederation({ + // ... + shareStrategy: 'loaded-first', +}), +``` + +## 示例 + +[Rslib Module Federation 示例](https://github.com/web-infra-dev/rslib/tree/main/examples/module-federation) + +- `mf-host`: Rsbuild App 消费者 +- `mf-react-component`: Rslib Module, 同时是消费者和生产者, 作为生产者向 `mf-host` 提供模块, 并消费 `mf-remote` +- `mf-remote`: Rsbuild App 生产者 + +[Rslib Module Federation Storybook 示例](https://github.com/web-infra-dev/rslib/tree/main/examples/module-federation/mf-react-component) diff --git a/website/docs/zh/guide/start/quick-start.mdx b/website/docs/zh/guide/start/quick-start.mdx index 876a48b15..c31eb62e8 100644 --- a/website/docs/zh/guide/start/quick-start.mdx +++ b/website/docs/zh/guide/start/quick-start.mdx @@ -1,6 +1,6 @@ # 快速上手 -## Setup Environment +## 初始化环境 开始之前,你需要先安装 [Node.js](https://nodejs.org/) >= 16 版本,建议使用 Node.js LTS 版本。 @@ -10,7 +10,7 @@ node -v ``` -如果当前环境没有安装Node.js,或者安装的版本太低,可以使用 [nvm](https://github.com/nvm-sh/nvm) 或 [fnm](https://github.com/nvm-sh/nvm) 或 [fnm](https:///github.com/Schniz/fnm)进行安装。 +如果当前环境没有安装 Node.js,或者安装的版本太低,可以使用 [nvm](https://github.com/nvm-sh/nvm) 或 [fnm](https://github.com/nvm-sh/nvm) 或 [fnm](https:///github.com/Schniz/fnm)进行安装。 以下是如何通过 nvm 安装的示例: