Skip to content

Commit

Permalink
feat: support splat route config file (#4673)
Browse files Browse the repository at this point in the history
  • Loading branch information
yimingjfe authored Sep 20, 2023
1 parent 93e4a01 commit 24482a5
Show file tree
Hide file tree
Showing 13 changed files with 98 additions and 62 deletions.
6 changes: 6 additions & 0 deletions .changeset/quick-seas-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@modern-js/app-tools': patch
---

feat: support splat route config file
feat: 支持通配路由配置文件
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 3
sidebar_position: 4
---

# Path Alias
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 2
sidebar_position: 1
---

# Styling
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
---
sidebar_position: 4
title: Data Fetching
sidebar_position: 3
---

# Data Fetching

Modern.js provides out-of-the-box data fetching capabilities, allowing developers to develop in an isomorphic way in both client-side and server-side code.
Modern.js provides out-of-the-box data fetching capabilities,developers can fetch data in the project through these APIs.

It should be noted that these APIs do not help applications initiate requests, but rather help developers better manage data and improve project performance.

Expand All @@ -13,7 +14,7 @@ It should be noted that these APIs do not help applications initiate requests, b
Modern.js recommends using [conventional routing](/guides/basic-features/routes) for routing management. Through Modern.js's [conventional (nested) routing](/guides/basic-features/routes#conventional-routing), each routing component (`layout.ts` or `page.ts`) can have a same-named `loader` file. The `loader` file needs to export a function that will be executed before the component is rendered to provide data for the routing component.

:::info
Modern.js v1 supports fetching data via [useLoader](/guides/basic-features/data-fetch.html#useloader-(old-version)), which is no longer the recommended usage. We do not recommend mixing the two except during the migration process.
Modern.js v1 supports fetching data via [useLoader](</guides/basic-features/data-fetch.html#useloader-(old-version)>), which is no longer the recommended usage. We do not recommend mixing the two except during the migration process.

:::

Expand Down Expand Up @@ -216,25 +217,24 @@ If you want to get the data returned by the `loader` in `entry1/routes/layout.ts

:::info
This feature is currently experimental and the API may change in the future.
Currently only supports CSR, please look forward to Streaming SSR.
:::

Create `user/layout.loader.ts` and add the following code:

```ts title="routes/user/layout.loader.ts"
import { defer } from "@modern-js/runtime/router"
import { defer } from '@modern-js/runtime/router';

const loader = () =>
defer({
userInfo: new Promise((resolve) => {
defer({
userInfo: new Promise(resolve => {
setTimeout(() => {
resolve({
age: 1,
name: 'user layout'
})
}, 1000)
})
})
name: 'user layout',
});
}, 1000);
}),
});

export default loader;
```
Expand Down
42 changes: 27 additions & 15 deletions packages/document/main-doc/docs/en/guides/basic-features/routes.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 1
sidebar_position: 2
---

# Routing
Expand Down Expand Up @@ -140,6 +140,7 @@ To simplify the introduction of the relationship between `<Layout>` and `<Outlet
</UserLayout>
</Layout>
```

In summary, if there is a `layout.tsx` file under the sub-route's file directory, the `<Outlet>` in the parent `layout.tsx` will represent the `layout.tsx` in the sub-route file directory. Otherwise, it will represent the `page.tsx` in the sub-route file directory.

#### Page
Expand All @@ -148,25 +149,24 @@ All routes should end with the `<Page>` component. If the developer introduces t

#### Config

Each `Layout` or `Page` file can define its own `config` file, such as `page.config.ts`. In this file, we have an conventinal on a named export called `handle`, which you can define any properties:
Each `Layout`,`$` or `Page` file can define its own `config` file, such as `page.config.ts`. In this file, we have an conventinal on a named export called `handle`, which you can define any properties:

```ts title="routes/blog/page.config.ts"
export const handle = {
breadcrumbName: 'profile'
}
breadcrumbName: 'profile',
};
```

These properties as defined are available via the [`useMatches`](https://reactrouter.com/en/main/hooks/use-matches) hook:

```ts title="routes/layout.ts"
export default () => {
const matches = useMatches;
const breadcrumbs = matches.map(matchedRoute => matchedRoute?.handle?.breadcrumbName);
return (
<Breadcrumb names={breadcrumbs}>
</Breadcrumb>
)
}
const breadcrumbs = matches.map(
matchedRoute => matchedRoute?.handle?.breadcrumbName,
);
return <Breadcrumb names={breadcrumbs}></Breadcrumb>;
};
```

### Dynamic Routing
Expand Down Expand Up @@ -227,6 +227,7 @@ For example, the following directory structure:
```

When accessing any path that does not match(For example `/blog/a`), the `routes/$.tsx` component will be rendered, because there is `layout.tsx` here, the rendered UI is as follows.

```tsx
<RootLayout>
<BlogLayout>
Expand Down Expand Up @@ -364,6 +365,17 @@ export default () => {
};
```

If you want to redirect in a component,you can navigate by `useNavigate` hook, for example:

```ts title="routes/user/page.ts"
import { useNavigate } from '@modern-js/runtime/router';

export default () => {
const navigate = useNavigate();
navigate('/login');
};
```

### ErrorBoundary

In each directory under `routes/`, developers can also define an `error.tsx` file that exports an `<ErrorBoundary>` component by default.
Expand Down Expand Up @@ -391,7 +403,6 @@ export const ErrorBoundary = () => {

In each root `Layout` component (`routes/layout.ts`), you can dynamically define runtime configuration:


```tsx title="src/routes/layout.tsx"
// Define runtime config
import type { AppConfig } from '@modern-js/runtime';
Expand Down Expand Up @@ -489,6 +500,7 @@ To further improve the user experience and reduce loading time, Modern.js suppor
```

:::info

- This feature is currently only supported in Webpack projects and not yet supported in Rspack projects.
- Preloading data currently only preloads the data returned by the [Data Loader](/guides/basic-features/data-fetch) in SSR projects.

Expand All @@ -508,13 +520,13 @@ The `prefetch` attribute has three optional values:
- By using `render`, static assets will only be loaded when the system is idle, and will not compete with the static assets of the initial screen for network assets.
- In the SSR scenario, data will also be pre-fetched.

import Motivation from '@site-docs-en/components/convention-routing-motivation'
import Motivation from '@site-docs-en/components/convention-routing-motivation';

<Motivation/>
<Motivation />

import Practice from '@site-docs-en/components/routes-practice'
import Practice from '@site-docs-en/components/routes-practice';

<Practice/>
<Practice />

## Self-controlled Routing

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 3
sidebar_position: 4
---

# 路径别名
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 2
sidebar_position: 1
---

# 样式开发
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
---
sidebar_position: 4
title: 数据获取
sidebar_position: 3
---

# 数据获取

Modern.js 中提供了开箱即用的数据获取能力,开发者可以通过这些 API,在 CSR 和 SSR 环境同构的进行开发
Modern.js 中提供了开箱即用的数据获取能力,开发者可以通过这些 API,在项目中获取数据

需要注意的是,这些 API 并不帮助应用发起请求,而是帮助开发者更好地管理数据,提升项目的性能。

Expand Down Expand Up @@ -216,26 +217,25 @@ export default function UserLayout() {

:::info
此功能目前是实验性质,后续 API 可能有调整。
目前仅支持 CSR,敬请期待 Streaming SSR。

:::

创建 `user/layout.loader.ts`,并添加以下代码:

```ts title="routes/user/layout.loader.ts"
import { defer } from "@modern-js/runtime/router"
import { defer } from '@modern-js/runtime/router';

const loader = () =>
defer({
userInfo: new Promise((resolve) => {
defer({
userInfo: new Promise(resolve => {
setTimeout(() => {
resolve({
age: 1,
name: 'user layout'
})
}, 1000)
})
})
name: 'user layout',
});
}, 1000);
}),
});

export default loader;
```
Expand Down Expand Up @@ -346,7 +346,6 @@ export default async (): Promise<ProfileData> => {

在 SSR 项目中,每个 `loader` 也是一个服务端接口,我们推荐使用 `loader` 替代 http method 为 `get` 的 BFF 函数,作为接口层,避免多一层转发和执行。


## useLoader(旧版)

**`useLoader`** 是 Modern.js 老版本中的 API。该 API 是一个 React Hook,专门提供给 SSR 应用使用,让开发者能同构的在组件中获取数据。
Expand Down
45 changes: 26 additions & 19 deletions packages/document/main-doc/docs/zh/guides/basic-features/routes.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 1
sidebar_position: 2
---

# 路由方案
Expand Down Expand Up @@ -151,29 +151,27 @@ export default () => {

#### Config

每个 `Layout``Page` 文件都可以定义一个自己的 `config` 文件,如 `page.config.ts`,该文件中我们约定了一个具名导出 `handle`
每个 `Layout`, `$``Page` 文件都可以定义一个自己的 `config` 文件,如 `page.config.ts`,该文件中我们约定了一个具名导出 `handle`
这个字段中你可以定义任意属性:

```ts title="routes/page.config.ts"
export const handle = {
breadcrumbName: 'profile'
}
breadcrumbName: 'profile',
};
```

定义的这些属性可以通过 [`useMatches`](https://reactrouter.com/en/main/hooks/use-matches) hook 获取:

```ts title="routes/layout.ts"
export default () => {
const matches = useMatches;
const breadcrumbs = matches.map(matchedRoute => matchedRoute?.handle?.breadcrumbName);
return (
<Breadcrumb names={breadcrumbs}>
</Breadcrumb>
)
}
const breadcrumbs = matches.map(
matchedRoute => matchedRoute?.handle?.breadcrumbName,
);
return <Breadcrumb names={breadcrumbs}></Breadcrumb>;
};
```


### 动态路由

通过 `[]` 命名的文件目录,生成的路由会作为动态路由。例如以下文件目录:
Expand Down Expand Up @@ -233,6 +231,7 @@ export default () => {
```

当访问任何匹配不到的路径时(如 `/blog/a`),都会渲染 `routes/$.tsx` 组件,因为这里有 `layout.tsx`,渲染的 UI 如下:

```tsx
<RootLayout>
<BlogLayout>
Expand All @@ -254,7 +253,6 @@ params['*']; // => 'aaa/bbb'

`$.tsx` 可以加入到 `routes` 目录下的任意目录中,一个常见的使用示例是添加 `routes/$.tsx` 文件去定制任意层级的 404 内容。


### 无路径布局

当目录名以 \_\_ 开头时,对应的目录名不会转换为实际的路由路径,例如以下文件目录:
Expand Down Expand Up @@ -368,6 +366,17 @@ export default () => {
};
```

在组件内做重定向,则可以通过 `useNavigate` hook,示例如下:

```ts title="routes/user/page.ts"
import { useNavigate } from '@modern-js/runtime/router';

export default () => {
const navigate = useNavigate();
navigate('/login');
};
```

### 错误处理

`routes/` 下每一层目录中,开发者同样可以定义一个 `error.tsx` 文件,默认导出一个 `<ErrorBoundary>` 组件。
Expand Down Expand Up @@ -493,6 +502,7 @@ export const init = (context: RuntimeContext) => {
```

:::info

- 该功能目前仅在 Webpack 项目中支持,Rspack 项目暂不支持。
- 对数据的预加载目前只会预加载 SSR 项目中 [Data Loader](/guides/basic-features/data-fetch) 中返回的数据。

Expand All @@ -512,14 +522,13 @@ export const init = (context: RuntimeContext) => {
- 使用 `render`,仅在空闲时对静态资源进行加载,不会与首屏静态资源抢占网络。
- 在 SSR 场景下,也会对数据进行预取。

import Motivation from '@site-docs/components/convention-routing-motivation'

<Motivation/>
import Motivation from '@site-docs/components/convention-routing-motivation';

import Practice from '@site-docs/components/routes-practice'
<Motivation />

<Practice/>
import Practice from '@site-docs/components/routes-practice';

<Practice />

## 自控式路由

Expand Down Expand Up @@ -569,5 +578,3 @@ export default defineConfig({
```

如上述配置, 如果关闭了 [`runtime.router`](/configure/app/runtime/router) 配置,并直接使用 `react-router-dom` 进行项目路由管理时,还需要根据 React Router 文档自行包裹 `Provider`。


1 change: 1 addition & 0 deletions packages/solutions/app-tools/src/analyze/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const NESTED_ROUTE = {
PAGE_DATA_FILE: 'page.data',
PAGE_CLIENT_LOADER: 'page.data.client',
SPLATE_FILE: '$',
SPLATE_CONFIG_FILE: '$.config',
SPLATE_LOADER_FILE: '$.loader',
SPLATE_DATA_FILE: '$.data',
SPLATE_CLIENT_DATA: '$.data.client',
Expand Down
Loading

0 comments on commit 24482a5

Please sign in to comment.