Skip to content

Commit

Permalink
chore: add test & docs
Browse files Browse the repository at this point in the history
  • Loading branch information
2heal1 committed Oct 16, 2024
1 parent c09a5c3 commit d9242ab
Show file tree
Hide file tree
Showing 47 changed files with 1,288 additions and 125 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Do not edit this file. It is a auto generated.
// It will render sub's layout.
// The layout pathname is: entry-one/nested-routes/pathname
// The layout pathname is: /entry-one/nested-routes/pathname
// The layout route id is: entry-one_nested-routes/pathname/layout
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Do not edit this file. It is a auto generated.
// It will render sub's layout.
// The layout pathname is: entry-two/federation
// The layout pathname is: /entry-two/federation
// The layout route id is: entry-two_federation/layout
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 15 additions & 1 deletion apps/website-new/docs/zh/practice/frameworks/modern/_meta.json
Original file line number Diff line number Diff line change
@@ -1 +1,15 @@
["index","dynamic-remote"]
[
"index",
{
"type": "dir",
"name": "ssr",
"label": "服务端渲染(SSR)",
"collapsed": true
},
{
"type": "dir",
"name": "csr",
"label": "客户端渲染(CSR)",
"collapsed": true
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[
"dynamic-remote"
]
37 changes: 0 additions & 37 deletions apps/website-new/docs/zh/practice/frameworks/modern/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -251,40 +251,3 @@ const Index = () => (

export default Index;
```

## CSS 闪烁问题

启动项目访问 `http://localhost:3007/`,发现SSR 正常工作,页面可以正常渲染,但是会有样式闪烁的问题。

这是因为生产者的样式文件无法注入到对应的 html 中。

此问题可以通过使用 `@module-federation/modern-js` 提供的 [createremotessrcomponent](../../../guide/framework/modernjs#createremotessrcomponent) 解决。

修改消费者引用生产者处的代码(`src/routes/page.tsx`):

```tsx title='page.tsx'
import { createRemoteSSRComponent } from '@modern-js/runtime/mf'
import './index.css';

const RemoteSSRComponent = createRemoteSSRComponent({
loader: () => import('remote/Image'),
loading: 'loading...',
export: 'default',
fallback: ({ error }) => {
if (error instanceof Error && error.message.includes('not exist')) {
return <div>fallback - not existed id</div>;
}
return <div>fallback</div>;
},
});

const Index = () => (
<div className="container-box">
<RemoteSSRComponent />
</div>
);

export default Index;
```

修改后重新访问页面,可以观测返回的 html 中会自动注入生产者的样式文件,从而解决 CSS 闪烁问题。
11 changes: 11 additions & 0 deletions apps/website-new/docs/zh/practice/frameworks/modern/ssr/_meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
"dynamic-remote",
"route-component",
{
"type": "dir",
"name": "data-manager",
"label": "数据管理",
"collapsed": true
},
"faq"
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
"index",
"data-fetch",
"data-write"
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
title: "数据获取"
---
数据获取
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
title: "数据获取"
---
数据获取
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
title: "概览"
---

# 介绍

此章节会介绍常见的数据请求方式及其弊端,并简述 Modern.js 是如何解决存在的问题。同时展示 Module Federation 如何使用此功能。

## 存在的问题

在过去,SSR 模式下,生产者组件无法直接在 Server 端获取数据,只能通过消费者透传或者使用 `Await/Suspense` 异步加载数据。

该解决方法会加载对应的页面 js 资源,然后发起网络请求,该过程是一个串行过程,这意味着如果有多层组件,那么首屏性能会较差。

![serial-request](@public/practice/modernjs/serial-request.jpeg)

## Modern.js 解决方案

在 Modern.js 中,框架会对路由进行分片,并获取对应路由的静态资源和数据,随后会对静态资源和数据进行并行加载。

![parallel-request](@public/practice/modernjs/parallel-request.jpeg)

## 在 Module Federation 中使用

根据调用场景有不同的使用方式。

### 消费者

如果是仅在消费页面使用 Data Loader,那么按照 Modern.js 官网中的[数据获取](https://modernjs.dev/guides/basic-features/data/data-fetch.html)[数据写入](https://modernjs.dev/guides/basic-features/data/data-write.html)使用即可。

### 生产者

因为 Data Loader 需要和路由绑定,所以常规的生产者是无法使用此功能,需要该生产者为[路由组件](../route-component)
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
---
title: "动态加载生产者"
---

# 动态加载生产者

Modern.js 提供了 [Data Loader](https://modernjs.dev/guides/basic-features/data/data-fetch.html#data-loader%E6%8E%A8%E8%8D%90) 来帮助进行数据管理,`Data Loader` 仅在服务端执行,不会在客户端重复执行。

本章节将介绍如何利用 `Data Loader` 获取生产者信息并动态加载。

## 创建生产者

创建一个新的生产者,用于动态加载。

### 1. 创建配置文件

在项目根目录创建 `module-federation.config.ts` 文件,并写入下列内容:

```ts title='module-federation.config.ts'
import { createModuleFederationConfig } from '@module-federation/modern-js';

export default createModuleFederationConfig({
name: 'dynamic_provider',
filename: 'remoteEntry.js',
exposes: {
'./Image': './src/components/Image.tsx',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
},
});
```

### 2. 应用插件

`modern.config.ts` 应用 `@module-federation/modern-js`

```ts title='modern.config.ts'
import { appTools, defineConfig } from '@modern-js/app-tools';
import { moduleFederationPlugin } from '@module-federation/modern-js';

// https://modernjs.dev/en/configure/app/usage
export default defineConfig({
runtime: {
router: true,
},
server: {
ssr: {
mode: 'stream',
},
port: 3008,
},
plugins: [appTools(), moduleFederationPlugin()],
});
```

### 3. 创建导出组件

创建文件 `src/components/Image.tsx` ,内容如下:

```tsx title='Image.tsx'
import React from 'react';
import styles from './Image.module.css';

export default (): JSX.Element => (
<div
id="remote-components"
style={{
backgroundColor: '#c0e91e',
color: 'lightgrey',
padding: '1rem',
}}
>
<h2>
<strong>dynamic remote</strong>&nbsp;image
</h2>
<button
id="dynamic-remote-components-button"
style={{ marginBottom: '1rem' }}
onClick={() => alert('[remote-components] Client side Javascript works!')}
>
Click me to test i'm interactive!
</button>
<img
id="dynamic-remote-components-image"
src="https://module-federation.io/module-federation-logo.svg"
style={{ width: '100px' }}
alt="serge"
/>
<button className={styles['button']}>Button from dynamic remote</button>
</div>
);
```
并创建对应的样式文件,内容如下:
```css title='Image.module.css'
.button {
background: red;
}
```

## 创建 loader

在对应的路由文件创建同名的 `.data` 文件,以根目录 `src/routes/page.tsx` 为例,创建 `src/routes/page.data.ts`

```ts title='page.data.ts'
import { LoaderFunctionArgs } from '@modern-js/runtime/router';

export type DataLoaderRes = {
providerList: Array<{
name: string,
entry: string,
id: string;
}>
}

const fetchProviderList = async () => {
const res = await new Promise(resolve => {
setTimeout(() => {
resolve([
{
name: 'dynamic_provider',
entry: 'http://localhost:3008/mf-manifest.json',
id: 'dynamic_provider/Image'
}
])
}, 1000);
});

return res as DataLoaderRes['providerList']
}

export const loader = async ({ request }: LoaderFunctionArgs): Promise<DataLoaderRes> => {
console.log('request params', request);
const providerList = await fetchProviderList();
return {
providerList
}
};
```

## 加载动态生产者

消费 loader 数据,并动态加载对应的生产者:

```tsx
import { createRemoteSSRComponent, loadRemote, registerRemotes } from '@modern-js/runtime/mf';
// 使用 import type ,仅获取类型
import type { DataLoaderRes } from './page.data';
import { useLoaderData } from '@modern-js/runtime/router';

import './index.css';

const RemoteSSRComponent = createRemoteSSRComponent({
loader: () => import('remote/Image'),
loading: 'loading...',
export: 'default',
fallback: ({ error }) => {
if (error instanceof Error && error.message.includes('not exist')) {
return <div>fallback - not existed id</div>;
}
return <div>fallback</div>;
},
});

const Index = () => {
// 获取 data loader 数据
const dataLoader = useLoaderData() as DataLoaderRes;
// 注册生产者信息
registerRemotes(dataLoader.providerList);

const DynamicRemoteSSRComponents = dataLoader.providerList.map(item => {
const { id } = item;
const Com = createRemoteSSRComponent({
loader: () => loadRemote(id),
loading: 'loading...',
fallback: ({ error }) => {
if (error instanceof Error && error.message.includes('not exist')) {
return <div>fallback - not existed id</div>;
}
return <div>fallback</div>;
},
});
return <Com />
})
return (
<div className="container-box">
<RemoteSSRComponent />
{DynamicRemoteSSRComponents}
</div>
);
}

export default Index;
```
42 changes: 42 additions & 0 deletions apps/website-new/docs/zh/practice/frameworks/modern/ssr/faq.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
title: "FAQ"
---

# FAQ

## CSS 闪烁问题

启动项目访问 `http://localhost:3007/`,发现SSR 正常工作,页面可以正常渲染,但是会有样式闪烁的问题。

这是因为生产者的样式文件无法注入到对应的 html 中。

此问题可以通过使用 `@module-federation/modern-js` 提供的 [createremotessrcomponent](../../../guide/framework/modernjs#createremotessrcomponent) 解决。

修改消费者引用生产者处的代码(`src/routes/page.tsx`):

```tsx title='page.tsx'
import { createRemoteSSRComponent } from '@modern-js/runtime/mf'
import './index.css';

const RemoteSSRComponent = createRemoteSSRComponent({
loader: () => import('remote/Image'),
loading: 'loading...',
export: 'default',
fallback: ({ error }) => {
if (error instanceof Error && error.message.includes('not exist')) {
return <div>fallback - not existed id</div>;
}
return <div>fallback</div>;
},
});

const Index = () => (
<div className="container-box">
<RemoteSSRComponent />
</div>
);

export default Index;
```

修改后重新访问页面,可以观测返回的 html 中会自动注入生产者的样式文件,从而解决 CSS 闪烁问题。
Loading

0 comments on commit d9242ab

Please sign in to comment.