1 单页Web应用(single page web application,SPA)。
2 整个应用只有一个完整的页面。
3 点击页面中的链接不会刷新页面,只会做页面的局部更新。
4 数据都需要通过ajax请求获取, 并在前端异步展现。
1.1 一个路由就是一个映射关系(key:value)
1.2 key为路径, value可能是function或component
1 后端路由:
1.1 理解: value是function, 用来处理客户端提交的请求。
1.2 注册路由: router.get(path, function(req, res))
1.3 工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据
2 前端路由:
1.1 浏览器端路由,value是component,用于展示页面内容。
1.2 注册路由:
1.3 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>前端路由的基石_history</title>
</head>
<body>
<a href="http://www.atguigu.com" onclick="return push('/test1') ">push test1</a><br><br>
<button onClick="push('/test2')">push test2</button><br><br>
<button onClick="replace('')">replace test3</button><br><br>
<button onClick="back()"><= 回退</button>
<button onClick="forword()">前进 =></button>
<script type="text/javascript" src="https://cdn.bootcss.com/history/4.7.2/history.js"></script>
<script type="text/javascript">
// let history = History.createBrowserHistory() //方法一,直接使用H5推出的history身上的API
let history = History.createHashHistory() //方法二,hash值(锚点)
function push (path) {
history.push(path)
return false
}
function replace (path) {
history.replace(path)
}
function back() {
history.goBack()
}
function forword() {
history.goForward()
}
history.listen((location) => {
console.log('请求路由路径变化了', location)
})
</script>
</body>
</html>
1 react的一个插件库。
2 专门用来实现一个SPA应用。
3 基于react的项目基本都会用到此库。
内置组件
1 <BrowserRouter>
2 <HashRouter>
3 <Route>
4 <Redirect>
5 <Link>
6 <NavLink>
7 <Switch>
其它
1 history对象
2 match对象
3 withRouter函数
yarn add react-router-dom@5.2.0 -S
App.jsx
import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Home from './components/Home'
import About from './components/About'
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header"><h2>React Router Demo</h2></div>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 原生html中,靠跳转不同的页面 */}
{/* About
Home */}
{/* 在React中靠路由链接实现切换组件--编写路由链接 */}
<Link className="list-group-item" to="/about">About</Link>
<Link className="list-group-item" to="/home">Home</Link>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
</div>
</div>
</div>
</div>
</div>
)
}
}
index.jsx
//引入react核心库
import React from 'react'
//引入ReactDOM
import ReactDOM from 'react-dom'
//
import {BrowserRouter} from 'react-router-dom'
//引入App
import App from './App'
ReactDOM.render(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.getElementById('root')
1.写法不同:
一般组件:<Demo/>
路由组件:<Route path="/demo" component={Demo}/>
2.存放位置不同:
一般组件:components
路由组件:pages
3.接收到的props不同:
一般组件:写组件标签时传递了什么,就能收到什么
路由组件:接收到三个固定的属性
history:
go: ƒ go(n)
goBack: ƒ goBack()
goForward: ƒ goForward()
push: ƒ push(path, state)
replace: ƒ replace(path, state)
location:
pathname: "/about"
search: ""
state: undefined
match:
params: {}
path: "/about"
url: "/about"
MyNavLink是一般组件,可以把NaviLink中重复的去掉
import React, { Component } from 'react'
import {NavLink} from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
// console.log(this.props);
return (
<NavLink activeClassName="atguigu" className="list-group-item" {...this.props}/>
)
}
}
App.jsx
import React, { Component } from 'react'
import {Route} from 'react-router-dom'
import Home from './pages/Home' //Home是路由组件
import About from './pages/About' //About是路由组件
import Header from './components/Header' //Header是一般组件
import MyNavLink from './components/MyNavLink'
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header/>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 原生html中,靠跳转不同的页面 */}
{/* About
Home */}
{/* 在React中靠路由链接实现切换组件--编写路由链接 */}
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
</div>
</div>
</div>
</div>
</div>
)
}
}
<MyNavLink to="/home">Home</MyNavLink>
也可以写成
<MyNavLink to="/home" children='Home' />
localhost:3000对应的是public文件夹
如果请求样式不存在会给index.html
index.html是兜底的
在调用时候会亲求bootstrap地址,具体地址如下
样式丢失是多级路径把之前的路由也加入了localhost:3000导致找不到css,刷新后会样式丢失。
删除点
请求路径正确了./是相对路径以当前文件出发,/是从localhost出发
可以保留.,但这里改为hashrouter。#号后面都不会认为是前端资源,都是hash值。
要home/a/b但只给了/home,给少了连接不上,给多了可以
下图就是给少了
开启精准匹配
希望一开始就有一个页面,这里用到一个组件redirect,重定向
redirect都匹配不上就去redirect内容
1.注册子路由时要写上父路由的path值
2.路由的匹配是按照注册路由的顺序进行的
1.params参数
路由链接(携带参数):详情
注册路由(声明接收):
接收参数:this.props.match.params
2.search参数
路由链接(携带参数):详情
注册路由(无需声明,正常注册即可):
接收参数:this.props.location.search
备注:获取到的search是urlencoded编码字符串,需要借助querystring解析
3.state参数
路由链接(携带参数):详情
注册路由(无需声明,正常注册即可):
接收参数:this.props.location.state
备注:刷新也可以保留住参数
import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
state = {
messageArr:[
{id:'01',title:'消息1'},
{id:'02',title:'消息2'},
{id:'03',title:'消息3'},
]
}
render() {
//const {messageArr} = this.state
//console.log(messageArr)
return (
<div>
<ul>
{
this.state.messageArr.map((msgObj)=>{
return (
<li key={msgObj.id}>
{/* 向路由组件传递params参数 */}
{/* {msgObj.title} */}
{/* 向路由组件传递search参数 */}
{/* {msgObj.title} */}
{/* 向路由组件传递state参数 */}
<Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>
</li>
)
})
}
</ul>
<hr/>
{/* 声明接收params参数 */}
{/* */ }
{/* search参数无需声明接收,正常注册路由即可 */}
{/* */ }
{/* state参数无需声明接收,正常注册路由即可 */}
<Route path="/home/message/detail" component={Detail}/>
</div>
)
}
}
Message.Detail.index
import React, { Component } from 'react'
// import qs from 'querystring'
const DetailData = [
{id:'01',content:'你好,中国'},
{id:'02',content:'你好,尚硅谷'},
{id:'03',content:'你好,未来的自己'}
]
export default class Detail extends Component {
render() {
console.log(this.props);
// 接收params参数
// const {id,title} = this.props.match.params
// 接收search参数
// const {search} = this.props.location
// const {id,title} = qs.parse(search.slice(1))
// 接收state参数
const {id,title} = this.props.location.state || {}
const findResult = DetailData.find((detailObj)=>{
return detailObj.id === id
}) || {}
return (
<ul>
<li>ID:{id}</li>
<li>TITLE:{title}</li>
<li>CONTENT:{findResult.content}</li>
</ul>
)
}
}
后退会直接去news,所有路由都开启replace就不会留下痕迹了,一个是替换一个是压栈
点击可以切换路由,都靠了link和NavLink
1如果是点击图片跳转如何实现?
1在图片外包裹link
2news组件展示后等3秒钟自动往message跳
这种不点的就是编程式路由导航
借助this.prosp.history对象上的API对操作路由跳转、前进、后退
-this.prosp.history.push()
-this.prosp.history.replace()
-this.prosp.history.goBack()
-this.prosp.history.goForward()
-this.prosp.history.go()
报错undefined,因为是一般组件没有history
,如何让一般组件也用上路由组件的功能。
这时候要用withRouter,它能接收一个一般组件,然后加上路由组件身上属性