视图
本项目视图分两部分:React前端页面
和 Midway后端模板渲染
。
React html模板
你可以修改默认的React html模板。
默认的React html模板,位置在 /web/index.html
,默认内容如下:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><!--app-title--></title>
<meta name="keywords" content="!--app-keywords--">
<meta name="description" content="!--app-description--">
<link rel="icon" href="/favicon.ico" />
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/entry-client.tsx"></script>
<script>
window.__INITIAL_DATA__ = "<!--app--state-->";
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
举个例子,你可以添加自定义的script
标签或代码块等其他内容:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><!--app-title--></title>
<meta name="keywords" content="!--app-keywords--">
<meta name="description" content="!--app-description--">
<link rel="icon" href="/favicon.ico" />
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/entry-client.tsx"></script>
<script>
window.__INITIAL_DATA__ = "<!--app--state-->";
</script>
<script src="http://echarts.com/echarts.js"></script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
WARNING
注意:<!--app-title-->
、!--app-keywords--
、!--app-description--
、<!--app-html-->
、<!--app--state-->
等插槽名称切记不要修改或删除。
React 布局
在 /web/layouts
目录下你可以修改默认布局或创建自定义的布局。
TIP
别忘了在布局文件中添加 {children}
组件用于显示页面的主体内容。
默认布局
/web/layouts/DefaultLayout/index.tsx
源码如下:
tsx
<script lang="ts" setup>
import { memo } from 'react';
import { Link } from 'react-router-dom';
import classnames from 'classnames';
import { useAsyncDataMetaContext } from '@/store/asyncDataContext';
import './css/index.less';
export interface DefaultLayoutProps {
children: React.ReactNode;
}
export default memo(({ children }: DefaultLayoutProps) => {
const meta = useAsyncDataMetaContext();
return (
<>
<nav>
<Link
to={'/'}
className={classnames({ active: meta.navActive === 'home' })}
>
Home
</Link>
|
<Link
to={'/about'}
className={classnames({ active: meta.navActive === 'about' })}
>
About
</Link>
|
<Link
to={{ pathname: '/localapi', search: 'uid=10' }}
className={classnames({ active: meta.navActive === 'localapi' })}
>
LocalApi
</Link>
</nav>
{children}
</>
);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
自定义布局
假设我们要创建一个 博客布局 并将其保存到 /web/layouts/BlogLayout/index.tsx
:
tsx
import { memo } from 'react';
export interface BlogLayoutProps {
children: React.ReactNode;
}
export default memo(({ children }: BlogLayoutProps) => {
return (
<>
<div>这个是我的博客导航栏</div>
{children}
</>
);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
然后我们必须再次创建一个 /web/layouts/BlogLayout/routes.ts
文件设置路由并确定哪些页面用到此布局:
ts
import { lazy } from 'react';
import { IRouter } from '@/@types/router';
// import About from '@/pages/About';
const LayoutRoutes: IRouter[] = [
{
path: '/about',
meta: {
title: '关于',
keywords: '关于k',
description: '关于d',
navActive: 'about',
},
//component: About,
component: lazy(() => import('@/pages/About')),
},
];
export default LayoutRoutes;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
最后我们需要到 /web/config/router.tsx
中配置上:
tsx
/**
* 配置所有路由
*/
export const originalRoutes: IRouter[] = [
{
path: '/',
children: DefaultLayoutRoutes,
},
{
path: '/blog',
children: BlogLayoutRoutes,
},
];
/**
* 配置框架对应的路由
*/
const layoutToRoutes = {
BlogLayout: pathKeyCreateUseRoutes([routes[1]]),
};
export default memo(() => {
const routesElement = useRoutes(routes);
const location = useLocation();
// 属于 BlogLayout
if (layoutToRoutes.BlogLayout[location.pathname]) {
return (
<BlogLayout>
<SuspenseLazy>{routesElement}</SuspenseLazy>
</BlogLayout>
);
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
React 页面
页面组件实际上是 React 组件,只不过 midway-react-ssr
为这些组件添加了一些特殊的配置项(对应 midway-react-ssr
提供的功能特性)以便你能快速开发通用应用。
tsx
import { memo } from 'react';
import { useAsyncDataPageContext } from '@/store/asyncDataContext';
import { ResponseData } from '@/utils/request';
import { queryDetail } from './service';
import { Article } from './data';
import { IServerPage } from '@/@types/server';
const Detail: IServerPage<Article> = () => {
// 取出数据
const article = useAsyncDataPageContext<Article>();
return (
<div className="detail">
<p>{article.title}</p>
<p>{article.addtime}</p>
<p>{article.content}</p>
</div>
);
};
Detail.asyncData = async ({ paresUrl }) => {
const id = paresUrl.query.id?.toString() || '1';
const response: ResponseData<Article> = await queryDetail(id);
return response.data || {};
};
Detail.seo = ({ asyncData }) => {
return {
title: asyncData.title + '-详情测试',
};
};
export default memo(Detail);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
midway-react-ssr
在 react 函数组件
原有的基础上扩展了 asyncData 方法 和 seo 方法,使用规则你可以点击链接查看明细。
TIP
为了TS友好,请一定绑定 IServerPage
类型。
Midway模板渲染
使用此框架你也可以不用 React ssr
,选择原始的模板渲染。
Midway模板渲染
详细规则请查看 官方文档。