导航
History API
HTML5标准的出现,给history API带来了华丽变身。 history 很早就有,在浏览器历史记录里游走 ,而HTML5赋予它修改URL的超能力!
History 初理解
我们先创建三个html文件 index.html、about.html、concat.html
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>首页</title>
</head>
<body>
<h1>欢迎来到主页</h1>
<a href="about.html">关于我们</a>
</body>
</html>
- about.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>关于我们</title>
</head>
<body>
<h1>关于我们</h1>
<a href="concat.html">联系</a>
<button onclick="">返回主页</button>
</body>
</html>
- concat.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>联系</title>
</head>
<body>
<h1>联系</h1>
<button onclick="">返回主页</button>
<button onclick="">后退一页关于我们</button>
<button onclick="">后退一页</button>
</body>
</html>
在about.html编写函数NavigateHistory
<h1>关于我们</h1>
<a href="concat.html">联系</a>
<button onclick="NavigateHistory(-1)">返回主页</button>
<script>
function NavigateHistory(steps) {
history.go(steps);
}
</script>
history.go API 根据历史跳转到对应的页面。这里我们给了个-1,便是跳转到上一个页面,而上一个页面是首页。(不考虑直接在地址栏输入/about.html,而是直接从index开始)
效果:
concat.html里也是类似的
<h1>联系</h1>
<button onclick="NavigateHistory(-2)">返回主页</button>
<button onclick="NavigateHistory(-1)">后退一页关于我们</button>
<button onclick="history.back()">后退一页</button>
<script>
function NavigateHistory(steps) {
history.go(steps);
}
</script>
这样我们对History便有了一个初始的了解,我们再来深入探讨一下,它究竟是怎么做到的。
✨ 革命性的API三兄弟
history.pushState()- 新增历史记录(不刷新页面)history.replaceState()- 替换当前历史记录popstate 事件- 监听历史记录变化 这组API就像成熟的绅士,让URL变得优雅:http://yourdomain.com/about终于摆脱了 # 的束缚!
SPA 路由模拟
我们随便新建一个html文件。
在body里编写页面。
<h2>SPA 路由模拟</h2>
<button onclick="navigate('/home')">首页</button>
<button onclick="navigate('/about')">关于</button>
<button onclick="navigate('/concat')">联系</button>
<button onclick="navigate('/login')">登录</button>
<button onclick="">支付</button>
<div id="view">当前视图</div>
接着在script中编写navigate函数和render函数
<script>
function navigate(path) {
history.pushState({path},'',path)
render(path);
}
function render(path) {
document.getElementById('view').textContent= `当前视图: ${path}`
}
</script>
navigate负责新增历史记录并且调用render渲染view
history.pushState({path},'',path) 可以把login,about等显示在地址栏,并且存入历史记录
让我们看看效果。
之后编写一个事件监听函数,用于实现历史回退和前进
// 监听浏览器前进/后退事件
window.addEventListener('popstate', (event) => {
console.log('pop state fired: ', event.state);
render(event.state.path || location.pathname);
})
- popstate 事件:当浏览器的活动历史记录条目更改时触发(通常由用户点击前进/后退按钮或通过 history.back() / history.forward() 调用触发)
我们看看效果。
我们看到回退的历史都记录下来了。但是我们也发现,最后会报一个错,这是为什么呢?
报错原因是 event.state 为 null 时尝试访问 path 属性。为什么 event.state 会是 null ?
初始页面加载 :首次打开页面或刷新时,浏览器历史记录的初始状态没有关联的 state 对象
如何解决这个报错呢?我们可以用可选链操作符做一个判断。
// 监听浏览器前进/后退事件
window.addEventListener('popstate', (event) => {
console.log('pop state fired: ', event.state);
render(event.state?.path || location.pathname);
})
再来看看效果。
最后我们想要一种效果,当登录成功后进入支付页面后,我们返回上一个页面,不应该回到登录页面了,而应该跳过登录页面,去登录页面的上一个页面。
因为我们已经登录成功了,不必重复登录了。
history.replaceState
我们该怎么做呢?这就需要用history.replaceState
history.replaceState
我们编写一个replace函数,同时让pay的onclick执行该函数。
<button onclick="replace('/pay')">支付</button>
function replace(path) {
history.replaceState({path}, '', path);
render(path);
}
history.replaceState 修改历史记录 :替换当前历史记录条目,而不是添加新的条目
所以它把本是login的path修改为了pay。所以返回上一个页面是concat。
与 pushState 的区别
- pushState :添加新的历史记录条目,浏览器历史记录长度增加
- replaceState :替换当前历史记录条目,浏览器历史记录长度不变
到这我们就基本了解History API。
总结
从 # 号的丑小鸭时期,到history API的优雅转身,再到React Router的工业化生产,前端路由的进化史就是一部追求更好用户体验的奋斗史。
当你在 About.jsx 中写下简单的About组件,却能通过路由系统无缝展现在用户面前时,是否想过这背后凝聚了多少开发者的智慧?
前端路由的冒险还在继续,而我们——永远是这场冒险中最勇敢的探险家!