路由
前言
传统模式下,我们是把整个应用分成了多个页面,然后通过 URL 进行连接。但是这种每次切换页面都需要重新发送所有请求和渲染整个页面,不止性能上会有影响,同时也会导致整个 JavaScript 重新执行,丢失状态。
安装
npm i -S react-router-dom
SPA
Single Page Application
: 单页面应用,整个应用只加载一个页面(入口页面),后续在与用户的交互过程中,通过 DOM 操作在这个单页上动态生成结构和内容
简单说来就是:不管你这个网站有多少页面,我都给你整到一个页面里去。
优点:
- 有更好的用户体验(减少请求和渲染和页面跳转产生的等待与空白),页面切换快
- 注重前端,数据和页面内容由异步请求(AJAX)+ DOM 操作来完成,前端处理更多的业务逻辑
缺点:
- SPA不做页面刷新,只做局部更新,也就是除了你第一次打开网站的时候需要加载整个页面之外,之后的一切站内跳转都是不重载页面的,而是在当前页面进行局部刷新,达到页面切换的效果。
因此首次加载页面的时间较长
。 - 不利于 SEO
SPA 的页面切换机制
虽然 SPA 的内容都是在一个页面通过 JavaScript 动态处理的,但是还是需要根据需求在不同的情况下分内容展示,如果仅仅只是依靠 JavaScript 内部机制去判断,逻辑会变得过于复杂
通过把 JavaScript 与 URL 进行结合的方式
:JavaScript 根据 URL 的变化,来处理不同的逻辑,交互过程中只需要改变 URL 即可。这样把不同 URL 与 JavaScript 对应的逻辑进行关联的方式就是路由,其本质上与后端路由的思想是一样的。
前端路由
前端路由
只是改变了 URL 或 URL 中的某一部分,但一定不会直接发送请求,可以认为仅仅只是改变了浏览器地址栏上的 URL 而已,JavaScript 通过各种手段处理这种 URL 的变化,然后通过 DOM 操作动态的改变当前页面的结构
- URL 的变化不会直接发送 HTTP 请求
- 业务逻辑由前端 JavaScript 来完成
前端路由:我们知道,在url中,#
号之后的内容是不会引起页面跳转的,所以我们利用这一点来实现,当url是#/a
的时候,显示a
的html片段,同理#/b
则显示b
的html片段,这样一来,前端就有了自己的路由控制,可以来实现前端自己的 多页面 。
目前前端路由主要的模式:
- 基于 URL Hash 的路由
- 基于 HTML5 History API 的路由
React Router
React Router 提供了多种不同环境下的路由库
- web
- native
基于 Web 的 React Router
基于 web 的 React Router 为:react-router-dom
import {Route} from ‘react-router-dom’ //引入路由
组件
BrowserRouter 组件
基于 HTML5 History API 的路由组件
HashRouter 组件
基于 URL Hash 的路由组件
在文件加载的主入口
1.入口文件需要再单独引入
import {BrowserRouter, HashRouter} from ‘react-router-dom’
2.引入进来的router必须包裹着当前项目的所有内容
ReactDOM.render(
// 引入进来的router必须包裹着当前项目的所有内容
// <HashRouter>
// <App />
// </HashRouter>,
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
Route 组件
通过该组件来设置应用单个路由信息,Route 组件所在的区域就是就是当 URL 与当前 Route 设置的 path 属性匹配的时候,后面 component 将要显示的区域
<Route path="/" component={IndexP}></Route>
<Route path="/about" component={AboutP}></Route>
<Route path="/detail" component={DetailP}></Route>
exact
exact 属性表示路由使用 精确匹配模式,非 exact 模式下 ‘/’ 匹配所有以 ‘/’ 开头的路由
<Route exact path="/" component={IndexP}></Route>
<Route exact path="/about" component={AboutP}></Route>
<Route exact path="/detail" component={DetailP}></Route>
Link 组件
Link 组件用来处理a
链接 类似的功能(它会在页面中生成一个a
标签),但设置这里需要注意的,react-router-dom
拦截了实际 a
标签的默认动作,然后根据所有使用的路由模式(Hash 或者 HTML5)来进行处理,改变了 URL,但不会发生请求,同时根据 Route 中的设置把对应的组件显示在指定的位置
- Link组件 只改变URL
- path属性:匹配URL
<Link to = "/">首页</Link><span>|</span>
<Link to = "/about">关于我们</Link><span>|</span>
<Link to = "/detail">详情</Link><span>|</span>
to 属性
to 属性类似 a 标签中的 href
传递 props
<Route exact path='/' component={Home}
如果 Route 使用的是 component 来指定组件,那么不能使用 props
Route : render传递参数
在父组件传递参数
function App() {
let [use,setUse] =useState ("给indexP传参")
return (
<div className="App">
<Route exact path="/" render={()=>{
//给IndexP子组件传递参数
return <IndexP use={use} setUse={setUse}></IndexP>
}}></Route>
</div>
);
}
export default App;
通过 render 属性来指定渲染函数,render 属性值是一个函数,当路由匹配的时候指定该函数进行渲染
在子组件接收参数
import React from 'react'
export default function IndexP(props){
console.log(props)
// props.setUse("在子组件使用方法")
let {use,setUse} = props;
return <h2>
<a onClick={()=>{
setUse("在子组件使用方法")
}}>首页</a>
</h2>
}
NavLink 组件
NavLink 与 Link 类似,但是它提供了两个特殊属性用来处理页面导航
NavLink也需要 exact属性进行精确匹配模式
定义组件
import React from "react"
// import {Link} from 'react-router-dom'
import {NavLink} from 'react-router-dom'
export default function Nav(){
return (<nav>
<NavLink
exact
to = "/"
activeClassName = "active"
activeStyle = {{
color:"red"
}}
>首页</NavLink>
{/* <Link to = "/">首页</Link><span>|</span>
<Link to = "/about">关于我们</Link><span>|</span>
<Link to = "/detail">详情</Link><span>|</span> */}
</nav>)
}
在需要的地方引入文件
import Nav from './component/nav'
function App() {
return (
<div className="App">
<Nav></Nav>
</div>
);
}
export default App;
activeStyle
当当前 URL 与 NavLink 中的 to 匹配的时候,激活 activeStyle 中的样式
activeClassName
与 activeStyle 类似,但是激活的是 className
isActive
默认情况下,匹配的是 URL 与 to 的设置,通过 isActive 可以自定义激活逻辑,isActive 是一个函数,返回布尔值
Switch 组件
该组件只会渲染首个被匹配的组件
相当于:
switch(表达式) {
case n:
代码块
break;
case n:
代码块
break;
default:
默认代码块
}
案例内容:判断路由的路径,显示相对应的一个页面,没有与之相对应的页面则显示提示页面
import {Route,Switch, Redirect} from 'react-router-dom' //引入路由
<Switch>
<Route exact path="/" render={()=>{
return <Redirect to="/list/1"></Redirect>
}}></Route>
<Route exact path="/list/:page" render={(props)=>{
return <IndexP {...props}></IndexP>
}}></Route>
<Error component={Error}></Error>
</Switch>
Redirect 组件
重定向
to
设置跳转的 URL
动态路由
为了能给处理上面的动态路由地址的访问,我们需要为 Route 组件配置特殊的 path
withRouter 组件(高阶组件,高阶函数,高阶路由)
如果一个组件不是路由绑定组件,那么该组件的 props 中是没有路由相关对象的,虽然我们可以通过传参的方式传入,但是如果结构复杂,这样做会特别的繁琐。幸好,我们可以通过 withRouter 方法来注入路由对象
import React from 'react'
import {withRouter} from 'react-router-dom'
// console.log(withRouter)
function DetailP(props){
console.log(props)
return <div>
<h1>详情</h1>
<button onClick={()=>{
console.log(props)
}}>按钮</button>
</div>
}
export default withRouter(DetailP)
hooks Router5.0之后出现的
不在类组件内使用
- useHistory :获取history对象
- useLocation :获取Location对象
- useParams :获取Params对象
- useRouteMatch :获取Match对象
import {useHistory ,useLocation , useParams,useRouteMatch } from "react-router-dom"
export default function IndexP(){
console.log(useHistory ())
console.log(useLocation ())
console.log(useParams())
console.log(useRouteMatch())
return <div>
首页
</div>
}
路由参数
- history : 历史记录以及路由给我们的一些操作
-history.go(-1)
- goBack 返回上一步
- goForward 前进一步
- push:修改当前的url
- replace:修改当前的URL
import React from 'react'
export default function IndexP(props){
console.log(props)
let {history} = props
console.log(history.length)
return <div>
<h1>首页 </h1>
<button onClick={()=>{
history.push("/about","state跳转路由传递的参数") //会增加历史记录,能够返回上一步
// history.replace("/about") //不会增加历史记录,不能够返回上一
}}>按钮</button>
</div>
}
- location :获取当前的 URL的一些信息
- pathname:当前的URL
- search:参数
- state:跳转路由时,传递的参数 - match:当前路由匹配的相关规则
- params:动态路由传过来的参数
react-router其优点可以总结如下:
-
风格: 与React融为一体,专为react量身打造,编码风格与react保持一致,例如路由的配置可以通过component来实现
-
简单: 不需要手工维护路由state,使代码变得简单
-
强大: 强大的路由管理机制,体现在如下方面
-
路由配置: 可以通过组件、配置对象来进行路由的配置
-
路由切换: 可以通过 Redirect进行路由的切换
-
路由加载: 可以同步记载,也可以异步加载,这样就可以实现按需加载
-
使用方式: 不仅可以在浏览器端的使用,而且可以在服务器端的使用
-
当然react-router的缺点就是API不太稳定,在升级版本的时候需要进行代码变动。