版本
v3 和 v4 有一些差距:
https://blog.csdn.net/qq_35484341/article/details/80500237
以下的总结, 都是基于 V4 的
官方文档: https://reacttraining.com/react-router/web/guides/quick-start
核心组件和用法
- <BrowserRouter/>
- <HashRouter/>
- <Route/>
- <Switch/>
- <Redirect/>
- <Link/>
- (
- <NavLink/>
- ) withRouter
不多解释, 先上实例:
- <BrowserRouter>
- <Link to="/">主页</Link>
- <Link to="/recommend">推荐</Link>
- <Link to="/detail/1">详情</Link>
- <Route path="/" exact component={Home}/>
- <Route path="/detail/:id" exact component={Detail}/>
- <Route path="/recommend" exact component={Recomm}/>
- </BrowserRouter>
BrowserRouter 和 HashRouter
这两者可以理解为路由的作用域, 所有的 Link 组件和 Route 组件都必须在其内部.
浏览器路由和哈希路由的区别:
BrowserRouter 中的 URL 指向真实的 url, 当页面刷新 (或直接操作 url 并回车) 时, 将产生指向该路径的 url 请求, 如果服务器端没有配置该路径, 则返回 404. 在项目上线后, 需要我们去配置服务端.(通过 Link 并不会向后端发送真实请求)
HashRouter 中 #后面的 uri 并不是真实的请求路径, 对于后端来说, 全都指向同一个地址. 另外哈希历史记录不支持 loaction.key 和 loaction.state, 当通过 state 传递参数的时候, 无法从历史记录中获取到, 可能会导致页面显示异常.
在 BrowserRouter 模式下, 如何在服务端配置呢?
Nginx 配置(前端项目打包), 将任何 URL 访问都指向 index.html
后端服务配置(前端项目打包), 将任何 URL 访问都指向 index.HTML
开启前端服务(需要进行 Nginx 反向代理)
Link
被渲染成 a 标签, 跟 href 属性的所用类似. 点击 Link 标签和 a 标签, 看到的效果是一样的, 但我们推荐 < Link>, 使用它才是单页面应用, 浏览器不会请求页面; 而使用 a 标签, 会重新请求页面.
- <Link to='/path' />
- <Link to={
- pathname, // 路径, 即 to 的字符串用法
- search, // search
- hash, // hash
- state, // state 对象
- action, // location 类型, 在点击 Link 时为 PUSH, 浏览器前进后退时为 POP, 调用
replaceState 方法时为 REPLACE
- key, // 用于操作 sessionStorage 存取 state 对象
- } />
- <Link to='/path' replace={
- true
- } /> // 覆盖当前路径
- NavLink
Link 的加强版
activeClassName(string): 设置选中样式, 默认样式名为 active
activeStyle(object): 当元素被选中时, 为此元素添加样式
exact(bool): 为 true 时, 只有当导致和完全匹配 class 和 style 才会应用
strict(bool): 为 true 时, 在确定为位置是否与当前 URL 匹配时, 将考虑位置 pathname 后的斜线
isActive(func): 判断链接是否激活的额外逻辑的功能. 可以做导航守卫
Route
具体的路由规则
属性:
- // 渲染目标
- component // 渲染组件
- render
- children
- // 地址匹配
- path
- <Route path="/hello/:name">
name 为参数名, 可以在其渲染组件中获取 this.props.params.name
<Route path="/files/*.*">
匹配 /files/hello.jpg , /files/hello.HTML
<Route path="/files/*">
匹配 /files/ , /files/a , /files/a/b
- exact // 完全匹配的意思, 排他
- Switch
有 < Switch > 嵌套, 则其中的 < Route > 在路径相同的情况下, 只匹配第一个, 这个可以避免重复匹配.
无 < Switch > 嵌套, 则其中的 < Route > 在路径相同的情况下全都会匹配, 包括上级路径.
Rediret
重定向到新地址
- <Redirect to='/path' />
- Prompt
当用户准备离开该页面时, 弹出提示
message: 字符串 / 函数, 离开页面时的提示信息
when: 布尔值, 通过设定条件决定是否启用该组件
例如:
- <Prompt message="您确定要离开该页面吗?" when={this.state.isOpen} />
- <Prompt
- message = {() => {this.state.isOpen? false: "您确定要离开该页面吗?"}}
- />
对象和方法
被 Route 绑定的渲染组件, 总是被传入三个属性(对象):history,location,match. 在渲染组件中也会有很多其他组件, 这些组件内部如果想要获取三个对象, 需要 withRouter(通过装饰器或函数调用的形式都可).
我们可以用这三个对象完成很多事情.
history
history 实现对会话历史的管理.
length: number 浏览历史堆栈中的条目数
action: string 路由跳转到当前页面执行的动作, 分为 PUSH, REPLACE, POP
location: object 当前访问地址信息组成的对象, 具有如下属性:
pathname: string URL 路径
search: string URL 中的查询字符串
hash: string URL 的 hash 片段
state: string 例如执行 push(path, state) 操作时, location 的 state 将被提供到堆栈信息里, state 只有在 browser 和 memory history 有效.
push(path, [state]) 在历史堆栈信息里加入一个新条目.
replace(path, [state]) 在历史堆栈信息里替换掉当前的条目
go(n) 将 history 堆栈中的指针向前移动 n.
goBack() 等同于 go(-1)
goForward 等同于 go(1)
block(prompt) 阻止跳转
location
loaction 指当前的位置
- {
- hash: '#sdfas',
- key: 'sdfad1'
- pathname: '/about',
- search: '?name=minooo'
- state: {
- price: 123
- }
- }
可以在不同场景中使用:
- <Link to={location} />
- <NaviveLink to={location} />
- <Redirect to={location />
- history.push(location) history.replace(location) match
match 对象包含了 < Route > 如何与 URL 匹配的信息.
params: object 路径参数, 通过解析 URL 中的动态部分获得键值对
isExact: bool 为 true 时, 整个 URL 都需要匹配
path: string 用来匹配的路径模式, 用于创建嵌套的 <Route>
url: string URL 匹配的部分, 用于嵌套的 <Link>
路由之间的传值
大致有三种方法:
- params
- // 路由设置
- <Route path='/user/:id' component={
- User
- } />
- // 传值
- <Link to='/user/2' />
- this.props.history.push("/user/2");
- // 获取值
- this.props.match.params.id
URL 参数
- // 路由设置
- <Route path='/user' component={
- User
- } />
- // 传值
- <Link to='/user?id=2' />
- this.props.history.push("/user?id=2");
- // 获取值
- this.props.location.seacrh // 获得 "?id=2", 将其进行拆解即可获得
location 对象(哈希路由无法利用)
又有两种方式:
(1)loaction.state. 传的参数是加密的
- // 路由设置
- <Route path='/user' component={
- User
- }></Route>
- // 传值
- <Link to={
- {
- pathname:'/user',state:{
- id:123
- },search:'?sort=name',hash:'#the-hash'
- }
- }>
- this.props.history.push({
- pathname:'/user',state:{
- id:123
- },search:'?sort=name',hash:'#the-hash'
- });
- // 获取值
- this.props.location.state.id
(2)自定义属性
- // 路由设置
- <Route path='/user' component={
- User
- }></Route>
- // 传值
- <Link to={
- {
- pathname:'/user',abc:{
- id:123
- },search:'?sort=name',hash:'#the-hash'
- }
- }>
- this.props.history.push({
- pathname:'/user',abc:{
- id:123
- },search:'?sort=name',hash:'#the-hash'
- });
- // 获取值
- this.props.location.abc.id
导航守卫的实现
在 react-router 中, 并没有提供导航守卫相关的 API, 作者这样描述: 你可以在渲染功能中实现此功能, JSX 不需要 API, 因为它更灵活.
首先, 导航守卫在业务层面可以有三种表现:
根据状态 (如登录 / 未登录) 和身份 (等级) 将路由进行限制. 低等级的用户根本没有定义某些路由.
路由在任何时候都是完整的, 只是根据状态和身份, 将入口进行限制. 没有任何操作能够导向没有权限访问的路由地址.
不对入口进行限制, 根据状态和身份, 将某些特定的路由添加拦截.
在这几种表现中, 2 和 3 并不相互冲突. 现在有两种思路实现导航守卫:
写一个路由配置表, 写一个高阶组件, 导航守卫的功能由高阶组件完成, 所有与路由绑定的组件都被高阶组件修饰.(对应业务场景 3)
写一个路由配置表, 定义一个组件: 根据路由配置生成最终的 < Route>. 对于用户没有权限的路由, 可以控制不将其渲染.(对应业务场景 1)
写一个路由配置表, 写一个高阶组件, 将是否渲染入口的逻辑写在高阶组件中, 所有可能被隐藏的入口都被此高阶组件修饰.(对应业务场景 2)
示例 1(业务场景 1):
- // 路由配置
- const routerConfig = [
- {
- path:'/',
- component:HomePage,
- auth:false,
- },{
- path:'/home',
- component:HomePage,
- auth:true, // 表示必须登录才能访问
- },{
- path:'/login',
- component:LoginPage,
- },{
- path:'/404',
- component:ErrorPage
- }
- ];
- class RouterTab extends React.Component{
- render(){
- return(
- <HashRouter>
- <Switch>
- <RouteGenerator config={routerConfig} />
- </Switch>
- </HashRouter>
- );
- }
- }
- class RouterGenerator extends React.Coponent{
- // 在这里定义限制路由的逻辑(业务场景 1)
- render(){
- const routeConf = this.props.config;
- routeConf.map((conf)=>{
- if(conf.auth) // 判断该路由是否满足渲染条件
- {
- if(login) // 判断当前有没有登录
- {
- retrun <Route path={conf.path} component={conf.component} exact={conf.exact?true:false} />
- }
- }else
- {
- retrun <Route path={conf.path} component={conf.component} exact={conf.exact?true:false} />
- }
- });
- }
- }
示例 2(业务场景 3):
- // 将此高阶组件修饰所有需要被守卫的路由组件
- const routerGuard = (RouteConfig) => {
- retrun (WrappedComponent) => {
- rentrun class NewComponent extends React.Component {
- componentWillMount(){
- const path = this.props.location.pathname;
- const config = RouteConfig.find(f => f.path === path);
- if(!config)
- {
- this.props.history.push("/404");
- }
- if(config.auth && !login)
- {
- this.props.history.push("/login");
- }
- }
- render(){
- retrun(
- <WrappedComponent {...this.props} />
- )
- }
- }
- }
- }
路由的异步加载
如果项目特别大, 打包后的 JS 文件会特别大. 在首次加载时, 一次性地将整个 JS 文件发送给浏览器, 一次性地加载所有代码, 会对浏览器产生比较大的压力, 影响用户体验. 我们希望的是, 只加载当前访问页面相关的 JS 代码, 当路由跳转的时候, 再请求相应页面的 JS 代码. 这需要使用第三方库, 做的比较优秀的有 react-loadable 和 react-async-component.
react-loadable 的原理是将代码进行分割, 分割成若干个 chunk.JS, 需要用到的时候就请求过来.
react-async-component 为按需异步加载, 使用 Promise 和高阶组件实现, 提高性能.
来源: http://www.bubuko.com/infodetail-3194209.html