跳到主要内容

认识

一、认识


React-Router

二、问题


2.1 说说 React Router 有几种模式?实现原理?

Router Router 有两种模式, BrowserRouterHashRouterMemoryRouter 模式、StaticRouter 模式。

一、BrowserRouter 模式, 利用 HTML5History APIpushState/replaceState)来实现路由跳转, URL 看起来更干净,例如 /home/about。内部借助 history 库, 监听 popstate 事件来响应浏览器的前进后退操作。当调用 pushreplace 方法时, 通过 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 事件, 当 URLhash 值发生变化时,触发路由更, 内部同样维护一个虚拟的路由状态, 根据 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 栈, 通过编程方式管理路由状态(pushpopreplace 等), 不会触发浏览器 URL 的变化, 完全依赖于应用自身来控制路由变更。

四、StaticRouter 模式, 主要用于服务端渲染(SSR), 不具备浏览器路由功能, 只负责渲染与当前 URL 匹配的组件。接收一个 location 属性(通常来自服务端的请求路径)和一个 context 对象。根据传入的 location 渲染匹配的路由组件,不监听路由变化。适合在服务端渲染时确定当前页面内容,并将渲染结果传递给客户端进行注水(hydrate)。