react-router 学习笔记
学习地址
https://github.com/ReactTraining/react-router
准备前工作
- 创建一个react项目
npm install -g create-react-app yarn
create-react-app antd-demo
cd antd-demo
$ yarn start
- 下载添加路由
npm i -D react-router react-router-dom
基本功
- 添加导航
-
Home
-
Topics
- 添加导航对应的组件
- 添加一级组件
// 设置Home组件
const Home = () => (
Home
);
// 设置 Topics组件
const Topics = ({ match }) => (
Topics
-
Rendering with React
-
Props v. State
// 设置一个组件,
// 设置默认组件
Please select a topic.
}
/>
);
- 添加一级组件内二级导航
-
Rendering with React
-
Props v. State
- 添加二级导航组件
// 设置一个组件,
- 添加二级导航默认组件
// 设置默认组件
Please select a topic.
}
/>
- 添加二级组件
// 设置二级组件
const Topic = ({ match }) => {
console.log(match);
return (
{match.url}
)
};
- 完整示范
- src/views/basic/index.js
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
// 设置根路由
const BasicExample = () => (
// 根路由标签
-
Home
-
Topics
{/*// 设置跳转的组件*/}
);
// 设置Home组件
const Home = () => (
Home
);
// 设置 Topics组件
const Topics = ({ match }) => (
Topics
-
Rendering with React
-
Props v. State
// 设置一个组件,
// 设置默认组件
Please select a topic.
}
/>
);
// 设置二级组件
const Topic = ({ match }) => {
console.log(match);
return (
{match.url}
)
};
export default BasicExample;
- src/App.js
import React, { Component } from 'react';
import './App.css';
import BasicExample from './views/basic/index.js';
class App extends Component {
render() {
return (
);
}
}
export default App;
- redirect 与登录验证
- 导入包
import React from "react";
import {
BrowserRouter as Router,
Route,
Link,
Redirect,
withRouter
} from "react-router-dom";
- 添加路由导航
-
Public Page
-
Protected Page
-
logout Page
- 添加路由组件
{/*// 定义导航对应组件*/}
{/*// 未登录的*/}
{/*// 登录后的*/}
- 定义路由组件
- 验证组件
// 判断是否登录, 如果登录, 则显示传递进来的组件, 否则重定向到登录组件
const PrivateRoute = ({ component: Component, ...rest }) => (
fakeAuth.isAuthenticated ? (
) : (
)
}
/>
);
- Public组件
- Protected 组件
const Public = () => Public
;
const Protected = () => Protected
;
- 退出组件
class Logout extends React.Component {
constructor(props){
super(props)
this.state = {
redirectToReferrer: fakeAuth.isAuthenticated
}
}
// 退出
logout = () => {
fakeAuth.signout(() => {
this.setState({ redirectToReferrer: false });
});
};
render() {
const { from } = this.props.location.state || { from: { pathname: "/" } };
const { redirectToReferrer } = this.state;
// 判断是否登录,
if (!redirectToReferrer) {
return ( )
}
// 未登录则提示登录
return (
You must log in to view the page at {from.pathname}
);
}
}
- 登录组件
class Login extends React.Component {
constructor(props){
super(props)
this.state = {
redirectToReferrer: fakeAuth.isAuthenticated
}
}
// 登录, 通过验证,
login = () => {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true });
});
};
render() {
const { from } = this.props.location.state || { from: { pathname: "/" } };
const { redirectToReferrer } = this.state;
// 判断是否登录,
if (redirectToReferrer) {
return ;
}
// 未登录则提示登录
return (
You must log in to view the page at {from.pathname}
);
}
}
- 验证数据
// 验证函数, 登录登出
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true;
setTimeout(cb, 100); // fake async
},
signout(cb) {
this.isAuthenticated = false;
setTimeout(cb, 100);
}
};
匹配当前路径
- 定义匹配Link组件
- children 返回当前路径, 会与当前route组件进行匹配, 如果匹配, 则返回当了路径match, 否则返回Null
/*
* 有时您需要渲染路径是否与位置匹配。在这些情况下,您可以使用儿童道具功能。它的工作方式与渲染完全相同,只是它会被调用,无论是否匹配。子渲染道具接收与组件和渲染方法相同的所有路径道具,除非路径无法匹配URL,否则匹配为空。这允许您根据路径是否匹配动态调整UI。如果路线匹配,我们在这里添加一个活动类
* */
const OldSchoolMenuLink = ({ label, to, activeOnlyWhenExact }) => (
{
return (
{match ? "> " : ""}
{label}
)
}}
/>
);
- 添加导航
- 添加导航对应组件
- 定义组件
const Home = () => (
Home
);
const About = () => (
About
);
- 完整代码
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import './index.css'
const CustomLinkExample = () => (
);
/*
* 有时您需要渲染路径是否与位置匹配。在这些情况下,您可以使用儿童道具功能。它的工作方式与渲染完全相同,只是它会被调用,无论是否匹配。子渲染道具接收与组件和渲染方法相同的所有路径道具,除非路径无法匹配URL,否则匹配为空。这允许您根据路径是否匹配动态调整UI。如果路线匹配,我们在这里添加一个活动类
* */
const OldSchoolMenuLink = ({ label, to, activeOnlyWhenExact }) => (
{
console.log(match)
return (
{match ? "> " : ""}
{label}
)
}}
/>
);
const Home = () => (
Home
);
const About = () => (
About
);
export default CustomLinkExample;
路由配置基础
- 引入对应的库
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
- 添加路由配置
const routes = [
{
path: "/sandwiches",
component: Sandwiches
},
{
path: "/tacos",
component: Tacos,
routes: [
{
path: "/tacos/bus",
component: Bus,
routes: [
{
path: "/tacos/bus/bus2",
component: Bus2
},
{
path: "/tacos/bus/bus3",
component: Bus3
}
]
},
{
path: "/tacos/cart",
component: Cart
}
]
}
];
- 添加一级导航
-
Tacos
-
Sandwiches
- 定义一个渲染当前路由配置的组件
传入一个route, 根据route配置绑定给Route, 并使用render渲染该组件, 其中 route.component 是 route配置中的component, 是Route.render渲染时返回的渲染标签
const RouteWithSubRoutes = route => (
(
// pass the sub-routes down to keep nesting
)
}
/>
);
- 定义一个循环渲染当前路由数组的组件
- 该组件值渲染当前数组的一级数组
const renderRoutes = (routes) => {return (routes.map((route, i) => ))};
- 渲染路由
{renderRoutes(routes)}
- 定义一级组件
- Sandwiches组件
const Sandwiches = () => Sandwiches
;
- Tacos组件
const Tacos = ({ routes }) => (
Tacos
);
- 准备渲染二级组件, 在路由配置对象内, 只有tacos组件才有子组件, 因此修改tacos组件
- 这里添加了 二级导航, 和渲染二级导航对应的组件
- 这里的routes 是
render={props =>(
这里的) routes={route.routes}
- renderRoutes组件是上面定义的组件, 他会帮我们循环routes数组, 然后渲染出当前数组的一维数组的组件
const Tacos = ({ routes }) => (
Tacos
-
Bus
-
Cart
{renderRoutes(routes)}
);
- 定义二级组件
- Bus
const Bus = () => Bus
;
- Cart
const Cart = () => Cart
;
10 二级组件定义好, 从路由配置上, 可以看出, 还有三级组件, 因此修改二级组件, 而在这里, 只有Bus组件才有三级组件, 因此只需要修改Bus组件就好
- Bus
跟前面的 Tacos 组件一样, 这里添加了三级导航, 并渲染了三级组件
const Bus = ({routes}) => {
return (
Bus constructor
- /tacos/bus/bus2
- /tacos/bus/bus3
{renderRoutes(routes)}
)
};
- 定义三级组件
const Bus2 = () => Bus2
;
const Bus3= () => Bus3
;
- 完整代码
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
// first our route components
const Sandwiches = () => Sandwiches
;
const Tacos = ({ routes }) => (
Tacos
-
Bus
-
Cart
{renderRoutes(routes)}
);
const renderRoutes = (routes) => {return (routes.map((route, i) => ))};
const Bus = ({routes}) => {
console.log(routes)
return (
Bus constructor
- /tacos/bus/bus2
- /tacos/bus/bus3
{renderRoutes(routes)}
)
};
const Bus2 = () => Bus2
;
const Bus3= () => Bus3
;
const Cart = () => Cart
;
////////////////////////////////////////////////////////////
// then our route config
const routes = [
{
path: "/sandwiches",
component: Sandwiches
},
{
path: "/tacos",
component: Tacos,
routes: [
{
path: "/tacos/bus",
component: Bus,
routes: [
{
path: "/tacos/bus/bus2",
component: Bus2
},
{
path: "/tacos/bus/bus3",
component: Bus3
}
]
},
{
path: "/tacos/cart",
component: Cart
}
]
}
];
// wrap and use this everywhere instead, then when
// sub routes are added to any route it'll work
const RouteWithSubRoutes = route => (
{
console.log(props)
console.log(route)
return (
// pass the sub-routes down to keep nesting
)
}}
/>
);
const RouteConfigExample = () => (
-
Tacos
-
Sandwiches
{renderRoutes(routes)}
);
export default RouteConfigExample;
升级路由配置
- 定义一个路由配置
const routes = [
{
path: "/login",
name: 'Login',
component: Login
},
{
path: "/tacos",
name: 'Tacos',
component: Tacos,
routes: [
{
path: "/tacos/bus",
name: "/tacos/bus",
component: Bus,
routes: [
{
path: "/tacos/bus/bus2",
name: "/tacos/bus/bus2",
component: Bus2
},
{
path: "/tacos/bus/bus3",
name: "/tacos/bus/bus3",
component: Bus3
}
]
},
{
path: "/tacos/cart",
name: "/tacos/Cart",
component: Cart
}
]
}
];
- 定义一级导航
-
Tacos
-
login
- 定义一级组件
{routes.map((route, i) => )}
- 封装渲染当前组件的组件
const RouteWithSubRoutes = route => (
{
return (
)
}}
/>
);
- 定义一级组件
- Login 组件
- Tacos 组件
const Login = () => login
;
const Tacos = ({ routes }) => (
Tacos
);
- 根据路由配置, 因为要在 Tacos组件内, 渲染二级组件, 所以需要修改Tacos组件
const Tacos = ({ routes }) => (
Tacos
{slideMenu(routes)}
{renderRoutes(routes)}
);
- 循环渲染Link组件 slideMenu
- OldSchoolMenuLink是封装的一层Link, 主要用来匹配当前组件并修改当前选中组件的样式
const slideMenu = (routes) => Array.isArray(routes) && routes.map(item => (
{/*{item.name}*/}
{Array.isArray(item.routes) && item.routes.length > 0 && (
{slideMenu(item.routes)}
)}
)
);
- 渲染当前路由数组组件
const renderRoutes = (routes) => {return (routes.map((route, i) => ))};
- 定义二级组件
const Bus= () => Bus
;
const Cart = () => Cart
;
- 修改 Bus组件
- renderRoutes 这里会渲染当前组件下面的子组件
const Bus = ({routes}) => {
return (
Bus constructor
{renderRoutes(routes)}
)
};
- 定义三级组件
const Bus3= () => Bus3
;
const Cart = () => Cart
;
- 完整代码
- js
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import './index.css'
// Some folks find value in a centralized route config.
// A route config is just data. React is great at mapping
// data into components, and is a component.
////////////////////////////////////////////////////////////
// first our route components
const Login = () => login
;
const Tacos = ({ routes }) => (
Tacos
{slideMenu(routes)}
{renderRoutes(routes)}
);
const slideMenu = (routes) => Array.isArray(routes) && routes.map(item => (
{/*{item.name}*/}
{Array.isArray(item.routes) && item.routes.length > 0 && (
{slideMenu(item.routes)}
)}
)
);
const renderRoutes = (routes) => {return (routes.map((route, i) => ))};
const Bus = ({routes}) => {
return (
Bus constructor
{renderRoutes(routes)}
)
};
const Bus2 = () => Bus2
;
const Bus3= () => Bus3
;
const Cart = () => Cart
;
////////////////////////////////////////////////////////////
// then our route config
const routes = [
{
path: "/login",
name: 'Login',
component: Login
},
{
path: "/tacos",
name: 'Tacos',
component: Tacos,
routes: [
{
path: "/tacos/bus",
name: "/tacos/bus",
component: Bus,
routes: [
{
path: "/tacos/bus/bus2",
name: "/tacos/bus/bus2",
component: Bus2
},
{
path: "/tacos/bus/bus3",
name: "/tacos/bus/bus3",
component: Bus3
}
]
},
{
path: "/tacos/cart",
name: "/tacos/Cart",
component: Cart
}
]
}
];
const OldSchoolMenuLink = ({ label, to, exact }) => (
{
return (
{match ? "> " : ""}
{label}
)
}}
/>
);
// wrap and use this everywhere instead, then when
// sub routes are added to any route it'll work
const RouteWithSubRoutes = route => (
{
console.log(props)
console.log(route)
return (
// pass the sub-routes down to keep nesting
)
}}
/>
);
const RouteConfigExample = () => (
-
Tacos
-
login
{routes.map((route, i) => )}
);
export default RouteConfigExample;
- css
.slider{
width: 200px;
float: left;
}
.content {
width: 100%;
padding-left: 200px;
}
ul{
list-style: none;
padding: 0;
margin: 0;
}
li {
padding: 0;
text-align: left;
}
ul>li>ul {
padding-left: 20px;
}