第六章:react-router4
6.1. 相关理解
6.1.1. react-router 的理解
-
react 的一个插件库
-
专门用来实现一个 SPA 应用
-
基于 react 的项目基本都会用到此库
6.1.2. SPA 的理解
-
单页 Web 应用(single page web application,SPA)
-
整个应用只有一个完整的页面
-
点击页面中的链接不会刷新页面, 本身也不会向服务器发请求
-
当点击路由链接时, 只会做页面的局部更新
-
数据都需要通过 ajax 请求获取, 并在前端异步展现
6.1.3. 路由的理解
- 什么是路由?
a. 一个路由就是一个映射关系(key:value)
b. key 为路由路径, value 可能是 function/component
- 路由分类
a. 后台路由: node 服务器端路由, value 是 function, 用来处理客户端提交的请求并返回一个响应数据
b. 前台路由: 浏览器端路由, value 是 component, 当请求的是路由 path 时, 浏览器端前没有发送 http 请求, 但界面会更新显示对应的组件
- 后台路由
a. 注册路由: router.get(path, function(req, res))
b. 当 node 接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来
处理请求, 返回响应数据
- 前端路由
a. 注册路由: <Route path="/about" component={About}>
b. 当浏览器的 hash 变为#about 时, 当前路由组件就会变为 About 组件
6.1.4. 前端路由的实现
- history 库
a. 网址: https://github.com/ReactTraining/history
b. 管理浏览器会话历史(history)的工具库
c. 包装的是原生 BOM 中 window.history 和 window.location.hash
- history API
a. History.createBrowserHistory(): 得到封装 window.history 的管理对象
b. History.createHashHistory(): 得到封装 window.location.hash 的管理对象
c. history.push(): 添加一个新的历史记录
d. history.replace(): 用一个新的历史记录替换当前的记录
e. history.goBack(): 回退到上一个历史记录
f. history.goForword(): 前进到下一个历史记录
g. history.listen(function(location){}): 监视历史记录的变化
- 测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>history test</title>
</head>
<body>
<p><input type="text"></p>
<a href="/test1" onclick="return push('/test1')">test1</a><br><br>
<button onClick="push('/test2')">push test2</button><br><br>
<button onClick="back()">回退</button><br><br>
<button onClick="forword()">前进</button><br><br>
<button onClick="replace('/test3')">replace test3</button><br><br>
<script type="text/javascript"
src="https://cdn.bootcss.com/history/4.7.2/history.js"></script>
<script type="text/javascript">
let history = History.createBrowserHistory() // 方式一
// history = History.createHashHistory() // 方式二
// console.log(history)
function push (to) {
history.push(to)
return false
}
function back() {
history.goBack()
}
function forword() {
history.goForward()
}
function replace (to) {
history.replace(to)
}
history.listen((location) => {
console.log(' 请求路由路径变化了', location)
})
</script>
</body>
</html>
6.2. react-router 相关 API
6.2.1. 组件
<BrowserRouter>
<HashRouter>
<Route>
<Redirect>
<Link>
<NavLink>
<Switch>
6.2.2. 其它
-
history 对象
-
match 对象
-
withRouter 函数
6.3. 基本路由使用
6.3.1. 效果
6.3.2. 准备
-
下载 react-router:
npm install --save react-router@4
-
引入
bootstrap.css: <link rel="stylesheet" href="/css/bootstrap.css">
6.3.3. 路由组件: views/about.jsx
import React from 'react'
export default function About() {
return <div>About 组件内容</div>
}
6.3.4. 路由组件: views/home.jsx
import React from 'react'
export default function About() {
return <div>Home 组件内容</div>
}
6.3.5. 包装 NavLink 组件: components/my-nav-link.jsx
import React from 'react'
import {NavLink} from 'react-router-dom'
export default function MyNavLink(props) {
return <NavLink {...this.props} activeClassName='activeClass'/>
}
6.3.6. 应用组件: components/app.jsx
import React from 'react'
import {Route, Switch, Redirect} from 'react-router-dom'
import MyNavLink from './components/my-nav-link'
import About from './views/about'
import Home from './views/home'
export default class App extends React.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">
{/* 导航路由链接 */}
<MyNavLink className="list-group-item" to='/about' >About</MyNavLink>
<MyNavLink className="list-group-item" to='/home'>Home</MyNavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 可切换的路由组件 */}
<Switch>
<Route path='/about' component={About} />
<Route path='/home' component={Home} />
<Redirect to='/about' />
</Switch>
</div>
</div>
</div>
</div>
</div>
)
}
}
6.3.7. 自定义样式: index.css
.activeClass {
color: red !important;
}
6.3.8. 入口 JS: index.js
import React from 'react'
import ReactDOM from 'react-dom'
import {BrowserRouter, HashRouter} from 'react-router-dom'
import App from './components/app'
import './index.css'
ReactDOM.render(
(
<BrowserRouter>
<App />
</BrowserRouter>
/*<HashRouter>
<App />
</HashRouter>*/
),
document.getElementById('root')
)
6.4. 嵌套路由使用
6.4.1. 效果
6.4.2. 二级路由组件: views/news.jsx
import React from 'react'
export default class News extends React.Component {
state = {
newsArr: ['news001', 'news002', 'news003']
}
render () {
return (
<div>
<ul>
{
this.state.newsArr.map((news, index) => <li key={index}>{news}</li>)
}
</ul>
</div>
)
}
}
6.4.3. 二级路由组件: views/message.jsx
import React from 'react'
import {Link, Route} from 'react-router-dom'
export default class Message extends React.Component {
state = {
messages: []
}
componentDidMount () {
// 模拟发送 ajax 请求
setTimeout(() => {
const data = [
{id: 1, title: 'Message001'},
{id: 3, title: 'Message003'},
{id: 6, title: 'Message006'},
]
this.setState({
messages: data
})
}, 1000)
}
render () {
const path = this.props.match.path
return (
<div>
<ul>
{
this.state.messages.map((m, index) => {
return (
<li key={index}>
<Link to='???'>{m.title}</Link>
</li>
)
})
}
</ul>
</div>
)
}
}
6.4.4. 一级路由组件: views/home.jsx
import React from 'react'
import {Switch, Route, Redirect} from 'react-router-dom'
import MyNavLink from './components/my-nav-link'
import News from './views/news'
import Message from './views/message'
export default function Home() {
return (
<div>
<h2>Home 组件内容</h2>
<div>
<ul className="nav nav-tabs">
<li>
<MyNavLink to='/home/news'>News</MyNavLink>
</li>
<li>
<MyNavLink to="/home/message">Message</MyNavLink>
</li>
</ul>
<Switch>
<Route path='/home/news' component={News} />
<Route path='/home/message' component={Message} />
<Redirect to='/home/news'/>
</Switch>
</div>
</div>
)
}
6.5. 向路由组件传递参数数据
6.5.1. 效果
6.5.2. 三级路由组件: views/message-detail.jsx
import React from 'react'
const messageDetails = [
{id: 1, title: 'Message001', content: ' 我爱你, 中国'},
{id: 3, title: 'Message003', content: ' 我爱你, 老婆'},
{id: 6, title: 'Message006', content: ' 我爱你, 孩子'},
]
export default function MessageDetail(props) {
const id = props.match.params.id
const md = messageDetails.find(md => md.id===id*1)
return (
<ul>
<li>ID: {md.id}</li>
<li>TITLE: {md.title}</li>
<li>CONTENT: {md.content}</li>
</ul>
)
}
6.5.3. 二级路由组件: views/message.jsx
import React from 'react'
import {Link, Route} from 'react-router-dom'
import MessageDetail from "./views/message-detail"
export default class Message extends React.Component {
state = {
messages: []
}
componentDidMount () {
// 模拟发送 ajax 请求
setTimeout(() => {
const data = [
{id: 1, title: 'Message001'},
{id: 3, title: 'Message003'},
{id: 6, title: 'Message006'},
]
this.setState({
messages: data
})
}, 1000)
}
render () {
const path = this.props.match.path
return (
<div>
<ul>
{
this.state.messages.map((m, index) => {
return (
<li key={index}>
<Link to={`${path}/${m.id}`}>{m.title}</Link>
</li>
)
})
}
</ul>
<hr/>
<Route path={`${path}/:id`} component={MessageDetail}></Route>
</div>
)
}
}
6.6. 多种路由跳转方式
6.6.1. 效果
6.6.2. 二级路由: views/message.jsx
import React from 'react'
import {Link, Route} from 'react-router-dom'
import MessageDetail from "./views/message-detail"
export default class Message extends React.Component {
state = {
messages: []
}
componentDidMount () {
// 模拟发送 ajax 请求
setTimeout(() => {
const data = [
{id: 1, title: 'Message001'},
{id: 3, title: 'Message003'},
{id: 6, title: 'Message006'},
]
this.setState({
messages: data
})
}, 1000)
}
ShowDetail = (id) => {
this.props.history.push(`/home/message/${id}`)
}
ShowDetail2 = (id) => {
this.props.history.replace(`/home/message/${id}`)
}
back = () => {
this.props.history.goBack()
}
forward = () => {
this.props.history.goForward()
}
render () {
const path = this.props.match.path
return (
<div>
<ul>
{
this.state.messages.map((m, index) => {
return (
<li key={index}>
<Link to={`${path}/${m.id}`}>{m.title}</Link>
<button onClick={() => this.ShowDetail(m.id)}>查看详情
(push)</button>
<button onClick={() => this.ShowDetail2(m.id)}>查看详情
(replace)</button>
</li>
)
})
}
</ul>
<p>
<button onClick={this.back}>返回</button>
<button onClick={this.forward}>前进</button>
</p>
<hr/>
<Route path={`${path}/:id`} component={MessageDetail}></Route>
</div>
)
}
}