React Router
现代的前端应用大多数是SPA(单页应用程序),也就是只有一个HTML页面的应用程序。因为它的用户体验更好、对服务器压力更小,所以更受欢迎。为了有效的使用单个页面来管理多页面的功能,前端路由应运而生。
- 前端路由功能:让用户从一个视图(组件)导航到另一个视图(组件)
- 前端路由是一套映射规则,在React中,是URL路径与组件的对应关系
- 使用React路由简单来说,就是配置路径和组件
- React Router的使用
React Router 的使用
- 安装 npm i -S react-router-dom / yarn add react-router-dom
相关组件**
- Router组件:包裹整个应用,一个React应用只需要使用一次
Router: HashRouter和BrowserRouter- BrowserRouter:使用H5的history API实现(localhost3000/first)
- HashRouter: 使用URL的哈希值实现 (localhost:3000/#/first)
- Link/NavLink组件:用于指定导航链接(a标签)
最终Link会编译成a标签,而to属性会被编译成 a标签的href属性 - Route组件:指定路由展示组件相关信息(组件渲染)
path属性:路由规则,这里需要跟Link组件里面to属性的值一致
component属性:展示的组件 - Redirect组件: 重定向
<Switch>
# 重定向 from从哪里来 to重定向到何处去
<Redirect from="/" to="/film" />
# 404设置
<Route component={Notfound} />
</Switch>
- Switch组件:
因为react-router是懒匹配,当有多个路由时会从上到下进行模糊匹配,只要符合要求就会进入路由,于是常常会出现进入我们不想进入的组件中,switch可以精确匹配 并且只匹配一个路由就会停止 - withRouter高阶组件: (
为了传递this.props.history
)
把不是通过路由直接渲染出来的组件,将react-router 的 history、location、match 三个对象传入props对象上
默认情况下必须是经过路由匹配渲染的组件才存在this.props,才拥有路由参数,才能使用编程式导航的写法,执行this.props.history.push(’/uri’)跳转到对应路由的页面,然而不是所有组件都直接与路由相连的,当这些组件需要路由参数时,使用withRouter就可以给此组件传入路由参数,此时就可以使用this.props
// 引入withRouter
import { withRouter} from 'react-router-dom'
// 执行一下withRouter
export default withRouter(Cmp)
导航方式
-
声明式导航
使用Link或NavLink组件完成声明式导航的定义
Link/NavLink区别
- Link组件不会根据路由的变化而添加或修改编译后html标签中的属性
- NavLink会根据路由的变化而自动修改编译后html标签中的属性
如果当前的路由规则和Navlink中的To所写的规则一致则添加class样式,
默认名称为active,可以通过activeClassName来修改匹配成功后样式名称。
-
编程式导航
react-router-dom中通过history对象中的push/replace/go等方法实现编程式导航功能。
this.props.history.push({
pathname: "/home",
search: "from=404", // 表示传递查询字符串
state: { // 隐式传参,地址栏不体现
username: "admin",
},
});
注! 重要:在react路由中this.props要想得到路由中的对象,则默认必须要通过路由规则匹配渲染的组件才能有此对象 - 必须是直接渲染的组件,因为涉及到路由参数的传递和编程式导航
传参方式
- 动态路由参数(param)
以“/detail/:id”形式传递的数据 在落地组件中通过this.props.match.params得到
常用于详情页跳转 - 查询字符串(query)
通过地址栏中的 ?key=value&key=value传递 在落地组件中通过this.props.location.search得到
用于搜索跳转 - 隐式传参(state),通过地址栏是观察不到的
通过路由对象中的state属性进行数据传递 在落地组件中通过this.props.location.state得到
埋点 以及账号密码跳转
三种路由渲染方式(面试常问) 共5种
//component (组件对象或函数)
<Route path="/home" component={Home} />
或
<Route path="/home" component={(router)=><Home {…router} />} />
//render (函数)
<Route path="/home" render={router=><Home {…router} />} />
children (函数或组件)
// 全匹配
<Route path="/about" children={router =>{
return <div>children渲染</div>
}} />
或
// 精确匹配
<Route path="/about" children={<About />} />
5种路由渲染方式的区别
- 组件渲染和回调函数的渲染的区别
- 回调函数的形式可以在函数内写逻辑进行判断而直接渲染组件不行
- 同时回调函数的组件收不到 路由对象 需要手动传递
- 关于component和render的回调函数的区别
- component的回调的每次跳转都会销毁原先的页面重新加载 而render不会 从性能优化上来说 我们最好选择render回调 除非特殊情况 需要每次跳转都要执行函数
- 关于children精确匹配基本没用 而全匹配可以帮助我们处理不管是否跳转页面都显示该路由
- children属性和component/render一样,也会接受所有由route传入的所有参数。不过当一个路由匹配失败时,match为null。那么无论路由是否匹配,都可以让我们动态更改要渲染的组件了。
- 全匹配可以处理那种首页导航 无论是否跳转页面都会显示 但是如果不是匹配路由就会接收不到this.props的参数 这样就可以判断是否是当前路由
自定义导航组件
为何需要自定义导航?
因为在项目中往往不是所有的声明式导航都是需要a标签完成,有时候可能需要别的标签,此时如果在需要的地方去写编程式导航就会有代码重复可能性,就在对于公共代码进行提取。
- 定义一个普通组件可以是类组件也可以是函数式组件
- 父组件能向子组件传值 props
- 此高阶组件不管路由规则是否匹配都要有渲染 children
import React, { Component } from 'react';
import { Route,withRouter } from 'react-router-dom'
import Storage from '@/utils/storage'
@withRouter
class AuthRouter extends Component {
// 判断用户是否登录过
checkLogin() {
if (Storage.get('islogin')) {
return true
}
return false;
}
render() {
const Cmp = this.props.cmp
return (
<>
{this.checkLogin() ?
<Route path={this.props.path} render={router => {
return <Cmp {...router} />
}} />
:
this.props.history.push('/login')
}
</>
);
}
}
export default AuthRouter;
等于二层嵌套 在第一层中是可以收到this.props 但是第二层不可以 所以需要配合withRouter一起使用