hash路由切换时页面不刷新是怎么实现的?
我最近在用React写一个单页应用,用了hash模式的路由,但不太明白为什么改变hash值页面不会整个刷新。比如下面这段代码,点链接后URL变了,但组件确实重新渲染了,这是怎么做到的?
import { useState, useEffect } from 'react';
function App() {
const [route, setRoute] = useState(window.location.hash || '#/home');
useEffect(() => {
const onHashChange = () => setRoute(window.location.hash);
window.addEventListener('hashchange', onHashChange);
return () => window.removeEventListener('hashchange', onHashChange);
}, []);
return (
<div>
<a href="#/home">首页</a> | <a href="#/about">关于</a>
<p>当前路由: {route}</p>
</div>
);
}
首先你得知道一个关键点:浏览器的默认行为是,当URL的hash部分发生变化时,不会触发页面重新加载。这就是整个机制的基础。
具体来说是这样的:
当你点击一个指向
#/about的链接时,浏览器会做这么几件事:第一步,浏览器地址栏的hash值变了,从
#/home变成了#/about。第二步,浏览器检测到hash变化,不会去服务器请求新页面,它只是把hash更新到地址栏里。
第三步,浏览器会抛出一个叫做
hashchange的事件,这就是你能监听的那个事件。你代码里的window.addEventListener('hashchange', onHashChange)就是在等这个信号。第四步,你的React组件收到信号,用
setRoute触发状态更新,然后重新渲染。整个流程就是这样:点击链接 → hash变化 → 浏览器不刷新页面 → 触发hashchange事件 → React监听事件 → 更新状态 → 组件重新渲染。
那为什么浏览器要这样设计呢?最早是为了方便页面内跳转,比如一个很长的页面,点击目录里的链接能跳到对应章节,但不会刷新整个页面。后来前端框架就利用这个特性来做路由了。
你写的代码基本思路是对的,不过有个小问题可以优化一下:你的初始状态用了
window.location.hash || '#/home',但如果页面刚加载时hash是空的,这个逻辑会没问题。不过更常见的写法是在useEffect里直接处理初始值,比如这样:如果你用的是react-router,那它底层也是这么个原理,封装得更完善一些,但万变不离其宗,核心就是监听hashchange事件然后手动切换组件。