React中的路由
概述
路由是什么
路由是一种实现单页面开发的解决方案(SPA),使用路由能够在不刷新页面的情况实现页面的跳转,更加贴近原生应用
怎么用
路由有两种:声明式导航和编程式导航
解决什么问题
通过依赖包能够解决在不刷新页面的情况下实现页面的跳转
下包
yarn add react-router-dom
yarn add react-router-dom@5.2.1
注意:不同版本的 router 使用方式不同
复制代码
声明式导航
基本使用
声明式导航使用的是组件的形式实现路由跳转
import {BrowserRouter as Router,Route,Link}
class Login extends React.Component {
render () {
<div>我是 Login 组件</div>
}
}
class App extends React.Component {
render () {
return (
<BrowserRouter>
<Link to="/login">点我去登录</Link>
<Route path="/login" component={Login}></Route>
</BrowserRouter>
)
}
}
复制代码
注意:Route 和 Link 组件必须放在 BrowserRouter 或者 HashRouter 中
匹配规则
模糊匹配
在 react-router-dom 中匹配默认采用的是模糊匹配,也就是说当路由地址开头部分相同的路由组件都会被一同渲染出来
例如:
import {BrowserRouter as Router,Route,Link}
class Demo1 extends React.Component {
render () {
return <div>我是Demo1</div>
}
}
class Demo2 extends React.Component {
render () {
return <div>我是Demo2</div>
}
}
class App extends React.Component {
render () {
return (
<BrowserRouter>
<Link to="/demo/1">点我去Demo1</Link>
<Link to="/demo/2">点我去Demo2</Link>
<Route path="/demo" component={Demo1}></Route>
<Route path="/demo" component={Demo2}></Route>
</BrowserRouter>
)
}
}
复制代码
只要 Route 中的 path 与 location 中的 pathname 开头部分相同,例如 login 与 / ,或者 /demo/first 与 /demo ,那么就会模糊匹配成功,渲染出所有路由
精准匹配
解决模糊匹配的解决方案,只有 path 与 pathname 完全一致才会匹配成功,渲染组件
import {BrowserRouter as Router,Route,Link}
class Demo1 extends React.Component {
render () {
return <div>我是Demo1</div>
}
}
class App extends React.Component {
render () {
return (
<BrowserRouter>
<Link to="/demo/1">点我去Demo1</Link>
<Route exact path="/demo" component={Demo1}></Route>
</BrowserRouter>
)
}
}
复制代码
上述情况就会匹配失败,原因是因为添加 exact 属性,也就是 path 必须与页面的 pathname 必须完全一致才会匹配成功
404
404 解决方案是利用了 Route 不添加 path 就会无条件的特性
import {BrowserRouter as Router,Route,Link}
class notFount extends React.Component {
render () {
return (<div>我是 404 组件</div>)
}
}
class App extends React.Component {
render () {
return (
<BrowserRouter>
<Route component={notFount}></Route>
</BrowserRouter>
)
}
}
复制代码
路由组件只要不添加 path 属性就会无条件渲染组件
常用组件
在 react-router-dom 中,有许多功能性组件,例如
高亮 NavLink
<NavLink to="/login" activeClassName="router-link-active">登录</NavLink>
复制代码
BrowserRouter
包裹路由组件,一个页面只能有一个 BrowserRouter,使用的是 history 模式的路由
import {BrowserRouter,Route,Link} from 'react-route-dom'
<BrowserRouter>
<Link to="/login">登录</Link>
<Route path="/login" component={Login}></Route>
</BrowserRouter>
复制代码
HashRouter
同上,使用的是 hash 模式的路由
import {HashRouter,Route,Link} from 'react-route-dom'
<HashRouter>
<Link to="/login">登录</Link>
<Route path="/login" component={Login}></Route>
</HashRouter>
复制代码
Route
用于路由组件占位,匹配成功后的路由组件将会替换 Route 组件
import {HashRouter,Route,Link} from 'react-route-dom'
<HashRouter>
<Link to="/login">登录</Link>
<Route path="/login" exact component={Login}></Route>
</HashRouter>
复制代码
可以使用 exact 属性实现精准匹配
Link
声明式导航的跳转组件,通过该组件可以实现页面的跳转
import {HashRouter,Route,Link} from 'react-route-dom'
<HashRouter>
<Link to="/login">登录</Link>
<Route path="/login" exact component={Login}></Route>
</HashRouter>
复制代码
Redirect
重定向组件,通过该组件可以实现路由地址的重定向
import {HashRouter,Route,Link,Redirect} from 'react-route-dom'
<HashRouter>
<Link to="/login">登录</Link>
<Redirect from="/" to="/dashbord"></Redirect>
<Route path="/login" exact component={Login}></Route>
</HashRouter>
复制代码
跳转后失效
Switch
Switch 是为了解决模糊匹配的问题,被该组件包裹的路由匹配规则只会渲染第一个成功匹配的组件
import {HashRouter,Route,Link,Redirect,Switch} from 'react-route-dom'
<HashRouter>
<Link to="/login">登录</Link>
<Redirect from="/" to="/dashbord"></Redirect>
<Switch>
<Route path="/login" exact component={Login}></Route>
</Switch>
</HashRouter>
复制代码
6.0+解决方案
6.0+ 的 react-router-dom 使用方式发生了些许变化
import {BrowserRoter as Router,Routes,Route,Link}
<Router>
<Link to="/login">登录</Link>
<Routes>
<Route path="/login" element={<Login></Login>}></Route>
</Routes>
</Router>
复制代码
编程式导航
跳转
编程式导航是利用了 props.history.push 方法实现跳转
import {BrowserRouter as Router,Route,Link}
class Demo1 extends React.Component {
render () {
return <button onClick={()=>{this.props.history.push('/login')}}>去登录</button>
}
}
class App extends React.Component {
render () {
return (
<BrowserRouter>
<Link to="/demo/1">点我去Demo1</Link>
<Route exact path="/demo" component={Demo1}></Route>
</BrowserRouter>
)
}
}
复制代码
回退
回退也是利用了 props 中的方法
import {BrowserRouter as Router,Route,Link}
class Demo1 extends React.Component {
render () {
return <button onClick={()=>{this.props.history.go(-1)}}>回退到上一页面</button>
}
}
class App extends React.Component {
render () {
return (
<BrowserRouter>
<Link to="/demo/1">点我去Demo1</Link>
<Route exact path="/demo" component={Demo1}></Route>
</BrowserRouter>
)
}
}
复制代码
动态路由
动态路由实际就是路由参数传参的形式,匹配规则为路由参数的动态属性的值
import {BrowserRouter as Router,Route,Link}
<Router>
<Link to="/dashbord/123123">去首页</Link>
<Route path="/dashbord/:userId"></Route>
</Router>
复制代码
路由传参
声明式导航
查询参数传参
传递参数
import {BrowserRouter as Router,Route,Link}
class Demo1 extends React.Component {
render () {
return <button onClick={()=>{this.props.history.go(-1)}}>回退到上一页面</button>
}
}
class App extends React.Component {
render () {
return (
<BrowserRouter>
<Link to="/demo1?id=213">点我去Demo1</Link>
<Route exact path="/demo" component={Demo1}></Route>
</BrowserRouter>
)
}
}
复制代码
接收参数
this.props.location.search
参数是字符串,需要手动转换成对象格式的数据
this.props.location.search
.slice(1)
.split("&")
.map((item) => {
const target = item.split("=");
return { [target[0]]: target[1] };
})
复制代码
路由参数传参
传递参数
import {BrowserRouter as Router,Route,Link}
class Demo1 extends React.Component {
render () {
return <button onClick={()=>{this.props.history.go(-1)}}>回退到上一页面</button>
}
}
class App extends React.Component {
render () {
return (
<BrowserRouter>
<Link to={{pathname:'/demo/123',a:{aa:123123}}}>点我去Demo1</Link>
<Route exact path="/demo/:id" component={Demo1}></Route>
</BrowserRouter>
)
}
}
复制代码
接收参数
this.props.match.params.id
复制代码
注意:对象中的所有的键值对都会被带到 this.props.location 中
编程式导航
查询参数传参
传递参数
import {BrowserRouter as Router,Route,Link}
class Demo1 extends React.Component {
render () {
return <button onClick={()=>{this.props.history.push('/login?id=123')}}>去登录</button>
}
}
class App extends React.Component {
render () {
return (
<BrowserRouter>
<Link to={{pathname:'login',params:{id:123}}}>点我去Demo1</Link>
<Route exact path="/demo" component={Demo1}></Route>
</BrowserRouter>
)
}
}
复制代码
接收参数
this.props.location.search
参数是字符串,需要手动转换成对象格式的数据
this.props.location.search
.slice(1)
.split("&")
.map((item) => {
const target = item.split("=");
return { [target[0]]: target[1] };
})
复制代码
路由参数
传递参数
import {BrowserRouter as Router,Route,Link}
class Demo1 extends React.Component {
render () {
return <button onClick={()=>{this.props.history.push({path:'/login/' + '123'})}}>去登录</button>
}
}
class App extends React.Component {
render () {
return (
<BrowserRouter>
<Link to={{pathname:'/demo/123',params:{id:123}}}>点我去Demo1</Link>
<Route exact path="/demo/:id" component={Demo1}></Route>
</BrowserRouter>
)
}
}
复制代码
接收参数
this.props.match.params.id
复制代码
传参总结
查询参数传参
查询参数的传参可以直接通过 中的 query 对象中添加属性传递参数
可以直接使用 this.props.location.参数名.属性
路由参数传参
路由参数需要提前定义,并且参数不会带到 this.props.location 中,而是会存储在 this.match.params.参数名.属性
seatch 传参
search 传参是以 ?号开头,& 分割,= 号相连的字符串,拼接到地址的末尾,需要手动将其转换成对象
state 传参
state 传参同 query 传参
跨域
package节点配置
由于 create-react-app 中未生成 webpack.config.js ,所以无法添加 proxyServer 插件,开启 proxy 服务
可以在 package.json 中配置代理节点
"proxy":"服务器地址"
复制代码
setupProxy配置
区别与在 package.json 中配置,可以使用第三方包实现跨域 http-proxy-middleware
const proxy = require('http-proxy-middleware')
module.exports (app) => {
app.use(
proxy('/api',{
target:'服务器地址',
changeOrigin:true // 允许跨域
})
)
}
复制代码
路由原理
hash模式
import React from "react";
import "./App.css";
class Demo1 extends React.Component {
render() {
console.log(this.props);
return (
<div>
<p>我是Demo1</p>
</div>
);
}
}
class Demo2 extends React.Component {
render() {
console.log(this);
return <div>我是Demo2</div>;
}
}
class App extends React.Component {
state = {
componentName: "",
};
componentDidMount() {
window.addEventListener("hashchange", (event) => {
this.setState((newState) => {
return {
componentName: window.location.hash.slice(1),
};
});
});
}
render() {
return (
<div>
<button onClick={() => (window.location.hash = "/demo1")}>
点我去Demo1
</button>
<button onClick={() => (window.location.hash = "/demo2")}>
点我去Demo2
</button>
{(this.state.componentName === "/demo1" ||
window.location.hash.slice(1) === "/demo1") && <Demo1></Demo1>}
{(this.state.componentName === "/demo2" ||
window.location.hash.slice(1) === "/demo2") && <Demo2></Demo2>}
</div>
);
}
}
export default App;
复制代码
history 模式
import React from "react";
import "./App.css";
class Demo1 extends React.Component {
render() {
return (
<div>
<p>我是Demo1</p>
</div>
);
}
}
class Demo2 extends React.Component {
render() {
return <div>我是Demo2</div>;
}
}
class App extends React.Component {
state = {
componentName: "",
};
componentDidMount() {
window.addEventListener("popstate", (event) => {
console.log(event);
});
}
render() {
const render = () => {
// 切换 history 地址让他更新一下
this.forceUpdate();
};
return (
<div>
<button
onClick={() => {
window.history.pushState({}, "", "/demo1");
render();
}}
>
点我去Demo1
</button>
<button
onClick={() => {
window.history.pushState({}, "", "/demo2");
render();
}}
>
点我去Demo2
</button>
{window.location.pathname === "/demo1" && <Demo1></Demo1>}
{window.location.pathname === "/demo2" && <Demo2></Demo2>}
</div>
);
}
}
export default App;
复制代码