一、前端路由
1.前端路由的由来
- 路由最早是出现在后端中的,在前端技术功能还不够丰富的时候,一开始是由后端主导根据路由渲染不同页面的: 前端通过url访问页面,会向后端发送请求,由后端匹配路由相关的页面和数据,组装成html文件返回给前端,并且前端每次切换页面都会重新刷新访问
- 前端路由是发展到SPA(即只有一个html页面)时出现在前端的,SPA出现后,前端可以自由控制组件的渲染,来模拟页面的跳转。用户点击切换页面时,浏览器监测到路由发生变化,去加载请求响应的js和css文件,并渲染对应的组件。
- 根据路由的不同展示方式以及不同的浏览器劫持方式,浏览器被分为了hash路由和history路由
2.hash路由
- hash路由的特点
- 路由是由location.hash实现,路由中带‘#’,井号后面就是location.hash的值
- 浏览器通过hashchange监测location.hash的变化去渲染相应的组件
- 改变hash值,浏览器不会重新加载页面,只是去渲染响应组件
- 当刷新页面时,hash不会传给服务器,hash值用于前端监测并加载响应路由,一切路由变化都在前端, 只有
#之前的内容才会作为URL发送给服务器,就算服务端没有对路由进行全覆盖也不会返回404 - 支持ie8
hash改变都会在浏览器访问的历史记录中增加一个记录,所以可以通过浏览器进行前进后退,如果想在hash模式下不保存记录,可以使用location.replace
- 简单实现一个hash路由
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>hash 路由</title>
</head>
<body>
<div id="container" >
<button onclick="window.location.hash = '#'">首页</button>
<button onclick="window.location.hash = '#about'">关于我们</button>
<button onclick="window.location.hash = '#user'">用户列表</button>
</div>
<div id="context"></div>
</body>
<script>
class BaseRouter {
constructor() {
this.routes = {};
this.refresh = this.refresh.bind(this);
window.addEventListener('load', this.refresh);
window.addEventListener('hashchange', this.refresh);
}
route(path, callback) {
this.routes[path] = callback || function() {}
}
refresh() {
const path = `/${window.location.hash.slice(1) || ''}`;
this.routes[path]();
}
}
const Route = new BaseRouter();
Route.route('/about', () => changeText("关于我们页面"));
Route.route('/user', () => changeText("用户列表页"));
Route.route('/', () => changeText("首页"));
function changeText(arg) {
document.getElementById('context').innerHTML = arg;
}
</script>
</html>
3.history路由
- history路由的特点
history是基于HTML5新增的pushState和replaceState两个API实现的,不带#,看起来更美观- 浏览器通过
popstate事件监听历史栈的改变该模式的路由 - pushState()方法或replaceState()不会触发popstate,只有浏览器的前进后退和History对象上的go,back,forward等才会触发popstate。
pushState设置的URL可以是任意的与当前URL同源的URL,而hash只能改变#后面的内容- 有go|back|repalce|push|forward一系列方法
- IE9及其以下版本浏览器不支持,IE10开始支持
- 简单实现一个history路由
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>H5 路由</title>
</head>
<body>
<div id="container">
<a href="./" >首页</a>
<a href="./about">关于我们</a>
<a href="./user">用户列表</a>
</div>
<div id="context"></div>
<script>
class BaseRouter {
constructor() {
this.routes = {};
this._bindPopstate();
this.init();
}
init(path) {
window.history.replaceState({path}, null, path);
const cb = this.routes[path];
if(cb) {
cb();
}
}
route(path, callback) {
this.routes[path] = callback || function() {}
}
go(path) {
window.history.pushState({path}, null, path);
const cb = this.routes[path];
if(cb) {
cb();
}
}
_bindPopstate() {
window.addEventListener('popstate', e => {
const path = e.state && e.state.path;
this.routes[path] && this.routes[path]();
})
}
}
const Route = new BaseRouter();
Route.route('./about', () => changeText("关于我们页面"));
Route.route('./user', () => changeText("用户列表页"));
Route.route('./', () => changeText("首页"));
function changeText(arg) {
document.getElementById('context').innerHTML = arg;
}
container.addEventListener('click' , e => {
if(e.target.tagName === 'A') {
e.preventDefault();
Route.go(e.target.getAttribute('href'))
}
})
</script>
</body>
</html>
二、Vue路由
Vue-Route是先实现history路由,然后再去兼容hash路由,当用IE8打开history路由时,会自动切换到hash
- 异步路由 包括 React.lazy 、 import() 就是⼀种对代码进⾏动态拆分的技术,我们⼀般 叫做 code splitting。 在需要的时候,才进行加载;
{
path: '/city',
name: 'City',
component: () => import('@/pages/city/City')
},
- 动态路由: 参数传值
{
path: '/detail/:id',
name: 'Detail',
component: Detail
}
- 路由守卫的触发流程
- 【组件】- 前⼀个组件 beforeRouteLeave
- 【全局】- router.beforeEach
- 【组件】-如果是路由的参数变化,触发 beforeRouteUpdate ;
- 【配置⽂件】⾥,下⼀个 beforeEnter
- 【组件】内部声明的 beforeRouteEnter
- 【全局】调⽤ beforeResolve
- 【全局】的 router.afterEach