导航
一、传统多页应用的局限
<nav>
<ul>
<li><a href="./1.html">Page 1</a></li>
<li><a href="./2.html">Page 2</a></li>
</ul>
</nav>
上面的html让我们看到了传统多页应用的实现方式。
这种方式存在明显缺陷:
- 每次导航都会触发整页刷新
- 需要从服务器重新加载HTML文档
- 用户体验不连贯(白屏现象)
二、SPA的雏形:基于hash的路由
SPA(单页应用)是一种Web应用架构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SPA</title>
</head>
<body>
<!-- 锚链接 -->
<a name="top"></a>
<h1>Navigation</h1>
<ul>
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
<div id="content-container" class="content">
Welcome, click on the links above to navigate
</div>
<div class="box" style="height: 200vh;">
</div>
<a href="#top">回到顶部</a>
<script>
const content = document.getElementById('content-container');
window.addEventListener('hashchange', () => {
switch(window.location.hash) {
case '#home':
content.innerHTML = '<h2>Home</h2><p>Welcome to our homepage</p>'
break;
case '#about':
content.innerHTML = '<h2>About</h2><p>Welcome to our aboutpage</p>'
break;
case '#contact':
content.innerHTML = '<h2>Contact</h2><p>Welcome to our contactpage</p>'
break;
default:
break;
}
})
</script>
</body>
</html>
上面的html展示了早期SPA的的实现方式:
window.addEventListener('hashchange', () => {
switch(window.location.hash) {
case '#home':
content.innerHTML = '<h2>Home</h2><p>Welcome to our homepage</p>'
break;
case '#about':
content.innerHTML = '<h2>About</h2><p>Welcome to our aboutpage</p>'
break;
case '#contact':
content.innerHTML = '<h2>Contact</h2><p>Welcome to our contactpage</p>'
break;
default:
break;
}
})
这种方案的优点:
- 无刷新页面切换
- 通过
hashchange事件监听路由变化 - 局部更新DOM内容
但仍有不足:
- URL中带有不美观的#符号
- 需要手动管理DOM更新
- 缺乏系统化的路由管理
三、现代SPA路由:React Router实践
<a> 标签 到 <Link> 组件的进化
在App.jsx 文件的Router组件里,我们放置了以下的代码。
// App.jsx 中的 <Router>组件中
<nav>
<ul>
<li><a href="./">Home</a></li>
<li><a href="./about">About</a></li>
</ul>
</nav>
其他的按照前端路由的奇妙冒险:从稚嫩走向成熟的全过程(上)中所说的方法定义路由,并且创建pages
// App.jsx
import { useState } from 'react'
import './App.css'
import {
BrowserRouter as Router,
Routes,
Route, // 前端路由二选一
} from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'
function App() {
return (
<>
<Router>
<nav>
<ul>
<li><a href="./">Home</a></li>
<li><a href="./about">About</a></li>
</ul>
</nav>
<Routes>
<Route path='/' element={<Home/>}/>
<Route path='/about' element={<About/>}/>
</Routes>
</Router>
</>
)
}
页面pages就你们自己创建一个页面了,方法上篇文章已经阐述,故不再叙述了。
我们看看效果。
我们可以看到虽然已经实现了页面的切换,但是页面的刷新过程,会有片刻的白屏。这对用户的体验会有影响,我们该如何解决这个问题呢?
这时,我们就要引入Link组件了
// App.jsx
import {
Link
} from 'react-router-dom'
然后把App.jsx中的nav标签的代码替换为
// App.jsx
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
</nav>
这时再看看效果
可以看到没有页面的刷新,恭喜你,成功地实现了无刷路由。
总结
从多页到单页 :
- 传统:每个页面独立HTML文件
- SPA:单个HTML + 动态组件切换:
- 早期:直接操作innerHTML, 使用a标签来发送请求,跳转页面,重新渲染页面,会白屏,用户体验差。
- 现代:基于组件的声明式开发,使用Link组件,触发事件,只渲染单独组件,不发送请求,实现无刷。用户体验更好。
后面我会聊聊路由是如何充当路由守卫,来进行权限控制的。这就放在下篇文章吧。