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
的應用不單單侷限與此。除此以外,它們還能夠應用於輪播效果、分頁、路由等場景。掌握了其使用,結合一些封裝,就能用於比較完整的系統中。