跳到主要内容

前端路由库设计原理

实现前端路由,离不开浏览器提供的 History API、Location API 这些 API

前端路由的实现,包括两种模式

  • Histrory模式
  • Hash模式

History 的模式 : 依赖了一个关键的属性window.history,该属性可用来获取用于操作浏览器历史记录的 History 对象

  • History 的路由模式需要依赖 HTML5 History API(IE10 以上),以及服务器的配置来支持
    • 所以不少的开发者会使用 Hash 模式来管理 Web 应用的路由

Hash 模式 : Hash 模式使用的是从井号(#)开始的 URL(锚)片段,主要依赖 Location 对象的 hash 属性

前端hash路由和history路由原理​

hash路由原理:​

  • hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中 (Hash路由地址URL不会对应服务器上资源),对后端完全没有影响,因此改变 hash 不会重新加载页面
  • hash 是通过监听浏览器 hashchange 事件变化,通过改变 location.hash 改变页面路由,当井号 # 后面的路径发生变化时,浏览器并不会重新发起请求,而是会触发hashchange 事件

history路由原理:​

  • history路由的话,利用了 HTML5 History 的API 中新增的 pushState() 和 replaceState() 方法,并不会刷新路由。
    • 提供了对历史记录进行修改的功能

history,hash区别​

  • 1、hash只需要前端配置路由表, 不需要服务端支持
  • 2、hash的url地址不会请求到后端,而history模式下,前端的URL会去请求服务器的,如果服务器中没有相应的响应,否则会报404错误
  • 2、hash模式下,只有#符号之前的内容才会包含在请求中被发送到服务端,也就是说服务器无法识别url中的#后面的部分
  • 3、hash兼容性好,hash 能兼容到IE8, history 只能兼容到 IE10
  • 4、history模式如果服务端没有配置,会出现404,而hash模式下,不会出现404错误
    • history模式下,前端的url必须和服务端发起的请求的url一致,如 http://www.test.com/user/id。如果服务端缺少对 /user/id 的路由处理,将返回 404 错误

为什么推荐使用 hash 模式?​

  • 兼容性好

hash的特点​

  • hash变化会触发网页跳转,就是浏览器的前进和后退
  • hash变化不会刷新页面,用户体验好

对于history路由而言,你觉得在服务端是如何做路由分发的​

  • history模式下,前端的URL必须和向发送请求后台URL保持一致,否则会报404错误

  • 不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。

所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面

  • 给个警告,因为这么做以后,你的服务器就不再返回 404 错误页面,因为对于所有路径都会返回index.html` 文件。为了避免这种情况,你应该在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面。

nginx服务端配置​

1、首先,需要在Nginx配置文件中定义一个location块,将所有请求都重定向到首页。这样可以确保所有请求都能被正确处理

  • 其中,try_files指令可以按顺序尝试查找文件。$uri表示请求的URL路径,$uri/表示请求的URL路径加上斜杠,/index.html表示首页。如果前面两个文件都不存在,就会返回首页
json
location / {
try_files $uri $uri/ /index.html;
}

2、其次,需要在Nginx配置文件中定义一个error_page块,将所有404错误重定向到首页。这样可以确保所有不存在的路由也能被正确处理

json
error_page 404 /index.html;

3、最后,如果你使用HTTPS,需要在Nginx配置文件中添加一个代理,将所有请求重定向到HTTPS。

  • 这样,所有请求都会被重定向到HTTPS。
json
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}

完整配置如下

json
server {
// 设置了监听80端口,并将所有请求都重定向到HTTPS。
listen 80;
server_name example.com;
// 1、第一部分是重定向到HTTPS:
return 301 https://$host$request_uri;

location / {
// 2、将所有请求都重定向到首页,使得所有路由都能被正确处理。
try_files $uri $uri/ /index.html;
}
// 3、重定向404错误到首页
error_page 404 /index.html;
}

js实现hash路由​

  • 【监听hash变化】window.onhashchange事件,当hash变化时,可以拿到新旧的hash地址值
  • 通过DOMContentLoaded页面加载的时候,获取hash
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button id="btn1">跳转 修改hash值</button>
</body>
<script>
// hash变化 1、js修改url 2、手动修改url的hash 3、浏览器前进和后退
window.onhashchange = (event) => {
console.log('event', event);
console.log('old url', event.oldURL);
console.log('new url', event.newURL);
};

// 页面初次加载,获取hash
document.addEventListener('DOMContentLoaded', () => {
console.log('hash:', location.hash);
});

// JS修改URL
document.getElementById('btn1').addEventListener('click', () => {
location.href = '#/user';
});
</script>
</html>

js实现history路由​

  • history.pushState、 replaceState
  • 【监听浏览器的前进后退】 window.onpopstate
  • 需要后端支持, 将所有请求都重定向到首页 index.html
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button id="btn1">跳转 修改hash值</button>
</body>
<script>
// 页面初次加载,获取hash
document.addEventListener('DOMContentLoaded', () => {
console.log('hash:', location.pathname);
});

// JS修改URL
document.getElementById('btn1').addEventListener('click', () => {
const state = { name: 'page1' }; // 切换page1路由
history.pushState(state, '', 'page1');
});

// 监听浏览器的前进后退
window.onpopstate = (event) => {
console.log(event.state);
console.log(location.pathname);
};
</script>
</html>
</html>

前端路由的优势​

  • 状态保留,可以通过url的变化获取到当前用户的状态,下次渲染的时候,就可以基于url来决定初始化渲染的组件
  • url变化不触发后端路由,体验更好同时能达到更新页面内容的效果