使用vue的同学都知道,
vue
的路由是静态的。什么叫做静态的呢?那就是可以通过一个配置文件来进行路由的配置,而我们的react的路由是动态的。为啥这么说呢?react
路由具有先天的优势——jsx
. 每一个页面都是组件,每一个组件可以随意的嵌套。所以react
的路由是动态加载的。那么我们是否也可以向vue一样,使用一个静态文件来自动配置呢?答案是:肯定可以的。
- 使用
npm install react-router-config
安装这个包,就可以使用静态路由了。详情配置文件查看官网- 我们可以自己手动来实现,因为react-router是非常灵活的。本文就描述大概手动实现的思路
效果
正文
声明配置文件
通过vue的配置文件,我们知道,配置文件最外层一定是一个数组,并且数组里面的对象有children属性。那么我们就构建一个这样的数组。如下:
// 这个对象数组的目的是,含有 Route 里面所有的属性,然后我们也可以自定义一些 name 啥,只是需要我们去手动处理。
const routeConfig = [
{
path: '/news', component: New, exact: false,
// 这里的children里面的path是需要合并上父级的path。这样path就不会重复
children: [
{ path: '/content', component: NewsCont, exact: true },
{ path: '/other', component: NewsOther, exact: true },
{ path: '/', component: NewsIndex, exact: true },
]
},
{ path: '/', component: Index, exact: true },
]
展示组件试图(view)
vue 在注册好router 后,有一个组件叫做
<router-view>
,来作为所有组件的出口。因此我们在react
中也可以实现一个组件,来作为跟组件的出口。
/**
* 核心组件的声明
*/
interface IRootRouteProp {
routeConfig: routeObj[],
basePath?: string,
}
/**
* 核心关键组件
* @param param0
* @returns
*/
function RootRoute({ routeConfig, basePath = '' }: IRootRouteProp) {
if (routeConfig.length === 0) {
return <></>
}
// 判断根路径不能为空
const rt = routeConfig.map((it, index) => {
const { component: Comp, children: Child, path, ...rest } = it;
// 获取一个新的路径,并且把 // 替换成 /
const newPath = (basePath + path).replace(/\/\./g, '/');
// 是否需要使用exact 进行精确匹配
const isExact = 'exact' in it ? it.exact : true
// ... 这里还可以拓展其他的属性,例如 如果需要使用vue的name属性的话
return (
<Route
key={newPath}
{...rest}
exact={isExact}
path={newPath}
render={(value) => {
let crt: JSX.Element = <></>;
if (Array.isArray(Child) && Child.length > 0) {
crt = RootRoute({ routeConfig: Child, basePath: newPath })
}
return (
<Comp {...value} >
{/* 把子组件的route 放在children中, */}
{crt}
</Comp>
)
}}
/>
)
})
return (
<Switch>
{rt}
</Switch>
)
}
然后我们在跟组件中就可以直接这么使用:
{/* 类似 vue的route-view */} 这里传递配置文件的参数
<RootRoute routeConfig={routeConfig} />
在子组件中我们也是需要使用出口的,方式是:
{/* 重新匹配子路由,这一行很关键,子组件是否能够匹配到路由 */}
// 因为我们把嵌套的子路由是放在了children属性中,需要在子组件中进行一下渲染
{prop.children}
最终代码
import React from 'react'
import { BrowserRouter as Router, NavLink, Route, RouteComponentProps, Switch, useRouteMatch } from 'react-router-dom'
/**
* 测试组件
* @returns
*/
export default function StaticRoute() {
return (
<div>
<Router>
<NavLink exact activeStyle={{ color: '#F40' }} to='/' style={{ marginRight: '20px' }}>首页</NavLink>
<NavLink activeStyle={{ color: '#F40' }} to='/news'>新闻页</NavLink>
{/* 类似 vue的route-view */}
<RootRoute routeConfig={routeConfig} />
</Router>
</div>
)
}
/**
* 首页
* @returns
*/
function Index() {
return <h1>首页</h1>
}
interface INew {
children?: JSX.Element
}
/**
* 新闻组件
* @param prop
* @returns
*/
function New(prop: INew) {
const rm = useRouteMatch()
return (
<div>
<div style={{ margin: '30px' }}> 新闻页</div>
<NavLink exact activeStyle={{ color: '#F40' }} to={`${rm.path}/`} style={{ marginRight: '20px' }}>新闻首页</NavLink>
<NavLink exact activeStyle={{ color: '#F40' }} to={`${rm.path}/content`} style={{ marginRight: '20px' }}>新闻内容</NavLink>
<NavLink exact activeStyle={{ color: '#F40' }} to={`${rm.path}/other`} style={{ marginRight: '20px' }}>新闻其他页</NavLink>
{/* 重新匹配子路由,这一行很关键,子组件是否能够匹配到路由 */}
{prop.children}
</div>
)
}
/**
* 新闻首页
* @returns
*/
function NewsIndex() {
return <h2>新闻首页的详情士大夫十分</h2>
}
/**
* 新闻内容
* @returns
*/
function NewsCont() {
return <h2>新闻内容的详情孙菲菲</h2>
}
/**
* 其他新闻
* @returns
*/
function NewsOther() {
return <h2>新闻其他是的方法可你男看就</h2>
}
/**
* 核心组件的声明
*/
interface IRootRouteProp {
routeConfig: routeObj[],
basePath?: string,
}
/**
* 核心关键组件
* @param param0
* @returns
*/
function RootRoute({ routeConfig, basePath = '' }: IRootRouteProp) {
if (routeConfig.length === 0) {
return <></>
}
// 判断根路径不能为空
const rt = routeConfig.map((it, index) => {
const { component: Comp, children: Child, path, ...rest } = it;
// 获取一个新的路径,并且把 // 替换成 /
const newPath = (basePath + path).replace(/\/\./g, '/');
// 是否需要使用exact 进行精确匹配
const isExact = 'exact' in it ? it.exact : true
// ... 这里还可以拓展其他的属性
return (
<Route
key={newPath}
{...rest}
exact={isExact}
path={newPath}
render={(value) => {
let crt: JSX.Element = <></>;
if (Array.isArray(Child) && Child.length > 0) {
crt = RootRoute({ routeConfig: Child, basePath: newPath })
}
return (
<Comp {...value} >
{/* 把子组件的route 放在children中, */}
{crt}
</Comp>
)
}}
/>
)
})
return (
<Switch>
{rt}
</Switch>
)
}
/**
* 路由对象声明
*/
interface routeObj {
path: string;
component: React.ComponentType<RouteComponentProps<any>> | React.ComponentType<any>;
exact: boolean;
children?: routeObj[]
}
// 配置文件
const routeConfig: routeObj[] = [
{
path: '/news', component: New, exact: false,
children: [
{ path: '/content', component: NewsCont, exact: true },
{ path: '/other', component: NewsOther, exact: true },
{ path: '/', component: NewsIndex, exact: true },
]
},
{ path: '/', component: Index, exact: true },
]