1、单页 Web 应用(single page web application, SPA)
2、整个应用只有一个完整的页面
3、点击页面中的链接不会刷新页面,只会做页面的局部更新
4、数据都需要通过 ajax 请求获取,并在前端异步展现
单页面,多组件
1、一个路由就是一个映射关系(key:value)
2、key 为路径,value 可能是 function 或 component
1、理解: value 是 function ,用来处理客户端提交的请求。
2、注册路由:router.get(path,function(req,res))
3、工作过程:当 node 接收到一个请求时,根据请求路径找到匹配的路由,调用路由中的函数来处理请求,返回响应数据
1、浏览器端路由:value 是 component, 用于展示页面内容
2、注册路由:
3、工作过程:当浏览器的 path 变成 /test 时,当前路由组件就会变成 Test 组件
4、工作原理:底层原理依靠的是浏览器的 history(BOM 对象上面),专门用来管理路由的,原本底层的操作 API 过于繁琐,所以一般都使用三方封装好的。
history 模式,直接使用 H5 推出的 API,个别旧版本的浏览器可能不支持
hash 值(锚点)跳转不会刷新页面,也会留下历史记录。兼容性好
1、React 的一个插件库
2、专门用来实现一个 SPA 应用
3、基于 React 的项目基本都会用到此库
4、react-router 有三种:Web(适用 web 开发)、native(适用 React Native 开发)、any(适用任何场景)
5、路由器(router)是用来管理 路由(route) 的
npm i react-router-dom@5
下面代码中相当于有两个 路由器包裹,所以没法统一管理
About
Home
路径都会有一个 # 号,后面的东西都不会作为资源发送给服务器端
注册路由
放在路由注册的最下方,如果所有的路由都没有匹配上,就跟着 redirect 走
原生 html 中,使用 a 标签跳转不同的页面
在 React 中靠路由链接实现切换组件—编写路由链接
Link 的属性跟 a 标签一致
外层需要 包裹 或者
没法高亮菜单
点击谁就会默认给谁追加一个 类样式名 .active
有一个 属性名 activeClassName=‘activeClass’
总结:
1、NavLink 可以实现路由链接的高亮,通过 activeClassName 指定样式名
2、组件标签,标签体内容是一个特殊的标签属性
3、通过 this.props.children 可以获取标签体内容
基本使用:
import React, { Component } from 'react'
import { NavLink, Route } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import Header from './components/Header'
import './App.css'
// 创建并暴露 App 组件
export default class App extends Component {
render() {
return (
About
Home
)
}
}
二次封装使用:
MyNavLink 组件
import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
let { to, pathName } = this.props
return (
{pathName}
)
}
}
App 组件
import React, { Component } from 'react'
import { Route } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import Header from './components/Header'
import MyNavLink from './components/MyNavLink'
import './App.css'
// 创建并暴露 App 组件
export default class App extends Component {
render() {
return (
{/* About
Home */}
)
}
}
MyNavLink 组件的第二种封装(更加简洁好用)
import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
return (
)
}
}
App 组件
import React, { Component } from 'react'
import { Route } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import Header from './components/Header'
import MyNavLink from './components/MyNavLink'
import './App.css'
// 创建并暴露 App 组件
export default class App extends Component {
render() {
return (
About
Home
{/* About
Home */}
)
}
}
注册的路由一个以上可以包裹起来,这时候匹配到第一个就直接停止了
总结:
1、通常情况下,path 和 component 是一一对应的关系
2、Switch 可以提高路由匹配效率(单一匹配)
import React, { Component } from 'react'
import { Route, Switch } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import Test from './pages/Test'
import Header from './components/Header'
import MyNavLink from './components/MyNavLink'
import './App.css'
// 创建并暴露 App 组件
export default class App extends Component {
render() {
return (
About
Home
Test
{/* About
Home */}
)
}
}
1、明确好界面中的 导航区、展示区
2、导航区的 a 标签改为 Link 标签
总结区别:
1、写法不同:
一般组件:
路由组件:
一般组件:components
路由组件:pages
3、接收到的 props 不同:
一般组件:与组件标签时传递了什么,就接收什么
路由组件:接收到三个固定的属性
{
"history": {
"action": "POP",
"location": {
"pathname": "/about",
"search": "",
"hash": "",
"key": "mlt4yu"
}
},
"location": {
"pathname": "/about",
"search": "",
"hash": "",
"key": "mlt4yu"
},
"match": {
"path": "/about",
"url": "/about",
"isExact": true,
"params": {}
}
}
规范些的写法是 放到 pages 中
不用传参数也可以收到 props
规范些的写法是 放到 components 中
没有传参就不会收到 props
BrowserRouter 刷新页面样式丢失,是出现在二级(多级)路由下,引入样式时会将路由路径添加进去,这时候样式就会丢失,react 就会返回 index.html 的内容回来
./ 以当前文件出发,在当前文件夹下面去找
解决方法 1:(去掉 .)
表示直接去 localhost:3000/css/bootstrap.css 查找
解决方法 2:(%PUBLIC_URL%)
绝对路径解决
解决方法 3:将路由模式改为 HashRouter
注意:包管理 npm 和 yarn 不要混着用,否则容易造成包的丢失
/home/a/b 和/home 就是可以匹配的
import React, { Component } from 'react'
import { Route, Switch } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import Test from './pages/Test'
import Header from './components/Header'
import MyNavLink from './components/MyNavLink'
import './App.css'
// 创建并暴露 App 组件
export default class App extends Component {
render() {
return (
About
Home
Test
{/* About
Home */}
)
}
}
import React, { Component } from 'react'
import { Route, Switch } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import Test from './pages/Test'
import Header from './components/Header'
import MyNavLink from './components/MyNavLink'
import './App.css'
// 创建并暴露 App 组件
export default class App extends Component {
render() {
return (
About
Home
Test
{/* About
Home */}
)
}
}
1、默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序一致)
2、开启严格匹配
3、严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由
一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到 Redirect 指定的路由
import React, { Component } from 'react'
import { Route, Switch, Redirect } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
import Test from './pages/Test'
import Header from './components/Header'
import MyNavLink from './components/MyNavLink'
import './App.css'
// 创建并暴露 App 组件
export default class App extends Component {
render() {
return (
About
Home
Test
{/* About
Home */}
)
}
}
React 中路由注册从 App 里面开始的,路由的匹配都是优先注册的先匹配
总结:
1、注册子路由时要写上父路由的 path 值
2、路由的匹配时按照注册路由顺序进行匹配
import React, { Component } from 'react'
import { Route, Switch, Redirect } from 'react-router-dom'
import MyNavLink from '../../components/MyNavLink'
import News from './News'
import Message from './Message'
export default class Home extends Component {
render() {
return (
我是 Home 的页面内容
news
Message
)
}
}
路由链接(携带参数):
{el.title}
注册路由(声明接收):
接收参数
let { id } = this.props.match.params
不需要声明接收, 正常注册路由即可
路由链接(携带参数):
{el.title}
注册路由(无需声明,正常注册即可):
接收参数
this.props.location.search
备注:获取到的 search 时 urlencoded 编码字符串,需要借助 querystring 解析
BrowserRouter 一直维护 history,所以刷新页面 state 中的数据不会丢失。如果清空浏览器缓存和历史记录等才会没有 state。
路由链接(携带参数):
{el.title}
注册路由(无需声明,正常注册即可):
接收参数
this.props.location.state
备注:参数不体现在地址栏上面,但刷新也可以保留住参数
默认时 push 模式,不做替换,能够留下所有的痕迹
开启 replace 模式:不留下痕迹
两种模式(push 和 replace)可以携带三种形式的参数,携带参数和路由注册以及接收参数都需要保持一致
借助 this.props.history 对象上的 API 进行前进、后退
this.props.history.replace
this.props.history.push
this.props.history.push(path, state) // state 参数传递
this.props.history.goForward() //前进
this.props.history.goBack() // 后退
this.props.history.go() // 整数为前进 n 步,负数为后退 n 步,0为当前页刷新
一般组件没有 history,也就是一般组件中不能用路由导航的 API
withRouter 能够接收一个一般组件,将一般组件加工后,能够拥有路由组件的 API
1、底层原理不一样
BrowserRouter 使用的是 H5 的 history API,不兼容 IE9 及以下版本
HashRouter 使用的 URL 的哈希值
2、path 表现形式不一样
BrowserRouter 的路径中没有 # ,例如: localhost:3000/demo/test
HashRouter 的路径中有 # ,例如: localhost:3000/#/demo/test
3、刷新后对路由 state 参数的影响
BrowserRouter 没有任何影响,因为 state 保存在 history 对象中
HashRouter 刷新后会导致路由 state 参数的丢失,因为它没有 history 对象
4、备注:HashRouter 可以用于解决一些路径错误相关的问题