认识
一、认识
二、问题
2.1 说说 React Router 有几种模式?实现原理?
Router Router
有两种模式, BrowserRouter
、HashRouter
、MemoryRouter
模式、StaticRouter
模式。
一、BrowserRouter
模式, 利用 HTML5
的 History API
(pushState/replaceState
)来实现路由跳转, URL
看起来更干净,例如 /home
、/about
。内部借助 history
库, 监听 popstate
事件来响应浏览器的前进后退操作。当调用 push
或 replace
方法时, 通过 History API
更新 URL
,同时 React Router
根据当前 URL
渲染匹配的组件。补充扩展: history
模式通过 HTML5 History API
来实现, API
如下: history.pushState
: 浏览器历史记录添加记录, 不会触发 popstate
事件, 不会触发页面刷新, 因此需要手动执行视图更新逻辑; history.replaceState
: 修改浏览器历史记录中当前记录, 不会触发 popstate
事件, 不会触发页面刷新, 因此需要手动执行视图更新逻辑; popState
事件: 当 history
发生变化时触发, 执行对应的视图更新逻辑; history
模式下, 通过 pushState
或者 replaceState
改变页面地址后, 刷新浏览器, 如果服务端没有对改变之后的 url
做相应的处理, 会出现 404
页面。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>History 路由</title>
</head>
<body>
<div id="root"></div>
<div id="router-container">
<a href="/home">首页</a>
<a href="/about">关于</a>
<a href="/contact">联系</a>
</div>
<script>
class HistoryRouter {
constructor() {
this.routes = {};
this.currentUrl = "";
this.container = document.getElementById("root");
this.init();
}
init() {
window.addEventListener("load", this.refresh.bind(this), false);
window.addEventListener("popstate", this.refresh.bind(this), false);
}
route(path, callback) {
this.routes[path] = callback || function () {};
}
refresh() {
this.currentUrl = location.pathname || "/";
this.routes[this.currentUrl]?.();
}
render(html) {
this.container.innerHTML = html;
}
push(path) {
history.pushState({}, null, path);
this.routes[path]?.();
}
replace(path) {
history.replaceState({}, null, path);
this.routes[path]?.();
}
}
const router = new HistoryRouter();
router.route("/", () => {
router.render("<h1>首页</h1>");
});
router.route("/home", () => {
router.render("<h1>首页</h1>");
});
router.route("/about", () => {
router.render("<h1>关于</h1>");
});
router.route("/contact", () => {
router.render("<h1>联系</h1>");
});
setTimeout(() => {
router.push("/home");
}, 1000);
</script>
</body>
</html>
二、HashRouter
模式, 通过 URL
中的 hash(#
)部分来管理路由, 例如 /#/home
、/#/about
。监听浏览器的 hashchange
事件, 当 URL
的 hash
值发生变化时,触发路由更, 内部同样维护一个虚拟的路由状态, 根据 hash
值匹配相应的路由组件进行渲染。补充扩展: hash
模式 通过 hashchange
事件监听 url
中的 hash
值变化, 执行相应的视图更新逻辑。hash
值的变化不会随请求发送到服务端, 所以改变 hash
, 不会重新加载页面。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Hash 路由</title>
</head>
<body>
<div id="root"></div>
<div id="router-container">
<a href="#/home">首页</a>
<a href="#/about">关于</a>
<a href="#/contact">联系</a>
</div>
<script>
class HashRouter {
constructor() {
this.routes = {};
this.currentUrl = "";
this.container = document.getElementById("root");
this.init();
}
init() {
window.addEventListener("load", this.refresh.bind(this), false);
window.addEventListener("hashchange", this.refresh.bind(this), false);
}
route(path, callback) {
this.routes[path] = callback || function () {};
}
refresh() {
this.currentUrl = location.hash.slice(1) || "/";
this.routes[this.currentUrl]?.();
}
render(html) {
this.container.innerHTML = html;
}
push(path) {
location.hash = path;
}
}
const router = new HashRouter();
router.route("/", () => {
router.render("<h1>首页</h1>");
});
router.route("/home", () => {
router.render("<h1>首页</h1>");
});
router.route("/about", () => {
router.render("<h1>关于</h1>");
});
router.route("/contact", () => {
router.render("<h1>联系</h1>");
});
</script>
</body>
</html>
三、MemoryRouter
模式, 将路由状态保存在内存中, 不与浏览器地址栏同步, 通常用于测试或非浏览器环境下(如 React Native
)。内部维护一个内存中的 history
栈, 通过编程方式管理路由状态(push
、pop
、replace
等), 不会触发浏览器 URL
的变化, 完全依赖于应用自身来控制路由变更。
四、StaticRouter
模式, 主要用于服务端渲染(SSR
), 不具备浏览器路由功能, 只负责渲染与当前 URL
匹配的组件。接收一个 location
属性(通常来自服务端的请求路径)和一个 context
对象。根据传入的 location
渲染匹配的路由组件,不监听路由变化。适合在服务端渲染时确定当前页面内容,并将渲染结果传递给客户端进行注水(hydrate
)。