之前弄react项目配置路由都是直接在App.js中配的,像这样
<Routes>
<Route path='/' element={<Backstage url='/index'></Backstage>}></Route>
<Route path='/login' element={<Login></Login>}></Route>
</Routes>
于是想到能不能实施一种类似于vue的路由配置,将路由的配置信息像vue的格式一样写在一个文件内,类似该格式
{
path:'/index',
name:'index',
component:()=>import('@/views/index/index.vue'),
},
我认为要弄懂该实现方式主要在于如何将component转换成Route的element或Component属性,以导入Index并在Route中使用为例
<Route path='/index' element={<Index></Index>}></Route>
<Route path='/index' Component={Index}></Route>{/* {等价于} */}
相比之下会发现Index是一个funtion类型的(typeof 为function),Index标签是一个JSX.Element类型的,那么这之间该如何转换呢?通过生成JSX.Element的React方法进行转换,但这里我不太懂的是React.createElement第一个参数type应该是string,但是Index却是function是怎么一回事
React.createElement(Index)
接下来使用j两种导入方式。通过控制台输出发现前者为Promise后者为一个object,将Promise通过then输出值发现与后者相同,后者object中的default值与Index相同
import('./views/Index/index');
require('./views/Index/index');
了解以上便可以实现路由的配置功能,基于后台项目常见的一个登录页与复数页面但是使用同一ui架构的方式设想路由,将以view来区分登录与主页,将页面按照分类menu,区分多个页面page;细化下,他们之间拥有共同属性url和name,不同的将是子元素与页面;view是必然拥有页面的但是不一定有子页面,menu为第二层作为分类但是也可能不进行分类的单一页面,因此有可能存在子页面和页面,page第三层只有页面,没有子页面;有以下代码
// 对应页面的object
interface element{
default:string
}
// 公共元素
interface all_common{
url:string,
name:string
}
// 子页面
interface route extends all_common{
element:element,
}
// 基础页面
interface view extends route{
children?:Array<menu>
}
// 菜单
interface menu extends all_common{
element?:element,
children?:Array<route>
}
// 菜单与子页面配置
const pages:Array<menu> = [
{
url:'/index',
name:'',
element:require('../pages/Index/index')
}
]
// 基础页面配置
const routes:Array<view> = [
{
url:'/login',
name:'用户登录',
element:require('../views/Login/login')
},
{
url:'/',
name:'主页',
element:require('../views/Index/index'),
children:pages
}
]
export default routes;
将路由内容解析有以下代码,将子页面均将内容以props传入view的页面中进一步进行处理
import React, { useEffect, useState } from 'react';
import { Route,Routes } from 'react-router-dom';
import routes from './utils/router';
function App() {
// 路由配置导入并解析
let [routelist,setroutelist] = useState<Array<JSX.Element>>();
useEffect(()=>{
// 定义JSX.Element的数组
let list:Array<JSX.Element> = [];
// view遍历,判断是否存在子页面,有则继续遍历
routes.forEach((ve,vi)=>{
if(ve.children){
// 判断是否存在子元素与页面,存在子元素继续遍历,存在页面则使用view的页面,并将内容作为props导入
// 这里分开判断主要是是否点击菜单时也会展示页面,否则使用else if即可
ve.children.forEach((me,mi)=>{
if(me.children){
// 第三层遍历使用view页面并且处理url和name,以细化的方式处理,将内容作为props导入
me.children.forEach((pe,pi)=>{
list.push(<Route path={me.url+pe.url} key={pi} element={React.createElement(ve.element.default,{
url:me.url+pe.url,
name:`${me.name}/${pe.name}`,
element:pe.element
})}></Route>)
})
}
if(me.element) list.push(<Route path={me.url} key={mi} element={React.createElement(ve.element.default,me)}></Route>)
})
}
list.push(<Route path={ve.url} key={vi} element={React.createElement(ve.element.default)}></Route>)
})
setroutelist(list);
},[routes])
return (
<div className="App">
<Routes>
{
routelist
}
</Routes>
</div>
);
}
export default App;