React Router甚至大部分的前端路由都是依赖于history.js
的,它是一个独立的第三方js库。可以用来兼容在不同浏览器、不同环境下对历史记录的管理,拥有统一的API。
- 老浏览器的history: 通过
hash
来存储在不同状态下的history
信息,对应createHashHistory
,通过检测location.hash
的值的变化,使用location.replace
方法来实现url跳转。通过注册监听window
对象上的hashChange
事件来监听路由的变化,实现历史记录的回退。 - 高版本浏览器: 利用HTML5里面的history,对应
createBrowserHistory
, 使用包括pushState
,replaceState
方法来进行跳转。通过注册监听window
对象上的popstate
事件来监听路由的变化,实现历史记录的回退。 -
node环境下: 在内存中进行历史记录的存储,对应
createMemoryHistory
。直接在内存里push
和pop
状态。
基础路由
安装依赖 npm i -S react-router-dom
引入
import { HashRouter as Router, Route, Link } from 'react-router-dom';
import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter as Router, Route, Link } from 'react-router-dom';
const Home = () => (<div>首页</div>)
const About = () => (<div>关于我们</div>)
const App = () => (
<Router>
<div>
<Link to={"/"}>首页</Link>
<Link to={"/about"}>关于我们</Link>
</div>
{/* exact={true} 只完全匹配/ */}
<div>
<Route path={"/"} component={Home} />
<Route path={"/about"} component={About} />
</div>
</Router>
)
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
BrowserRouter | HashRouter,前者不带#,后者带#
NavLink | Link, 前者可以设置用户的选中样式,后者不可以 ---- 用来做声明式跳转
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, NavLink } from 'react-router-dom';
import "./index.css";
//BrowserRouter 不带#,NavLink有激活状态链接效果
const Home = () => (<div>首页</div>)
const About = () => (<div>关于我们</div>)
const App = () => (
<Router>
<div>
<NavLink to={"/"} exact={true} >首页</NavLink>
<NavLink to={"/about"}>关于我们</NavLink>
</div>
{/* 只完全匹配/ */}
<div>
<Route path={"/"} exact={true} component={Home} />
<Route path={"/about"} component={About} />
</div>
</Router>
)
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
Route 设置 路由对应的组件有 3种形式 component | children | render
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, NavLink } from 'react-router-dom';
import "./index.css";
//BrowserRouter 不带#,NavLink有激活状态链接效果
const Home = () => (<div>首页</div>)
const About = () => (<div>关于我们</div>)
const App = () => (
<Router>
<div>
<NavLink to={"/"} exact={true} >首页</NavLink>
<NavLink to={"/about"}>关于我们</NavLink>
<NavLink to={"/render"}>render页面</NavLink>
<NavLink to={"/child"}>children页面</NavLink>
</div>
{/* 只完全匹配/ */}
<div>
<Route path={"/"} exact={true} component={Home} />
<Route path={"/about"} component={About} />
<Route path="/render" render={() => <div>render页面</div>} />
{/* children 比较特殊,每个页面都会匹配上 */}
<Route path="/child" children={() => <div>children页面</div>} />
</div>
</Router>
)
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
传参
三种方式
history go(-1) push()
location 中的 search state (可以传对象,不会显示在地址栏上)
match 中的 params (会显示在地址栏)
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, NavLink } from 'react-router-dom';
import "./index.css";
//BrowserRouter 不带#,NavLink有激活状态链接效果
const Home = () => (<div>首页</div>)
const About = (props) => (<div>关于我们{props.match.params.userid}</div>)
class Cart extends Component {
render() {
console.log(this.props);
return (<div>购物车页面
用户id:{new URLSearchParams(this.props.location.search).get('id')}
用户名:{new URLSearchParams(this.props.location.search).get('name')}
</div>);
}
}
class State extends Component {
render() {
console.log(this.props);
return (<div>
state页面
{this.props.location.state.username}
</div>);
}
}
/*
props中有
history go(-1) push()
location search state
match params
*/
const App = () => (
<Router>
<div>
<NavLink to={"/"} exact={true} >首页</NavLink>
<NavLink to={"/about/666"}>关于我们</NavLink>
<NavLink to={{ pathname: "/cart", search: "?id=123&name=lili" }}>购物车页面</NavLink>
<NavLink to={{ pathname: "/state", state: { "username": "山海经" } }}>state页面</NavLink>
</div>
{/* 只完全匹配/ */}
<div>
<Route path={"/"} exact={true} component={Home} />
<Route path={"/about/:userid"} component={About} />
<Route path={"/cart"} component={Cart} />
<Route path={"/state"} component={State} />
</div>
</Router>
)
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
编程式导航,js中的页面跳转
goHome = () => {
this.props.history.push('/');
}
goBack = () => {
this.props.history.go(-1);
}
switch及404页面处理
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom';
import "./index.css";
//BrowserRouter 不带#,NavLink有激活状态链接效果
const Home = () => (<div>首页</div>)
const About = (props) => (<div>关于我们{props.match.params.userid}</div>)
class Cart extends Component {
render() {
console.log(this.props);
return (<div>购物车页面</div>);
}
}
const App = () => (
<Router>
<div>
<Link to={"/"} exact={true} >首页</Link>
<Link to={"/about/666"}>关于我们</Link>
<Link to={{ pathname: "/cart" }}>购物车页面</Link>
</div>
{/* 只完全匹配/ */}
<div>
<Switch>
<Route path={"/"} exact={true} component={Home} />
<Route path={"/about/:userid"} component={About} />
<Route path={"/cart"} component={Cart} />
<Route render={() => <div>404页面</div>} />
<Route path="/:userid" render={() => <div>render页面</div>} />
</Switch>
</div>
</Router>
)
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
Redirect \Prompt
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, Link, Switch, Redirect, Prompt } from 'react-router-dom';
import "./index.css";
//BrowserRouter 不带#,NavLink有激活状态链接效果
const Home = () => (<div>首页</div>)
const About = (props) => (<div>关于我们{props.match.params.userid}</div>)
class Center extends Component {
render() {
return (loginType ? <div>用户中心</div> : <Redirect to="/login" />);
}
}
const Login = (props) => (<div>登录</div>)
class Cart extends Component {
render() {
console.log(this.props);
return (<div>购物车页面
<Prompt when={true} message={"您确定要离开这个页面么?"} />
</div>);
}
}
let loginType = false;
const App = () => (
<Router>
<div>
<Link to={"/"} exact={true} >首页</Link>
<Link to={"/about/666"}>关于我们</Link>
<Link to={{ pathname: "/cart" }}>购物车页面</Link>
<Link to={"/center"} >用户中心</Link>
<Link to={"/login"} >登录</Link>
</div>
<div>
<Switch>
<Route path={"/"} exact={true} component={Home} />
<Route path={"/about/:userid"} component={About} />
<Route path={"/cart"} component={Cart} />
<Route path={"/center"} component={Center} />
<Route path={"/login"} component={Login} />
<Redirect from="/*" to="/" />
{/* 404页面定位到首页 */}
</Switch>
</div>
</Router>
)
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
子路由
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import "./index.css";
//BrowserRouter 不带#,NavLink有激活状态链接效果
const Home = () => (<div>首页</div>)
const About = () => (<div>关于我们</div>)
const Login = () => (<div>登录</div>)
//需求:center页面中有两个子页面
class Center extends Component {
render() {
return <div>用户中心
<Link to="/center/child1">子页一</Link>
<Link to="/center/child2">子页二</Link>
<div className="child">
<Route path="/center/child1" component={Child1} />
<Route path="/center/child2" component={Child2} />
</div>
</div>;
}
}
const Child1 = () => (<div>子页1</div>)
const Child2 = () => (<div>子页2</div>)
const App = () => (
<Router>
<div>
<Link to={"/"} exact={true} >首页</Link>
<Link to={"/about"}>关于我们</Link>
<Link to={"/center"} >用户中心</Link>
<Link to={"/login"} >登录</Link>
</div>
<div>
<Route path={"/"} exact={true} component={Home} />
<Route path={"/about/:userid"} component={About} />
<Route path={"/center"} component={Center} />
<Route path={"/login"} component={Login} />
</div>
</Router>
)
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
路由综合实例
装饰器在 react中的配置
create-react-app 支持decorators
yarn add @babel/core @babel/plugin-proposal-decorators @babel/preset-env
创建 .babelrc
{
"presets": [
"@babel/preset-env"
],
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
]
]
}
创建config-overrides.js
const path = require('path')
const { override, addDecoratorsLegacy } = require('customize-cra')
function resolve(dir) {
return path.join(__dirname, dir)
}
const customize = () => (config, env) => {
config.resolve.alias['@'] = resolve('src')
if (env === 'production') {
config.externals = {
'react': 'React',
'react-dom': 'ReactDOM'
}
}
return config
};
module.exports = override(addDecoratorsLegacy(), customize())
安装依赖
yarn add customize-cra react-app-rewired
修改package.json
...
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},
...