前端路由库设计原理
实现前端路由,离不开浏览器提供的 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表示首页。如果前面两个文件都不存在,就会返回首页
location / {
try_files $uri $uri/ /index.html;
}
2、其次,需要在Nginx配置文件中定义一个error_page块,将所有404错误重定向到首页。这样可以确保所有不存在的路由也能被正确处理
error_page 404 /index.html;
3、最后,如果你使用HTTPS,需要在Nginx配置文件中添加一个代理,将所有请求重定向到HTTPS。
- 这样,所有请求都会被重定向到HTTPS。
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
完整配置如下
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
<!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
<!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变化不触发后端路由,体验更好同时能达到更新页面内容的效果