数据获取
远程模块通常希望能独立完成数据获取:消费者只负责加载和渲染,无需了解远程模块依赖哪些数据,也避免每个消费它的应用重复实现取数逻辑。
在 CSR 场景下,远程模块直接在组件内通过 useEffect 发起请求即可。但在 SSR 场景下 useEffect 不会执行,远程模块就无法在服务端先拿到数据再渲染。
主流的 SSR 框架(如 Next.js、Remix、Modern.js 等)通常会在路由层面提供数据预取能力:路由命中时先执行数据加载函数拿到数据,再注入对应的路由组件参与渲染。但这套机制依赖框架自身的路由系统,Module Federation 暴露的模块并不在宿主的路由树中,无法直接复用。
为了解决这一问题,Module Federation 提供了组件级别数据获取能力,以便开发者可以在 SSR 场景下获取数据并渲染组件。
如何使用
数据获取能力同时支持 SSR 和 CSR 两种场景,下面分为生产者和消费者两部分来介绍:
生产者
数据获取文件约定
数据获取通过文件约定实现:
- 每个 expose 模块都可以配套一个同名的
.data文件。 - 在
.data文件中导出的函数被称为 Data Loader,构建插件会识别这个约定。 - 当 expose 组件被加载时,运行时会先调用 Data Loader 拿到数据,把数据注入组件后再渲染。
文件结构示例:
约定文件中需要导出名为 fetchData 的函数,该函数将会在远程组件渲染前执行:
函数会在组件渲染前被调用,运行时会把这个数据对象注入到组件 的 props 中,默认字段名为 mfData:
Loader 函数默认传参
默认会往 loader 函数传递参数,其类型为 DataFetchParams,包含以下字段:
isDowngrade:表示当前执行上下文是否处于降级模式。例如,在服务端渲染失败,在浏览器端渲染时会重新往服务端发起请求,调用 loader 函数,此时该值为true。
在不同环境使用 Data Loader
loader 函数可能会在服务端或浏览器端执行。在服务端执行的 loader 函数,我们称为 Server Loader,在浏览器端执行的称为 Client Loader。
在 CSR 应用中,loader 函数会在浏览器端执行,即默认都是 Client Loader。
在 SSR 应用中,loader 函数只会在服务端执行,即默认都是 Server Loader。在 SSR 应用中,Module Federation 会直接在服务端调用对应的 loader 函数。在浏览器端切换路由时,Module Federation 会发送一个 http 请求到 SSR 服务,同样在服务端触发 loader 函数。
-
简化使用方式:保证 SSR 应用获取数据的方式是同构的,开发者无需根据环境区分 loader 函数执行的代码。
-
减少浏览器端 bundle 体积:将逻辑代码及其依赖,从浏览器端移动到了服务端。
-
提高可维护性:将逻辑代码移动到服务端,减少了数据逻辑对前端 UI 的直接影响。此外,也避免了浏览器端 bundle 中误引入服务端依赖,或服务端 bundle 中误引入浏览器端依赖的问题。
在 SSR 应用中使用 Client Loader
默认情况下,在 SSR 应用中,loader 函数只会在服务端执行。但有些场景下,开发者可能期望在浏览器端发送的请求不经过 SSR 服务,直接请求数据源,例如:
- 在浏览器端希望减少网络消耗,直接请求数据源。
- 应用在浏览器端有数据缓存,不希望请求 SSR 服务获取数据。
Module Federation 支持在 SSR 应用中额外添加 .data.client 文件,同样具名导出。此时,SSR 应用在服务端执行 Data Loader 报错降级,或浏览器端切换路由时,会像 CSR 应用一样在浏览器端执行该函数,而不是再向 SSR 服务发送数据请求。
要使用 Client Loader,必须有对应的 Server Loader,且 Server Loader 必须是 .data 文件约定。
生产者消费自身应用数据
生产者本身也可能作为独立页面被访问。这时它需要在两种场景下都能取到数据:
- 被消费者加载时由 Data Loader 注入
- 独立访问时由生产者自己的框架机制取数
例如,如果生产者使用 Modern.js,它既要支持被消费者加载获取数据,也要支持在生产者页面独立访问获取数据。这时可以通过以下方式实现:
- 在生产者页面创建
page.data.ts文件,导出名为loader的函数:
Modern.js 约定 page.data.ts 中导出 loader 函数来 获取数据。
- 在生产者
page页面消费此数据:
消费者
目前仅 Modern.js 支持在 SSR 环境下使用数据获取能力。
在消费者中,我们需要通过 createLazyComponent API 来加载远程组件,并获取数据。
传递自定义参数
除了运行时默认注入的数据结构,消费者还可以通过 createLazyComponent 的 dataFetchParams 字段向 loader 函数透传自定义参数,例如用户标识、语言、租户等宿主侧上下文。
该字段会与默认参数合并后传入 loader。
FAQ
应用级别数据获取?
应用级别的模块,我们更希望使用 RSC 来实现,使其功能更加的完善。目前该功能正在探索中,敬请期待。
是否支持嵌套生产者?
不支持。
生产者除了使用 Rslib 插件或者 Modern.js 插件外还有其他的插件吗?
暂时只有 Rslib 插件和 Modern.js 插件才可以创建 Data Loader。