单页和多页
- SPA:
Single Page Application单页面应用程序,整个应用中只有一个页面(index.html) - MPA :
Multiple Page Application多页面应用程序,整个应用中有很多个页面(*.html)
优点
-
加快页面响应速度,降低了对服务器的压力
- 传统的多页面应用程序,每次请求服务器返回的都是一整个完整的页面
- 单页面应用程序只有第一次会加载完整的页面,以后每次请求仅仅获取必要的数据
-
更好的用户体验,运行更加流畅
缺点
不利于 SEO 搜索引擎优化
-
因为 爬虫 只爬取 HTML 页面中的文本内容,不会执行 JS 代码
-
可以通过 SSR(服务端渲染 Server Side Rendering)来解决 SEO 问题
- 解释:先在服务器端把内容渲染出来,然后,返回给浏览器的就是纯 HTML 内容了
-
页面静态化,比如,对于一个电商应用可以为每一个商品生产一个静态的HTML页面,静态 HTML 页面中是带有文字内容的,所以,有利于 SEO 的
React 路由介绍
现代的前端应用大多都是 SPA(单页应用程序),也就是只有一个 HTML 页面的应用程序。因为它的用户体验更好、对服务器的压力更小,所以更受欢迎。为了有效的使用单个页面来管理原来多页面的功能,前端路由应运而生。前端路由的功能:让用户从一个视图(页面)导航到另一个视图(页面)
- 前端路由是一套映射规则,在React中,是 URL路径 与 组件 的对应关系
- 使用 React 路由简单来说就是:配置路径和组件(配对)
react模拟hash路由的实现
实现原理
hash值: localhost:3000/#/page1
#之后的部分就是hash值,它有两个特点:
- hash值的变化(地址栏中改了hash值再回车)不会重新发请求。它不是请求路径的一部分。
- hash值的变化会触发hashChange事件。
目录结构
pages/Comment.jsx
pages/Home.jsx
pages/Search.jsx
index.js
index.js
import React, { useEffect, useState } from 'react'
import Home from './pages/Home.jsx'
import Search from './pages/Search.jsx'
import Comment from './pages/Comment.jsx'
import ReactDom from 'react-dom'
export default function App () {
const [curHash, setCurHash] = useState('')
useEffect(() => {
const onChange = () => {
// console.log(window.location.hash)
setCurHash(window.location.hash.slice(1))
}
onChange()
window.addEventListener('hashchange', onChange)
return () => {
window.removeEventListener('hashchange', onChange)
}
}, [])
return (
<div>
<ul>
<li>
<a href="#/home">首页</a>
</li>
<li>
<a href="#/comment">评论</a>
</li>
<li>
<a href="#/search">搜索</a>
</li>
</ul>
<hr />
{curHash === '/home' && <Home />}
{curHash === '/search' && <Search />}
{curHash === '/comment' && <Comment />}
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
React路由使用的基本
版本
v5.reactrouter.com/ (5.X)
React-router提供了一些router的核心api,包括Router, Route, Switch等,但是它没有提供dom操作进行跳转的api
步骤
-
安装包。
npm i react-router-dom@5.3.0这个包提供了三个核心的组件:HashRouter, Route, Link
-
导入包,并使用。
import { HashRouter, Route, Link } from 'react-router-dom'- 使用HashRouter包裹整个应用,一个项目中只会有一个Router
-
使用Link指定导航链接
-
使用
Route指定路由规则(哪个路径展示哪个组件)
-
import React from 'react'
import ReactDom from 'react-dom'
import { HashRouter, Route, Link } 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>
<Link to="/comment">评论</Link>
<Link to="/search">搜索</Link>
<Route path="/comment" component={Comment} />
<Route path="/search" component={Search} />
</HashRouter>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
路由三大对象之-Router
Router 组件:包裹整个应用,一个 React 应用只需要使用一次
两种常用 Router:HashRouter 和 BrowserRouter
-
HashRouter:使用 URL 的哈希值实现(http://localhost:3000/#/first)
- 原理:监听 window 的
hashchange事件来实现的
- 原理:监听 window 的
-
(推荐)BrowserRouter:使用 H5 的 history.pushState() API 实现(http://localhost:3000/first)
- 原理:监听 window 的
popstate事件来实现的
- 原理:监听 window 的
命名
使用es6的导入重命名来统一名字: 无论导入的是哪个路由对象,都叫Router
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
import { HashRouter as Router, Route, Link } from 'react-router-dom'
<Router>
路由三大对象之-Link
Link
Link组件最终会渲染成a标签,用于指定路由导航
- to属性,将来会渲染成a标签的href属性
Link组件无法展示哪个link处于选中的效果
NavLink
NavLink组件,一个更特殊的Link组件,可以用用于指定当前导航高亮
格式:
<NavLink to="/xxx" activeClassName="active">链接</NavLink>
说明:
- to属性,用于指定地址,会渲染成a标签的href属性
- activeClassName: 用于指定高亮的类名,默认
active。一般不去修改。 - exact: 精确匹配,表示必须地址栏和to的属性值 精确匹配类名才生效
测试代码
import React from 'react'
import ReactDom from 'react-dom'
import { BrowserRouter as Router, Route, Link, NavLink } from 'react-router-dom'
import Search from './pages/Search.jsx'
import Comment from './pages/Comment.jsx'
export default function App () {
return (
<div>
<h1>react路由基本使用-Link</h1>
<Router>
<div>
Link:
<Link to="/search">搜索</Link>
<Link to="/comment">评论</Link>
</div>
<div>
NavLink: 自带高亮类
<NavLink to="/" exact>主页</NavLink>
<NavLink to="/search">搜索</NavLink>
<NavLink to="/comment">评论</NavLink>
</div>
<Route path="/comment" component={Comment} />
<Route path="/search" component={Search} />
</Router>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
总结
- link和NavLink都用来做路由跳转。它们都用****属性来指定跳转地址
- link和NavLink的区别是,NavLink指向的路径会自带一个名为 active的css类名
路由三大对象之-Route
route的作用和格式
- 作用: 决定路由匹配规则
- 格式:
<Route path="/xx/xx" component={组件}></Route>
匹配规则
名词约定:
-
path: Route组件中path属性的值
-
pathname: 指的如下格式
- link组件中to的属性值
- 地址栏中的地址
模糊匹配规则
- 只要pathname以path开头就算匹配成功
- 匹配成功就加载对应组件;
- 整个匹配过程是逐一匹配,一个匹配成功了,并不会停止匹配。
模糊匹配和精确匹配
- 默认是模糊匹配的
- 补充exact可以设置成精确匹配
示例
import React from 'react'
import ReactDom from 'react-dom'
import { BrowserRouter as Router, Route, NavLink } 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 />
<Route path="/" component={Home} />
<Route path="/article" component={Article} />
<Route path="/article/123" component={ArticleDetail} />
</Router>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
exact
<Route path="/" exact component={Home} />
总结
-
path 的说明
- 默认情况下,/能够匹配任意/开始的路径
- 如果 path 的路径匹配上了,那么就可以对应的组件就会被 render
-
exact , exact 表示精确匹配某个路径
- 一般来说,如果路径配置了 /, 都需要配置 exact 属性
Switch与404
Switch
用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页
不设置path属性,将404页对应的路由放在switch内部的最后位置
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'))
通过Switch组件非常容易的就能实现404错误页面的提示
页面跳转 Redirect
格式
<Redirect from="/" exact to="/comment" />
示例代码
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'))
编程式导航
页面跳转有两类方式:
- 用户点击链接跳转
- 写代码跳转-编程式导航
编程式导航的格式
import {useHistory} from 'react-router'
export default function App() {
const history = useHistory()
history.push('/find')
// 前进或后退到某个页面,参数 n 表示前进或后退页面数量(比如:-1 表示后退到上一页)
history.go(-1)
// 进入/frend,并替换记录
history.replace('/frend')
}
history.replace和push的区别
push:向历史记录中添加一条
replace:在历史记录中用目标记录来替换当前记录
示例
push
详情页 --> login页(push) ----> 主页
此时,从主页后退,会回到login页。
replace
详情页 --> login页(replace) ----> 主页
此时,从主页后退,会回到详情页。
动态路由与路由参数获取
动态路由
// 可以匹配 /article/1 /article/2 /article/xxx
<Route path="/article/:id" component={Article} />
说明:
- 上面的/:id 称为占位符。id可以改成其他的变量名。
- 占位符可以有多个。例如:
/article/:形参1/:形参2
在组件中接收到路由的参数
有两个方式:
- 通过
props可以
function Article(props){
console.log(props.match.params.id)
}
- 通过
hooks可以
impoirt { useParams } from 'react-router'
const params = useParams()
console.log(params) // 这里有数据
嵌套路由的配置
核心代码
App () {
return
<Router>
<ul>
<li><NavLink to="/find">发现</NavLink></li>
<li><NavLink to="/my">我的音乐</NavLink></li>
<li><NavLink to="/frend">朋友</NavLink></li>
</ul>
<Switch>
<Route path="/find" component={Find}></Route>
<Route path="/my" component={My}></Route>
<Route path="/frend" component={Frend}></Route>
</Switch>
</Router>
}
Find.js
Find(){
return <Router>
<ul>
<li><NavLink to="/find/recommand">推荐</NavLink></li>
<li><NavLink to="/find/top">排行榜</NavLink></li>
<li><NavLink to="/find/list">歌单</NavLink></li>
</ul>
<Switch>
<Route path="/find/recommand" component={Com1}></Route>
<Route path="/my/top" component={Com2}></Route>
<Route path="/frend/list" component={Com3}></Route>
</Switch>
</Router>
}
注意
配置嵌套路由的时候,需要对路径进行处理,必须要先匹配到父级路由,才能匹配到子路由
路由总结
用到的组件
HashRouter, BrowserRouter, Link, NavLink, Route, Redirect, Switch
执行过程
- 点击 Link 组件(a标签),修改了浏览器地址栏中的 url
- React 路由监听到地址栏 url 的变化 hashChange popState
- React 路由内部遍历所有 Route 组件,使用路由规则(path)与 pathname(hash)进行匹配
- 当路由规则(path)能够匹配地址栏中的 pathname(hash) 时,就展示该 Route 组件的内容
宝,你都看到这了不给我一个star嘛?
PS: 如果内容有错误的地方欢迎指出(觉得看着不理解不舒服想吐槽也完全没问题);如果有帮助,欢迎点赞和收藏,转载请著明出处,如果有问题也欢迎私信交流