路由模式
Vue-router 支持 3 种模式:
- Hash 模式
- HTML5 模式
- Memory 模式
Hash 模式
Hash 是 URL 组成的一部分,例如:
https://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2#anchor
其 # 后面的部分就是 Hash 部分。早期 Hash 更多是被用作锚点:
<a href="target">go target</a>
......
<div id="target">i am target place</div>
在上面代码中,点击 <a> 链接,文档会滚动到 id.target 的 div 的位置。
Hash 另一个重要特性:Hash 的变化不会请求服务器
利用该特性可以实现不同 URL 映射不同的模块。
#a ---> A
#b ---> B
Hash 实现原理
监听 window 的
hashChange事件
,当 hash 改变后,映射出对应的模块。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./index.css" />
</head>
<body>
<nav>
<a href="#home">Home</a>
<a href="#about">About</a>
<a href="#contact">Contact</a>
</nav>
<div id="content"></div>
<script src="./index.js"></script>
</body>
</html>
document.addEventListener("DOMContentLoaded", () => {
const renderPage = (hash) => {
switch (hash) {
case "#home":
content.innerHTML = "<h1>Home</h1>";
break;
case "#about":
content.innerHTML = "<h1>About</h1>";
break;
case "#contact":
content.innerHTML = "<h1>Contact</h1>";
break;
default:
content.innerHTML = "<h1>404</h1>";
}
};
window.addEventListener("hashchange", () => {
renderPage(window.location.hash);
});
renderPage(window.location.hash || "#home");
});
vue-router 中使用 hash 模式
Vue-router 中如何使用 Hash 模式?
// router/index.js
const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL),
routes: [
// ...
],
});
import.meta.env.BASE_URL 是 Vite 里面提供的一个环境变量,默认是应用的根路径
- 开发环境,默认值是 '/'
- 生产环境,这个值可以在 vite.config.js 里面的 base 中来指定
HTML5/History 模式
HTML5 模式也被称之为 History 模式。该模式利用 HTML5 的 HistoryAPI 来管理浏览器的历史记录从而实现单页应用。
History API:
- history.pushState(state, title, url):将一个状态推入到历史堆栈里面
- history.replaceState(state, title, url):替换当前历史堆栈最上面的状态
- window.onpopstate:这是一个事件,当用户点击浏览器的前进或者后退按钮的时候,会触发该事件
History 实现原理
工作原理:
- 拦截链接点击事件
- 客户端路由器会拦截页面上的所有链接点击事件(通常是通过阻止链接的默认行为 event.preventDefault( ))
- 取而代之的是,路由器使用 history.pushState 或 history.replaceState 更新 URL。
- URL 变化处理:
- 当 URL 变化时,路由器会捕捉到这个变化。
- 路由器不会发出新的 HTTP 请求,而是根据新的 URL 查找预先定义好的路由规则,并加载相应的视图组件
举个例子,假设有一个单页应用,使用 history 模式,并且有以下路由规则:
- /home: 显示主页内容
- /about: 显示关于页面内容
当用户点击导航链接从 /home 切换到 /about 时,流程如下:
- 用户点击链接 <a href="/about">About</a>
- 路由器拦截点击事件,调用 event.preventDefault( ) 阻止浏览器的默认行为(即不发出 HTTP 请求)
- 路由器调用 history.pushState(null, '', '/about') 更新浏览器的地址栏 URL 为 /about
- 路由器检测到 URL 变化,查找路由规则,发现 /about 对应的视图组件
- 路由器加载并渲染 /about 视图组件,将其插入到页面的特定位置
整个过程中,浏览器地址栏的 URL 更新了,但没有发出新的 HTTP 请求,所有的视图更新都是在客户端完成的。
实战:使用 History 实现单页应用
拦截每一个
a
标签的跳转事件,阻止跳转,从用户写好的匹配规则中找到对应的模块进行渲染。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="./index.css" />
</head>
<body>
<nav>
<a href="/" data-link>Home</a>
<a href="/about" data-link>About</a>
<a href="/contact" data-link>Contact</a>
</nav>
<div id="content"></div>
<script src="./index.js"></script>
</body>
</html>
document.addEventListener("DOMContentLoaded", () => {
const content = document.getElementById("content");
//渲染函数
const renderPage = (path) => {
console.log(path, "path");
switch (path) {
case "/":
case "/index.html":
content.innerHTML = "<h1>Home</h1>";
break;
case "/about":
content.innerHTML = "<h1>About</h1>";
break;
case "/contact":
content.innerHTML = "<h1>Contact</h1>";
break;
default:
content.innerHTML = "Not Found";
}
};
//拦截a标签的跳转
document.querySelectorAll("a[data-link]").forEach((link) => {
link.addEventListener("click", (e) => {
//禁止a标签的默认跳转
e.preventDefault();
//拿到当前要跳转的路由
const path = e.target.getAttribute("href");
//推入到浏览器历史记录中
history.pushState(null, null, path);
//渲染对应的模块
renderPage(path);
});
});
//上来先匹配一次
renderPage(window.location.pathname || "/");
});
vue-router 中使用 history 模式
Vue-router 中如何使用 History 模式?
// router/index.js
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
// ...
],
});
History 存在的问题
一旦刷新,就会报 404 错误。
思考 🤔 为什么会这样?
答案:当你刷新的时候,是会请求服务器的。但是服务器并没有这个后端路由,这个仅仅是一个前端路由。
要解决这个问题,需要在服务器上面做一些配置。添加一个回退路由,如果 URL 不匹配任何的静态资源,回退到首页。
vue-router 官网提供了服务器的解决方案: (https://router.vuejs.org/zh/guide/essentials/history-mode.html#HTML5-模式)
Memory 模式
无论是 Hash 也好、History API 也好,本质上都是基于浏览器的特性来实现的。
而 Memory 模式一般用于非浏览器环境,例如 Node 或者 SSR. 因为是非浏览器环境,所以不会有 URL 交互也不会自动触发初始导航。
该模式用 createMemoryHistory( ) 创建,并且需要在调用 app.use(router) 之后手动 push 到初始导航。
import { createRouter, createMemoryHistory } from "vue-router";
const router = createRouter({
history: createMemoryHistory(),
routes: [
//...
],
});
-EOF-