嵌套路由
- 在Route中使用render嵌套(v4版本)
<Route path='/Comp1' render={props => <Comp1 {...props}>
<Route index path={`/Comp1/Son1`} component={Son1} />
<Route path={`/Comp1/Son2`} component={Son2} />
</Comp1>}>
</Route>
- 外部组件实现嵌套
//父组件内 这里不要加exact
<Route path='/Comp1' component={Comp1} />
//Comp1组件内 路径可以写成动态的`${path}/Son1`,path从props.match中获取
<Link to={`Comp1/Son1`}>Son1</Link>
<Link to={`Comp1/Son2`}>Son2</Link>
<Route path={`Comp1/Son1`} component={Son1} />
<Route path={`Comp1/Son2`} component={Son2} />
默认路由:
- 可以使用重定向Redirect实现
function App() {
return (
<div className="App">
<Router>
<Main>
<Menus />
<Switch>
<Route path={"/home"} component={Home}></Route>
<Route path={"/list"} component={List}></Route>
<Route path={"/children"} component={Children}></Route>
<Redirect to='/home' /> //这里一进入页面会默认匹配home,需要放到最后,前面不匹配时才匹配这个
</Switch>
</Main>
</Router>
</div>
);
}
- 增加一个路由,component为默认路由组件
function App() {
return (
<div className="App">
<Router>
<Main>
<Menus />
<Switch>
<Route exact path={"/"} component={Children}></Route> 默认展示Children组件,注意需要加exact,否则后面路由不生效
<Route exact path={"/home"} component={Home}></Route>
<Route exact path={"/list"} component={List}></Route>
<Route path={"/children"} component={Children}></Route>
</Switch>
</Main>
</Router>
</div>
);
}
嵌套路由的情况
//父路由
<Route path={"/children"} component={Children}></Route>
//子路由
const Children = () => {
return (
<div>
<h2>我是children组件</h2>
<nav>
<Link to={"/children/children1"}>children页面</Link>
<Link to={"/children/children2"}>children页面</Link>
</nav>
<Route exact path={"/children/"} component={Children1}></Route>这里path等于'/children/'或者'/children'都可以,但是需要严格匹配
<Route path={"/children/children1"} component={Children1}></Route>
<Route path={"/children/children2"} component={Children2}></Route>
</div>
);
};
- 注意:v4还不支持Router中嵌套多个子组件,只能有一个根元素,否则就会报错
动态传参
1. params传参(不好用)
①Route path后用 冒号 + key 拼接,用 / 隔开多个参数;
②Link 接收参数,并传入参数值;
//Route和Link中的值一一对应
<Route exact path='/Comp2/:id/:name' component={Comp2} />
<Link to='/Comp2/123/xiaowang'>Comp2</Link>
使用方法:在Comp2组件内部通过props.match.params可以拿到参数
优点:刷新页面参数不丢失。
缺点:①只能传递字符串,没办法直接传递对象,只能通过JSON.stringify转换为JSON对象再转换回来(JSON.parse)的方式传递。
②参数会拼接到地址栏,传递过多参数url会变得很长,同时也缺乏安全性。
③参数必须在路由上配置。
(解决不能传Object的问题)通过JSON.stringify和JSON.parse转换对象
const data={'name':'xw',age:18}
<Route exact path='/Comp2/:data' component={Comp2} />
<Link to={`/Comp2/${JSON.stringify(data)}`}>Comp2</Link>
//Comp2中获取
console.log(props,JSON.parse(props.match.params.data));
2. 通过query传参(更方便)
①将Link中的to的属性值传为一个包含pathname(n小写)和参数的对象
②使用时通过props.location.query获取
<Route exact path='/Comp2' component={Comp2} />
//Link
const data = { 'name': 'xw', age: 18 }
const path = {
pathname: '/Comp2',
query: data
}
<Link to={path}>Comp2</Link>
//获取
console.log(props.location.query);
优点:不拼接参数到地址栏,传参方便,不限制传参类型
缺点:刷新页面参数丢失。
3. 通过state传值(最好用)
将上述query对象中的参数名改为state即可,通过props.location.state获取
<Route exact path='/Comp2' component={Comp2} />
//Link
const data = { 'name': 'xw', age: 18 }
const path = {
pathname: '/Comp2',
state: data
}
<Link to={path}>Comp2</Link>
//获取
console.log(props.location.state);
同时具备上述两个方法的优点。
①参数不拼接到url后面,非明文方式传参更安全。
②使用方便。
③刷新页面参数不丢失。
React-Router Hook
- 注意:你必须使用 react@16.8+ 且 react-router-dom@5+ 才能使用这些 hooks!
- useHistory
可以获取到history对象
import {
Route,
useHistory,
} from "react-router-dom";
const Children=()=>{
let history = useHistory();
const handleClick = () => {
console.log(history);
history.push("/list");
};
return(
<button type="button" onClick={handleClick}>跳转</button>
)
}
- useLocation
返回当前url的location对象(属于windows内置对象),可以返回实时的loacation对象,上面有一些内置方法go、push、replace以及传过来的参数等等。
- useParams
返回params方式传递的参数对象(拼接在url后面的参数的对象),当前对应的match.params。
//路由
<Link to={'/children/xw/18'}>children页面</Link>
<Route path={"/children/:name/:age"} component={Children}></Route>
//Children组件
import {
useParams
} from "react-router-dom";
let {name,age} =useParams();
console.log('params',name,age);
- useRouteMatch
可以以的方式去匹配当前的url,返回的是match对象。例如嵌套路由中子路由的path可以通过useRouteMatch获取到父路由的path,再拼接得到。
useRouteMatch返回的对象是props里面的match对象。
import {
useRouteMatch
} from "react-router-dom";
//父路由
<Route path={"/children/:name/:age"} component={Children}></Route>
//子路由
let match=useRouteMatch();
<Link to={`${match.path}/children1`}>children页面</Link>
<Link to={`${match.path}/children2`}>children页面</Link>
<Route path={`${match.path}/children1`} component={Children1}></Route>
<Route path={`${match.path}/children2`} component={Children2}></Route>
- useRouteMatch还可以接收参数,参数是的path属性对象;
useRouteMatch({
path:'/home',
strict:true, //严格匹配
sensitive:true //区分大小写
})
动态路由
- 利用antd+react-router+pubsub写了一个demo,gitee地址(目前只更新了V6以下的版本)。
代码目录:
oldVersion是V6以下版本(我用的是v5),newVersion之后会更新V6版本
依赖版本:
- 主入口
import React, { Suspense } from "react";
import config from "./oldVersion/dynamicRouter/config";
import { BrowserRouter as Router } from "react-router-dom";
import { Layout } from "antd";
import RouteMenu from "./oldVersion/Menus";
import { RouteWithSubRoutes } from "./utill";
const { Sider } = Layout;
//左右结构,侧边栏Slider,右边另一块Layout
const DynamicRouter = () => {
return (
<Router>
<Layout>
<Sider width={200} className="site-layout-background">
<RouteMenu /> //路由菜单<Link> <NavLink>
</Sider>
<Layout style={{ padding: "0 24px 24px" }}>
<Suspense fallback={<div>loading...</div>}> //这里用懒加载按需引用模块
{config.map((route, i) => (
<RouteWithSubRoutes key={i} {...route} /> //根据路由配置文件遍历选择路由
))}
</Suspense>
</Layout>
</Layout>
</Router>
);
};
export default DynamicRouter;
- 路由配置文件
import React, { lazy } from "react";
import {
SmileTwoTone,
ProfileTwoTone,
ContactsTwoTone,
GithubOutlined,
TwitterOutlined,
} from "@ant-design/icons";
//懒加载引入组件
const handleLazy = (name) => {
return lazy(() => import(`../${name}`));
};
const config = [
{
key: "sub1",
label: "首页", //用key、label、icon是为了用antd的Menu组件
icon: <SmileTwoTone />,
path: "/home",
component: handleLazy("Home"), //注意:component对应的应该是callback,否则无法匹配<Route>
exact: true,
},
{
key: "sub2",
label: "列表",
icon: <ProfileTwoTone />,
path: "/list",
component: handleLazy("List"),
exact: true,
},
{
key: "sub3",
label: "人员",
icon: <ContactsTwoTone />,
path: "/children",
component: handleLazy("Children"),
exact: false,
children: [
{
key: "children1",
label: "人员1",
icon: <GithubOutlined />,
path: "/children/children1",
component: handleLazy("Children1"),
exact: true,
},
{
key: "children2",
icon: <TwitterOutlined />,
label: "人员2",
path: "/children/children2",
component: handleLazy("Children2"),
exact: true,
},
],
},
];
export default config;
- RouteMenu 路由菜单
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { Menu } from "antd";
import config from "../oldVersion/dynamicRouter/config";
import PubSub from "pubsub-js";
const Menus = () => {
const [selectedKeys, setSelectedKeys] = useState([]);
//这里用递归处理一下配置文件中的label,使得他们被<Link>包裹(否则用不了路由)
const handleLable = (value) => {
return value.children
? {
...value,
children: value.children.map((child) => handleLable(child)),
}
: {
...value,
label: <Link to={value.path}>{value.label}</Link>,
};
};
useEffect(() => {
//订阅backtohome这条消息
PubSub.subscribe("backtohome", (_, data) => {
setSelectedKeys([data]); //点击人员中的按钮返回首页
});
}, []);
return (
<Menu
mode="inline"
defaultOpenKeys={[config[0].key]}
selectedKeys={selectedKeys}
onClick={(v) => setSelectedKeys([v.key])}
style={{
height: "100%",
borderRight: 0,
}}
items={config.map((i) => handleLable(i))}
></Menu>
);
};
export default Menus;
- Children(嵌套路由)
import React from "react";
import {
useHistory,
} from "react-router-dom";
import { HomeOutlined } from "@ant-design/icons";
import { Button, Tooltip } from "antd";
import PubSub from 'pubsub-js';
import {RouteWithSubRoutes} from '../utill';
const Children = (props) => {
let history = useHistory();
//点击返回首页
const handleClick = () => {
//发布backtohome使得Menu更新界面
PubSub.publish('backtohome','sub1');
history.push("/home");//更新路由
};
return (
<div>
<h2>我是children组件</h2>
{props.children.length > 0 &&
props.children.map((route, i) => (
<RouteWithSubRoutes key={i} {...route} /> //遍历出子路由,我需要子路由显示在这里,所以在这里遍历(Route与它对应的Link应该在同一层级,比如Link是最外层,那么对应的Route应该也应该是最外层,不应该被其他Route包裹)
))}
<div style={{width:'100%',textAlign:'right'}}>
<Tooltip title="返回首页">
<Button type="primary" shape="circle" icon={<HomeOutlined />} onClick={handleClick} />
</Tooltip>
</div>
</div>
);
};
export default Children;
- utill.js(公共方法)
import { Route, Redirect } from "react-router-dom";
/*
根据路由配置选择Route
*/
const RouteWithSubRoutes = (route) => {
if (!route.path) return <Route component={NotFound} />;
return (
<Route
exact={route.exact}
strict
path={route.path}
render={(props) => //render的意思其实就是当path对应上以后,当前Route需要渲染成什么样子,你可以自己定义,需要注意的是这三个属性的优先级(component>render>children),所以你不能同时使用
route.redirect ? (
<Redirect push to={route.redirect} from={route.path}></Redirect>
) : (
<route.component {...props} children={route.children} /> //route.component对应着配置文件中按需引入的component,如果component不是callback这里会报错
)
}
/>
);
};
const NotFound = () => {
return <div>抱歉,页面丢失啦</div>;
};
export { RouteWithSubRoutes };