React-router 手动实现静态路由(staticRoute)

使用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 },
]
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

twinkle||cll

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值