一:react-router的理解
单页和多页的区别
首先在了解react-router之前, 我们要知道 vue和react都是基于单页面的开发那么什么是单页面呢?
SPA:
Single Page Application单页面应用程序,整个应用中只有一个页面(index.html)
MPA :
Multiple Page Application多页面应用程序,整个应用中有很多个页面(*.html)
优点:
-
加快页面响应速度,降低了对服务器的压力
- 传统的多页面应用程序,每次请求服务器返回的都是一整个完整的页面
- 单页面应用程序只有第一次会加载完整的页面,以后每次请求仅仅获取必要的数据
-
更好的用户体验,运行更加流畅 缺点: 不利于 SEO 搜索引擎优化
-
因为 爬虫 只爬取 HTML 页面中的文本内容,不会执行 JS 代码
-
可以通过 SSR(服务端渲染 Server Side Rendering)来解决 SEO 问题
- 解释:先在服务器端把内容渲染出来,然后,返回给浏览器的就是纯 HTML 内容了
-
页面静态化,比如,对于一个电商应用可以为每一个商品生产一个静态的HTML页面,静态 HTML 页面中是带有文字内容的,所以,有利于 SEO 的
react-router是什么
vue-router和react-router等前端路由的原理区别不大,都是为了基于单页面开发的产物,为了可以实现无刷新的条件下切换显示不同的页面。
本质:当页面的URL发生变化时,页面的显示结果可以根据URL的变化而变化,但是页面不会全部更新
安装
使用react-router的步骤:
1.安装包npm i react-router-dom@5.3.0
2.导入并使用 import { HashRouter, Route, Link } from 'react-router-dom'
补充:每个路由不同的包的不同功能:
- react-router: 实现了路由的核心功能
- react-router-dom: 基于 react-router,加入了在浏览器运行环境下的一些功能
- react-router-native:基于 react-router,加入了 react-native 运行环境下的一些功能
- react-router-config: 用于配置静态路由的工具库
二:有哪些API
BrowserRouter、HashRouter
BrowserRouter-->history模式
HashRouter-->hash模式
使用方法--例子:
import React from 'react'
import Login from './Login'
import Home from './Home'
import Rect from './Rect'
import { BrowserRouter as Router ,Route, Redirect } from 'react-router-dom'
const Java =(props)=>{
return(
<div>GetValue</div>
)
}
export default class App extends React.Component {
render() {
return (
<Router>
<Route path='/login' component={Login}></Route>
<Route path='/home' component={Home}></Route>
<Route path='/rect' component={Rect}></Route>
<Route path='/java/:id' component={Java}></Route>
<Redirect exact from='/' to='/login' component={Login}></Redirect>
</Router>
)
}
}
Route
作用-->匹配路径,进行组件的渲染
Route的属性
- path 属性:用于设置匹配到的路径
- component 属性:设置匹配到路径后,渲染的组件
- render 属性:设置匹配到路径后,渲染的内容
- exact 属性:开启精准匹配,只有精准匹配到完全一致的路径,才会渲染对应的组件
//path-component
<Route path='/rect' component={Rect}>
//path-component-exact
<Route exact path='/rect' component={Rect}>
//path-render-exact
<Route exact path='/rect' render={()=>{
return <Rect />
}}>
Link 和 NavLink
使用import { Link, NavLink } from 'react-router-dom'
Link
Link组件最终会渲染成a标签,用于指定路由导航
- to属性,将来会渲染成a标签的href属性
Link组件无法展示哪个link处于选中的效果
NavLink
NavLink组件,一个更特殊的Link组件,可以用用于指定当前导航高亮
<Link to="/" exact>首页</Link>
--------
<NavLink to="/" exact active={{color: "green"}}>首页</NavLink>
redirect
用于路由的重定向,当这个组件出现时,就会执行跳转到对应的to路径中,如下例子:
import React from 'react'
import ReactDom from 'react-dom'
import { HashRouter, Route, Link, Redirect } from 'react-router-dom'
import Search from './pages/Search.jsx'
import Comment from './pages/Comment.jsx'
export default function App () {
return (
<div>
<h1>react路由基本使用</h1>
<HashRouter>
<Switch>
<Link to="/comment">评论</Link>
<Link to="/search">搜索</Link>
<Route path="/comment" component={Comment} />
<Route path="/search" component={Search} />
{/* <Route path="/" component={Comment} /> */}
<Redirect from="/" to="/comment" />
</Switch>
</HashRouter>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
当接收到的路径没有匹配到时 将重定向到/comment
switch
问题:
Route组件的匹配成功之后并不会停止,它可能会匹配多个组件
解决:
用Switch组件包裹多个Route组件。
在Switch组件下,不管有多少个Route的路由规则匹配成功,都只会渲染第一个匹配的组件
import React from 'react'
import ReactDom from 'react-dom'
import {
BrowserRouter as Router,
Route,
NavLink,
Switch
} from 'react-router-dom'
const Home = () => <div>主页</div>
const Article = () => <div>文章列表页</div>
const ArticleDetail = () => <div>文章详情页</div>
export default function App () {
return (
<div>
<h1>react路由基本使用</h1>
<Router>
<NavLink to="/">主页</NavLink>
<NavLink to="/article">文章列表页</NavLink>
<NavLink to="/article/123">文章详情页-123</NavLink>
<hr />
<Switch>
<Route path="/" exact component={Home} />
<Route path="/article" component={Article} />
<Route path="/article/123" component={ArticleDetail} />
</Switch>
</Router>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
处理404
import React from 'react'
import ReactDom from 'react-dom'
import {
BrowserRouter as Router,
Route,
NavLink,
Switch
} from 'react-router-dom'
const Home = () => <div>主页</div>
const Article = () => <div>文章列表页</div>
const ArticleDetail = () => <div>文章详情页</div>
const Page404 = () => <div>Page404</div>
export default function App () {
return (
<div>
<h1>react路由基本使用</h1>
<Router>
<NavLink to="/">主页</NavLink>
<NavLink to="/article">文章列表页</NavLink>
<NavLink to="/article/123">文章详情页-123</NavLink>
<hr />
<Switch>
<Route path="/" exact component={Home} />
<Route path="/article" component={Article} />
<Route path="/article/123" component={ArticleDetail} />
<Route component={Page404} />
</Switch>
</Router>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
可以看出 将404页面对应的路由放在switch就解决了
useHistory
使用import { useHistory } from "react-router-dom";
useHistory可以让组件内部直接访问history,无须通过props获取
import { useHistory } from "react-router-dom";
const Login = () => {
const history = useHistory();
return (
<div>
<h1>login</h1>
<button onClick={() => history.push("/")}>Go to home</button>
</div>
);
};
useParams
用于获取当前url匹配到的所有params
通过useParams() 获取返回URL参数的键、值对的对象
import { useParams } from 'react-router-dom'; export default function Foo(){
const params = useParams();
return (
<div>
<h1>{params.id}</h1>
</div>
)
}
useLocation
useLocation 会返回当前 URL的 location对象
import { useLocation } from "react-router-dom";
const Login = () => {
const { pathname } = useLocation();
return (
<div>
<h1>Login</h1>
<p>Current URL: {pathname}</p >
</div>
);
};
三:参数传递
动态路由
<NavLink to="/home">登录</NavLink>
<Switch>
... 其他Route
<Route path="/home/:id" component={Home}/>
<Route component={Nanmap} />
</Switch>
获取参数的方式:
console.log(props.match.params.xxx)
search传递参数
<NavLink to="/home?name=jack&age=18">登录</NavLink>
<Switch>
<Route path="/home" component={Home}/>
</Switch>
获取参数的方式:
console.log(props.location.search)
to传对象
<NavLink to={{
pathname: "/home",
query: {name: "rose", age: 30},
state: {height: 1.68, address: "洛杉矶"},
search: "?apikey=123"
}}>
登录
</NavLink>
获取参数的方式:
console.log(props.location)
state参数
//通过Link的state属性传递参数
<Link
className="nav"
to={`/b/child2`}
state={{ id: 999, name: "i love merlin" }}
>
Child2
</Link>
//注册路由(无需声明,正常注册即可):
<Route path="/b/child2" component={Test}/>
//接收参数:
import { useLocation } from "react-router-dom";
const { state } = useLocation();
//state参数 => {id: 999, name: "我是梅琳"}
//备注:刷新也可以保留住参数
四:路由模式进阶
HashRouter实现原理
改变hash值不会导致浏览器发送请求,就不会刷新页面
所以
hash值改变 会触发window对象的hashchange的事件,HashRouter通过hashchange事件监听URL的变化 从而对DOM操作模拟页面跳转
对应代码:
import React, { Component } from 'react';
import { Provider } from './context'
// 该组件下Api提供给子组件使用
class HashRouter extends Component {
constructor() {
super()
this.state = {
location: {
pathname: window.location.hash.slice(1) || '/'
}
}
}
// url路径变化 改变location
componentDidMount() {
window.location.hash = window.location.hash || '/'
window.addEventListener('hashchange', () => {
this.setState({
location: {
...this.state.location,
pathname: window.location.hash.slice(1) || '/'
}
}, () => console.log(this.state.location))
})
}
render() {
let value = {
location: this.state.location
}
return (
<Provider value={value}>
{
this.props.children
}
</Provider>
);
}
}
export default HashRouter;
可以得出 HashRouter通过window.addEventListener('hashChange',callback)监听hash值的变化,并传递给其嵌套的组件,
然后通过context将location数据往后代组件传递
BrowserRouter
Router组件主要做的是通过BrowserRouter传过来的当前值,通过props传进来的path与context传进来的pathname进行匹配,然后决定是否执行渲染组件
import React, { Component } from 'react';
import { Consumer } from './context'
const { pathToRegexp } = require("path-to-regexp");
class Route extends Component {
render() {
return (
<Consumer>
{
state => {
console.log(state)
let {path, component: Component} = this.props
let pathname = state.location.pathname
let reg = pathToRegexp(path, [], {end: false})
// 判断当前path是否包含pathname
if(pathname.match(reg)) {
return <Component></Component>
}
return null
}
}
</Consumer>
);
}
}
export default Route;