Ajax的流行和前端MVVM框架的快速发展给Web开发带来了极大的便利,也让Web应用体验愈来愈好,近些年单页应用也随之流行起来。Ajax的应用可让网页实现无刷新更新数据,但其也会形成浏览器没法前进和后退(浏览器和手机的物理返回键)的问题。早期这个问题一般是借助iframe
来解决。得益于HTML5,目前有了一些诸如用pjax
(ajax+pushState)的解决方案。本文要讲解的是如何用锚点
和history API
来实现单页面应用页面之间的切换。javascript
讲解以前先说说需求,效果以下图:css
需求点:html
点击手机信息所在项,从右边划出手机编辑页面前端
输入手机号码和验证码保存后返回基本信息页面vue
浏览器点击前进后退能够在两个页面以前切换java
支持手机的物理返回键(等价于history.back()
)git
页面须要局部刷新,保持url一致github
整个问题的难点应该在于前进和后退的处理,其它的需求如下仅做简单说明。web
页面的html主要结构以下:ajax
<div class="main" id="main">
<div v-show="!edit" v-cloak>
<ul class="list">
<li @click="toggle(1)">
<label for="">手机</label>
<div class="list-info">{{showPhone}}</div>
</li>
<!-- 其余的基本信息 -->
</ul>
<div class="btn">退出登陆</div>
</div>
<div class="translate" :class="{open: edit}">
<div class="input-wrap">
<input type="text" class="input" v-model="phone" placeholder="请输入手机">
</div>
<div class="input-wrap">
<input type="text" class="input" placeholder="请输入收到的验证码" v-model="code">
<a class="input-btn">获取验证码</a>
</div>
<div class="text-right">收不到?试试语音验证码</div>
<div class="btn btn-submit" @click="toggle(0)" :class="{disabled: !phone || !code}">提交</div>
</div>
</div>复制代码
以上将基本信息页面和编辑页面分别包括在两个同级的div
标签内。
部分主要的CSS样式以下(未做前缀处理):
.translate {
position: fixed;
top: 0;
bottom: 0;
width: 100%;
background: #efeff4;
-webkit-transform: translateX(100%);
-webkit-transition: transform .4s;
}
.translate.open {
-webkit-transform: translateX(0);
}复制代码
默认状况下,编辑页面平移至右侧不可见的范围内,当打开编辑页面时,添加open
类名,使其平移至可见范围内。
接下来,用vue来实现页面的基本的逻辑。基本的代码以下:
var vm = new Vue({
el: '#main',
data: {
edit: false,
phone: '',
code: ''
},
methods: {
toggle: function(value){
//这里处理逻辑
}
},
computed: {
showPhone: function(){
return (this.phone || '13688888888').replace(/(\d{3})(\d{4})(\d{4})/, '$1****$3')
}
}
});复制代码
如下经过两种方法来完成以上的需求
HTML5位history提供了如下API:
window.history.pushState(stateObj, title, url)
: 向当前浏览记录栈中添加一条新的历史记录,添加后页面不会从新加载,参数分别表明:
stateObj
:描述新记录的对象或字符串,方便之后使用,使用history.state
能够获取,如{id: 0, name: 'home'}
title
: 一个字符串,表明页面的标题,目前多数浏览器基本会忽略该参数
url
: 一个字符串,新页面的url地址
window.history.replaceState(stateObj, title, url)
: 与pushState
一致,不一样的是不会往历史栈添加新记录,而是替换当前的浏览记录,经常使用于落地页。
popstate(e)事件
当用户点击浏览器的前进
和后退
按钮时,就会触发该事件,事件接收一个参数,指向当前历史记录。前面设置的stateObj则是包含于此对象。e
打印出来格式以下:
有了以上的API,针对需求,实现的思路大体以下:
进入页面后使用replaceState
替换当前浏览记录,stateObj
值为{page: 'home'}
点击手机编辑后,使用pushState
往浏览记录记录添加新记录,stateObj
值为{page: 'edit'}
,同时为编辑页面添加open
类,使其进入可视范围
当进行前进和后退时,监听popstate
事件,并获取当前的history.state
,经过判断state.page
的值来实现页面的切换
最终处理的逻辑以下:
var vm = new Vue({
el: '#main',
data: {...},
methods: {
toggle: function(value){
this.edit = value;//切换页面
if(value){//若是切换到编辑页面,则添加新的浏览记录
history.pushState({page: 'edit'},'');
} else {//从编辑页面回到基本信息页面
history.back();
}
}
},
computed: {...}
});
history.replaceState({page:'home'},'' );//进入页面后替换当前浏览记录
window.addEventListener('popstate', function(e){//监听前进和后退
if (history.state) {
vm.edit = history.state.page == 'edit';//切换页面
}
})复制代码
执行结果以下:
这样子咱们就借助history API
实现了一个简单的单页面页面切换,有了这个基础就能够实现更复杂的应用,若是你还想在页面切换的过程当中改变url(这有利于SEO),则能够经过指定pushState
和replaceState
的url
参数来实现。
锚点通常用于页面内的快速定位,经过指定锚点,可使页面跳转至指定元素所在的位置。改变hash值具备不刷新页面的特色。使用锚点来操做浏览器的前进和后退主要用到如下两个:
location.hash
: 获取当前的锚点值,返回空字符串或者如#detail
格式的值
window.onhashchange
: HTML5新增的事件,用于监听地址的hash值的改变,接收一个回调函数做为参数,回到函数接收接收一个对象,对象的格式以下:
借助hash
实现咱们的需求的基本思路以下:
点击手机编辑后,改变hash
值为'#edit'
监听hash
值的变化,经过判断值来控制页面的切换(在这里改变vm.edit
的值)
所以,完整的代码将变为以下:
var vm = new Vue({
el: '#main',
data: {...},
methods: {
toggle: function(value){
location.hash = value ? '#edit' : '';
}
},
computed: {...}
}
});
window.addEventListener('hashchange', function(e){
vm.edit = location.hash == '#edit';
})复制代码
在这里例子中,这种方式看起的代码要比使用history API
的代码简洁
执行结果以下(注意浏览器的地址变化):
以上经过两种方案实现了单页面中页面以前的切换并可以操做浏览器的前进和后退。事实上,history API
和location.hash
的应用不单单局限与此。除此以外,它们还能够应用于轮播效果、分页、路由等场景。掌握了其使用,结合一些封装,就能用于比较完整的系统中。