前言
目前前端开发最流行的莫过于SPA单页面架构,前端开发的三驾马车react、vue和angluarJS均是基于此模型来运行的。单页面指的是只有一个主页面,通过动态的替换DOM内容并修改URL地址,来模拟多页面的效果,切换页面的功能直接由前台脚本完成。SPA能够模拟多页面应用的效果,归功于前端路由机制。它有两种实现方式
1. HashChange
基本原理
更改URL的hash值(锚点),然后触发window对象的hashChange事件,通过代码触发对应页面的DOM改变,就可以实现基本的路由了
实例
// HTML
<a href="#angluarJS">angluarJs</a>
<a href="#react">react</a>
<a href="#vue">vue</a>
<div id="content"></div>
// JS
location.hash = 'angluarJs';
let c = document.querySelector('#content');
window.onhashchange = function (opt) {
c.innerHTML = '当前页面是' + location.hash;
}
2. History API
在HTML4中,已经支持window.history对象来控制页面历史记录跳转,常用方法包括:
- history.forward(); 相当于浏览器的前进
- history.back(); 相当于浏览器的后退
- history.go(n); 在历史记录中跳转n步骤,n=0时刷新页面,n=1时后退一页
在HTML5中,window.history再次扩展,新增API有:
- history.pushState(); 向历史记录中追加一条记录
- history.replaceState(); 替换当前页在历史记录中的信息
- history.state; 一个属性,当前页的state信息
- window.onpopstate(); 一个事件,当点击浏览器前进后退的时候触发
基本原理
浏览器访问一个页面的时候,当前地址的信息会被压入历史栈,当调用history.pushState()方法向历史栈中压入一个新的state后,历史栈顶部的指针就指向了这个新的state,就相当于假装已经修改了URL地址并进行跳转,当点击浏览器的前进后退或者JS触发前进后退时,触发window.onpopstate事件。
实例
// HTML
<h1>前端三驾马车</h1>
<ul id="list">
<li>angularJs</li>
<li>react</li>
<li>vue</li>
</ul>
<div id="content"></div>
// JS
document.querySelector('#list').addEventListener('click',function (e) {
if(e.target.tagName === 'LI') {
let content = e.target.innerHTML;
let newState = {
url: location.origin + '/' + content,
title: document.title,
state: content
};
// (data,title,url)
window.history.pushState(newState,'','/'+content);
urlChange(newState);
}
},false);
// 浏览器前进后退的时候触发
window.onpopstate = function (e) {
// pushState或repalceState方法传入的data参数
urlChange(e.state);
};
let c = document.querySelector('#content');
function urlChange(state) {
if(state) {
c.innerHTML = '当前页是' + state.state;
}else {
c.innerHTML = '当前页是angularJs';
}
}
两种方式比较
| 对比 | hash路由 | History API路由 |
|---|---|---|
| 命名限制 | 通常只能在一个document下进行改变 |
URL地址可以自定义,只要同一个域名下就可以 |
| URL地址变更 | 会改变 | 可以改变,也可以不改变 |
| 状态保存 | 无内置方法 | 将页面压入信息栈时可以附带自定义的信息 |
| 实用性 | 可直接使用 | 需要服务器修改代码配合实现 |
| 兼容性 | IE8以上 | IE10以上 |
应用
我封装了一个插件,可实现简单的前端路由,支持hash和history模式,代码在这里,使用如下:
hash模式
// HTML
<p>前端三驾马车</p>
<ul>
<li><a ym-link="#angularJs" >angular</a></li>
<li><a ym-link="#vue">vue</a></li>
<li><a ym-link="#react">react</a></li>
</ul>
<div id="ymapp">这里是模板容器</div>
// JS
$YMRouter.init({
mode: 'hash',
list: {
'#angularJs': 'this is the page of angularJs',
'#vue': 'this is the page of vue',
'#react': 'this is the page of react'
}
});
history模式
// HTML
<p>前端三驾马车</p>
<ul>
li><a ym-link="/angularJs" >angular</a></li>
<li><a ym-link="/vue">vue</a></li>
<li><a ym-link="/react">react</a></li>
</ul>
<div id="ymapp">这里是模板容器</div>
// JS
$YMRouter.init({
mode: 'hash',
list: {
'/angularJs': 'this is the page of angularJs',
'/vue': 'this is the page of vue',
'/react': 'this is the page of react'
}
});
问题
- 服务器端必须做路由处理,无论前端访问哪个页面,都返回首页,不然刷新页面会出现404
- history模式也可以不改变URL,但是本插件未做此功能