目录
一、介绍
现代的前端应用大多数是SPA(单页应用程序),也就是只有一个HTML页面的应用程序。因为它的用户体验更好、对服务器压力更小,所以更受欢迎。为了有效的使用单个页面来管理多页面的功能,前端路由应运而生。
Ø 前端路由功能:让用户从一个视图(组件)导航到另一个视图(组件)
Ø 前端路由是一套映射规则,在React中,是URL路径与组件的对应关系
Ø 使用React路由简单来说,就是配置路径和组件
注意:2021年11月路由依赖包react-router-dom开启v6版本,注意新api的变化
在介绍完v5.0稳定版以后,会在文章后面介绍v6.0版本的改动以及案例演示。
二、事前准备
2.1 安装路由模块
npm i -S react-router-dom 安装指令
2.2 相关组件
Ø Router组件:包裹整个应用,一个React应用只需要使用一次
Router: HashRouter和BrowserRouter
HashRouter: 使用URL的哈希值实现 (localhost:3000/#/first)
BrowserRouter:使用H5的history API实现(localhost3000/first)
Ø Link/NavLink组件:用于指定导航链接(a标签)
最终Link会编译成a标签,而to属性会被编译成 a标签的href属性
Ø Route组件:指定路由展示组件相关信息(组件渲染)
path属性:路由规则,这里需要跟Link组件里面to属性的值一致
component属性:展示的组件
三、V5版本路由使用
3.1 声明式导航
使用Link或NavLink组件完成声明式导航的定义
代码展示:
import React, { Component } from 'react';
// 引入相关组件
import { Link,Route,Switch,NavLink } from 'react-router-dom'
// 引入类组件
import Home from './pages/Home';
import User from './pages/User';
class App extends Component {
render() {
return (
<>
{/* 声明式导航 */}
< NavLink to={"/home"} >home页面</NavLink>
< NavLink to={"/user"}>user页面</NavLink>
<Switch>
<Route path={"/home"} component = { Home }></Route>
<Route path={"/user"} component = { User }></Route>
</Switch>
</>
);
}
}
export default App;
运行结果:
3.2 编程式导航
通过与react-router-dom中withRouter与history对象中的push/replace/go等方法搭配使用实现编程式导航功能。
这里需要注意,引入whitRouter,用于解决this.props.history 拿不到值的问题。
import React, { Component } from 'react';
// 引入相关组件
import { Link,Route,Switch,NavLink } from 'react-router-dom'
// 引入类组件
import Home from './pages/Home';
import User from './pages/User';
// 引入withRouter来解决 this.props.history 拿不到值的问题
import { withRouter } from 'react-router-dom'
@withRouter
class App extends Component {
homeHander = () =>{
console.log(this.props.history);
this.props.history.push("/home")
}
userHander = () =>{
console.log(this.props.history);
this.props.history.push("/user")
}
render() {
return (
<>
{/* 编程式导航 */}
<a onClick={this.homeHander}>home界面</a>
<a onClick={this.userHander}>user界面</a>
<Switch>
<Route path={"/home"} component = { Home }></Route>
<Route path={"/user"} component = { User }></Route>
</Switch>
</>
);
}
}
export default App;
3.3 嵌套路由
在有一些功能中,往往请求地址的前缀是相同的,不同的只是后面一部份,此时就可以使用多级路由(路由嵌套)来实现此路由的定义实现。
例如,路由规则如下
admin/index admin/user它们路由前缀的admin是相同的,不同的只是后面一部份。
获取前缀,供后续地址做路由拼接
let prefix = this.props.match.path;
3.4 路由传参
路由参数:在Route定义渲染组件时给定动态绑定的参数。
React路由传参方式有三种:
Ø 动态路由参数(param)
以“/detail/:id”形式传递的数据
在落地组件中通过this.props.match.params得到
Ø 查询字符串(query)
通过地址栏中的 home?key=value&key=value传递
在落地组件中通过this.props.location.search得到
Ø 隐式传参(state),通过地址栏是观察不到的
通过路由对象中的state属性进行数据传递
在落地组件中通过this.props.location.state得
3.4.1 动态路由参数(param)
通过this.props.match.params拿值
运行结果:
3.4.2 查询字符串(query)
通过 this.props.location.search 拿值
运行结果:
3.4.3 隐式传参(state)
这里注意,传过去的参数刷新会消失
通过this.props.location.state拿state的值
通过this.props.history.location.params拿params的值
运行结果:
3.5 重定向 与 404
# 重定向 from从哪里来 to重定向到何处去
<Redirect from="/home" to="/" /># 404设置
<Route component={Notfound} />这里的Notfound需要引入对应的404类组件
3.6 三种路由的渲染方式【了解】
3.6.1 component= (组件对象或者函数)
<Route path="/home" component={Home} />
或
<Route path="/home" component={()=><Home />} />
3.6.2 render 函数
<Route path="/home" render={props=><Home />} />
3.6.3 children(函数或者组件)
// 全匹配
<Route path="/about" children={props=>{
return <div>children渲染</div>
}} />
或
// 精确匹配
<Route path="/about" children={<About />} />
3.6.4 3种渲染区别总结
-
component可以使用组件类渲染或内联式方式渲染,render只能使用函数,children使用函数或直接使用组件对象
-
component内联式渲染方式在每次匹配路由成功后都将创建一个新组件,而后两者不会,所以用内联式方式建议使用后两者,内联方式渲染组件,推荐用render
-
当children的值是一个函数时,无论当前地址和path路径匹不匹配,都将会执行children对应的函数,当children的值为一个组件时,当前地址和path不匹配时,路由组件不渲染
-
children函数方式渲染,会在形参中接受到一个对象,对象中match属性如果当前地址匹配成功返回对象,否则null
3.7 withRouter高阶组件
作用:把不是通过路由切换过来的组件中,将react-router 的 history、location、match 三个对象传入props对象上
默认情况下必须是经过路由匹配渲染的组件才存在this.props,才拥有路由参数,才能使用编程式导航的写法,执行this.props.history.push('/uri')跳转到对应路由的页面,然而不是所有组件都直接与路由相连的,当这些组件需要路由参数时,使用withRouter就可以给此组件传入路由参数,此时就可以使用this.props
import {
useLocation,
useNavigate,
useParams
} from "react-router-dom";
// useNavigate 用来跳转用的 函数组件使用 useLocation接收参数用 useParams接收参数
export function withRouter(Component) {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return (
<Component
{...props}
router={{ location, navigate, params }}
/>
);
}
return ComponentWithRouterProp;
}
3.8 自定义导航组件
为何需要自定义导航?
因为在项目中往往不是所有的声明式导航都是需要a标签完成,有时候可能需要别的标签,此时如果在需要的地方去写编程式导航就会有代码重复可能性,就在对于公共代码进行提取。
思路:
Ø 定义一个普通组件可以是类组件也可以是函数式组件MyLink
Ø 父组件能向子组件传值 props
Ø 不管路由规则是否匹配都要显示组件 Route children渲染方式(函数式)
Ø 注意点:react中组件是大写字母开头 html也是组件
import React, { Component } from 'react';
// 引入相关组件
import { Link,Route,Switch,NavLink } from 'react-router-dom'
// 引入函数组件
import Home from './pages/Home';
import User from './pages/User';
// 引入自定义导航组件
import Mylink from './pages/Mylink';
class App extends Component {
render() {
return (
<>
{/* 传参 */}
< NavLink to="/home" >home页面</NavLink>
< NavLink to="/user">user页面</NavLink>
{/* 引入自定义导航组件 */}
<Mylink to="/home" tag = 'p'>首页</Mylink>
<Mylink to="/user"tag = 'p'>用户页</Mylink>
<Switch>
<Route path="/user" component = { Home }></Route>
<Route path="/user" component = { User }></Route>
</Switch>
</>
);
}
}
export default App;
自定义路由组件 公共组件
import React from 'react';
import { Route , withRouter } from 'react-router-dom'
const Mylink = (props) => {
const handerClick = () => {
props.history.push(props.to)
}
const Tag = props.tag
const to = props.to
return (
// match 使用函数组件 类组件match获取不到最新匹配的值
<Route path={to} children={({match})=>{
console.log('match',match);
if(match){
return <Tag style={{color:'red'}} onClick={ handerClick }
>{ props.children }</Tag>
}
return <Tag onClick={handerClick}
>{ props.children }</Tag>
}} />
);
}
export default withRouter(Mylink);
运行结果:
四、V6版本路由使用
4.1 声明式导航
三个改动:
-
swicth替换为 Routes
-
comopnent替换为 element
-
element里以前是直接放User 现在是<User/>
import React, { Component } from 'react';
import { Route, Routes, Link, useNavigate } from 'react-router-dom'
import User from './pages/User';
import Home from './pages/Home';
class App extends Component {
render() {
return (
<>
{/* swicth替换为 Routes */}
<Routes>
{/* comopnent替换为 element */}
<Route path='/home' element={<Home />}></Route>
{/* 以前是直接放User 现在是<User/> */}
<Route path='/user' element={<User />}></Route>
</Routes>
<Link to={'/home'}>首页</Link>
<Link to={'/user'}>用户</Link>
</>
);
}
}
export default App;
4.2 编程式导航
通过this.props.router.navigate进行路径更改
需要注意的是V6版本中,删除了withRouter,所以需要自己封装,并引入
WithRouter代码如下:
import {
useLocation,
useNavigate,
useParams
} from "react-router-dom";
// useNavigate 用来跳转用的 函数组件使用 useLocation接收参数用 useParams接收参数
export function withRouter(Component) {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return (
<Component
{...props}
router={{ location, navigate, params }}
/>
);
}
return ComponentWithRouterProp;
}
代码展示:
import React, { Component } from 'react';
import { Route, Routes, Link, useNavigate } from 'react-router-dom'
import User from './pages/User';
import Home from './pages/Home';
// 需要自己封装 V6版本删除了withRouter
import {withRouter} from './pages/WithRouter';
class App extends Component {
hander = ()=>{
console.log(this.props);
this.props.router.navigate('/home')
}
handers = ()=>{
console.log(this.props);
this.props.router.navigate('/user')
}
render() {
return (
<>
{/* swicth替换为 Routes */}
<Routes>
{/* comopnent替换为 element */}
<Route path='/home' element={<Home />}></Route>
{/* 以前是直接放User 现在是<User/> */}
<Route path='/user' element={<User />}></Route>
</Routes>
<Link to={'/home'}>首页</Link>
<Link to={'/user'}>用户</Link>
{/* 编程式导航 */}
<button onClick={this.hander}>首页</button>
<button onClick={this.handers}>用户</button>
</>
);
}
}
export default withRouter(App);
运行结果:
4.3 重定向与404
4.3.1 重定向
先引入navigate
import { Route, Routes, Link, useNavigate,Navigate } from 'react-router-dom'
使用
<Route index element={ <Navigate to="/home "/>}/>
4.3.2 404
<Route path= "*" element={ <NotFind />} />
4.4 传参
4.4.1 query传参
this.props.router.location.search 接收参数
this.props.router.navigate('/home?name=xzl')
4.4.2 params传参
this.props.router.params 接收参数
this.props.router.navigate('/user/12345') 传参
<Route path='/user/:id' element={<User />}></Route> 路由更改
4.4.3 state传参
注意:V6中state传参只能用于函数组件,类组件不可用
先引入
import { useLocation } from 'react-router-dom'
再赋值
var localtion = useLocation()
最后通过 localtion.state 接收参数。
4.5 嵌套路由
方法一:
<Routes>
{/* 注意带*号表示下面有路由匹配 */}
<Route path='/home/*' element={<Home />}>
{/* 二级路由不带 / */}
<Route path='Footer' element={<Footer />}></Route>
<Route path='Header' element={<Header />}></Route>
</Route>
<Route path='/user' element={<User />}></Route>
</Routes>
注意这里需要在二级路由页面有类似于vue路由占位符一样的东西。
<Outlet /> 占位符
方法二:
4.6 批量配置路由
import React from 'react'
import { useRoutes } from 'react-router-dom'
import Home from './pages/Home'
import User from './pages/User'
import NotFind from './pages/NotFind'
import MyHeader from './pages/MyHeader'
import MyFooter from './pages/MyFooter'
export default function App() {
//useRoutes //use开头的函数 统称hooks 函数组件专用 v6常用编程了函数组件
var ele = useRoutes([
{
path:'/home',
element: <Home />,
children:[
{path:'myheader',element: <MyHeader /> },
{path:'myfooter',element: <MyFooter /> },
]
},
{ path:'/user',element: <User />},
{ path:'*',element: <NotFind />},
])
return (
<div>
<h3>useRoutes案例</h3>
{ ele }
</div>
)
}