原生開發(fā)移動web單頁面(step by step)8——History對象

增加了頁面切換效果后,再配合手勢以及按鍵,讓單頁面app更像一個原生app浮创, 然而卻引來了一個新的問題, 通過瀏覽器自帶的前進后退無法有選擇的采取動畫效果砌函。因為在popstate的時候斩披,都是采用一種動畫。如果能夠在popstate事件中能夠知道當前頁面以及要更換的頁面的位置讹俊,采取相應的動畫的垦沉,那就可以解決這個問題。由于瀏覽器的history的棧無法讀取仍劈, 我們就創(chuàng)建一個History對象厕倍, 執(zhí)行的過程與瀏覽器的原生行為一致, 通過取當前頁面在創(chuàng)建的History的位置贩疙, 以及它的前后的頁面與要切換的頁面進行比較位置關系讹弯。
通過第六篇已經(jīng)了解瀏覽器原生的history棧的原理况既。這里創(chuàng)建一個History對象,構造函數(shù)代碼如下

function History() {
    this.history = [];
    this.index = null;
}

history的數(shù)組代表瀏覽器中的頁面的url组民, index表示當前頁面的url的索引值棒仍。
對應的pushState方法如下

pushState: function (str) {
    if (this.index !== null) {
        var len = this.history.length;
        var nextIndex = this.index + 1;
        this.history.splice(nextIndex, this.len - nextIndex, str);
        this.index = nextIndex;
    }
    else {
        this.history.push(str);
        this.index = 0;
    }

    return str;
},

replaceState的方法如下

replaceState: function (str) {
    if (this.index) {
        this.history.splice(this.index, 1, str);
    }
    else {
        this.history.push(str);
        this.index = 0;
    }
    return str;
},

這兩個方法完全照瀏覽器的原理而創(chuàng)建的
對應的還有back以及forward, 在單頁面中邪乍, 原生瀏覽器history的go方法不常用降狠,因此不實現(xiàn), back方法和forward方法代碼如下:

back: function () {
    if (this.index === null) return "";
    return this.history[this.index === 0 ? 0 : --this.index];
},
forward: function () {
    if (this.index === null) return "";
    var len = this.history.length;
    return this.history[this.index === len - 1 ? len - 1 : ++this.index];
},

返回history索引值的url字符串

接著實現(xiàn)獲取當前索引值前后位置的url值庇楞,代碼如下:

getSurround: function () {
    var len = this.history.length;
    if (this.index === null) {
        return {
            next: "",
            prev: ""
        };
    }
    else if (this.history.length === 1) {
        return {
            next: "",
            prev: ""
        };
    }
    else if (this.index === 0) {
        return {
            next: this.history[1],
            prev: ""
        };
    }
    else if (this.index === len - 1) {
        return {
            next: "",
            prev: this.history[len - 2]
        }
    }
    else {
        return {
            next: this.history[this.index + 1],
            prev: this.history[this.index - 1]
        }
    }
}

這里的情況比較多榜配,不過思路很簡單, 接著修改App對象吕晌,將History對象組合在App對象中蛋褥,App對象的構造函數(shù)加入一個history,放置History對象

function App(options) {
    options = options || {};
    App.extend(options, {
        appClass: "app",
        changeClass: "app-change",
        backClass: "app-back",
        changeState: "change-state",
        pageInReverse: "page-in-reverse",
        pageOutReverse: "page-out-reverse",
        pageIn: "page-in",
        pageOut: "page-out"
    });
    this.options = options;
    this.currentPage = null;
    this.staticPage = null;
    this.pageContainer = null;
    this.routeObj = {};
    this.history = new History();
}

然后修改_attachHistory方法睛驳,將對應的方法在自定義的history對象中補上烙心,代碼如下:

_attachHistory: function (page, isBack) {
    var newUrl =  page.url;

    if (isBack) {
        history.replaceState({data: newUrl}, "", newUrl);
        this.history.replaceState(newUrl);
    }
    else {
        history.pushState({data: newUrl}, "", newUrl);
        this.history.pushState(newUrl);
    }
},

接著修改initialize方法中的popstate事件,因為這里不僅有history操作乏沸,還要分別獲取當前頁面的前后頁面的url淫茵,代碼如下:

window.addEventListener("popstate", function (ev) {
    if (ev.state && ev.state.data) {
        var url = ev.state.data;
        var page = that.routeObj[url];
        var urlObj = that.history.getSurround();
        if (urlObj.prev == url) {
            that.history.back();
            that.isRenderBack = true;
        }
        else if (urlObj.next === url) {
            that.history.forward();
            that.isRenderBack = false;
        }
        
        that._renderPage(page);
    }
}, false);

首先獲取當前頁面的前后的url, 判斷下一個即將渲染的頁面蹬跃,如果是后退操作匙瘪, 自定義的history執(zhí)行back方法,反之執(zhí)行前進方法蝶缀, 然后設置不同的動畫方式丹喻。
當然這里有個特殊情況, 就是如果當前頁面的前后頁面的url都相同, 然而這個情況在正常情況下可以完全被避免的翁都。 假如在a頁面碍论,pushstate到b頁面,然后在pushstate到a頁面柄慰,接著執(zhí)行history.back()操作鳍悠。這一系列的操作方式是可以直接用a頁面, pushstate到b頁面坐搔,在執(zhí)行history.back(), 接著執(zhí)行history.forward()來代替贼涩。

除了前后切換的效果之外,還有一種就是直接替換完全不變薯蝎,相當于第二章那種的遥倦,html完全替換, 因此引入了renderInstance方法,因此為了修改代碼如下:

renderBack: function (page, isBack) {
    this.isRenderBack = true;
    this._render(page, isBack);
},
render: function (page, isBack) {
    this._render(page, isBack);
},
renderInstance: function (page, isBack) {
    this._render(page, isBack, true);
},
_render: function (page, isBack, instance) {
    if (typeof page === "string") page = this.routeObj[page];
    if (page === this.currentPage) return;
    this.routeObj[page.url] = page;
    this._attachHistory(page, isBack);
    this._renderPage(page, instance);
    this.pageContainer.scrollTop = 0;
},
_renderPage: function (page, isInstance) {
    if (this.currentPage) this.currentPage._dispose();
    this.currentPage = page;
    page.app = this;
    var that = this;

    document.title = page.title;
    
    page.render(function (html) {
        if (isInstance) {
            var changeDom = that.changeDom;
            changeDom.innerHTML = html;
            page._initialize(changeDom);
        }
        else {
            var backDom = that.backDom;
            backDom.innerHTML = html;
            that._replaceDom();
            page._initialize(backDom);
        }
    });
},

原理非常的的簡單袒哥,通過一個布爾值來選擇那種切換形式缩筛。
這里還可以通過增加方法,或者在初始化中傳入不同的動畫類來切換動畫類型堡称。讓動畫變得非常的容易瞎抛。

這一篇就是為多頁面切換的最后一篇, 做到了這篇后却紧,做到了單頁面的基礎桐臊。頁面切換的動態(tài)效果是單頁面和多頁面的最大區(qū)別,多頁面是無法做到的晓殊。雖然這里的代碼非常簡單断凶,App對象, History對象和Page對象加起來才500行代碼有余巫俺, 卻讓單調(diào)的多頁面改成交互優(yōu)雅的單頁面认烁, 這里雖然只有4個簡單的頁面, 但是這一套邏輯可以為轉(zhuǎn)化任意多個頁面的項目介汹,把它轉(zhuǎn)化為你想要的單頁面却嗡。 現(xiàn)在正是你實踐的時候,選一個自己做過的多頁面嘹承,或者隨便想一個自己想做的窗价, 可以按照1-8篇的的順序,把它們改造為單頁面叹卷。改造的效果會讓你得到真正的收獲撼港。

總結:單頁面的靈活性增加了開發(fā)的無限玩法, 不變的是css豪娜, html和js基礎, 不用多增加任何的其它的知識就能做一個優(yōu)質(zhì)的單頁面哟楷。如果你已經(jīng)通過上面的教程改編過了一個單頁面瘤载, 你覺得你自己可以把它引入項目中了, 事實告訴你卖擅, 離真正要做成一個項目鸣奔, 現(xiàn)在才剛剛開始,后面還有很多坑需要填補惩阶。在多頁面開發(fā)時挎狸,你不用關心一個頁面切換到另外一個頁面后, 之前的頁面怎么辦断楷, 然而單頁面卻要異常關心锨匆,你需要把之前頁面的無關數(shù)據(jù)(包括組件)給清理了,防止垃圾累計導致頁面的卡頓以及移動端電量的消耗冬筒, 另外就是異步的操作恐锣, 單頁面頻繁使用異步的操作茅主, 比如ajax與indexdb操作, 一個微小的異步操作就會帶來整個應用的崩潰土榴。 因此這里推薦每個Page頁面都應該是單獨的個體诀姚,它與其它頁面沒有任何關系,它只能通過App對象來選擇渲染與否玷禽。

后續(xù)更新:之前的篇章都是靜態(tài)的頁面赫段,接下來就要轉(zhuǎn)化為動態(tài)的,通過ajax與后端交互數(shù)據(jù)矢赁,然后更新前端顯示的信息糯笙。在這之前, 需要給之前創(chuàng)建的服務器加上POST應答的功能坯台。

請用移動設備打開該案例
案例鏈接


原生開發(fā)移動web單頁面(step by step)1——傳統(tǒng)頁面的開發(fā)
原生開發(fā)移動web單頁面(step by step)2——Page對象
原生開發(fā)移動web單頁面(step by step)3——App對象
原生開發(fā)移動web單頁面(step by step)4——tap事件與slide事件
原生開發(fā)移動web單頁面(step by step)5——nodejs服務器的搭建
原生開發(fā)移動web單頁面(step by step)6——history api應用
原生開發(fā)移動web單頁面(step by step)7——頁面切換動畫

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末炬丸,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蜒蕾,更是在濱河造成了極大的恐慌稠炬,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咪啡,死亡現(xiàn)場離奇詭異首启,居然都是意外死亡,警方通過查閱死者的電腦和手機撤摸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門毅桃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人准夷,你說我怎么就攤上這事钥飞。” “怎么了衫嵌?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵读宙,是天一觀的道長。 經(jīng)常有香客問我楔绞,道長结闸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任酒朵,我火速辦了婚禮桦锄,結果婚禮上,老公的妹妹穿的比我還像新娘蔫耽。我一直安慰自己结耀,他們只是感情好,可當我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著饼记,像睡著了一般香伴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上具则,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天即纲,我揣著相機與錄音,去河邊找鬼博肋。 笑死低斋,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的匪凡。 我是一名探鬼主播膊畴,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼病游!你這毒婦竟也來了唇跨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤衬衬,失蹤者是張志新(化名)和其女友劉穎买猖,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滋尉,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡玉控,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了狮惜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片高诺。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖碾篡,靈堂內(nèi)的尸體忽然破棺而出虱而,到底是詐尸還是另有隱情,我是刑警寧澤开泽,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布牡拇,位于F島的核電站,受9級特大地震影響眼姐,放射性物質(zhì)發(fā)生泄漏诅迷。R本人自食惡果不足惜佩番,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一众旗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧趟畏,春花似錦贡歧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽律想。三九已至,卻和暖如春绍弟,著一層夾襖步出監(jiān)牢的瞬間技即,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工樟遣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留而叼,地道東北人。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓豹悬,卻偏偏與公主長得像葵陵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子瞻佛,可洞房花燭夜當晚...
    茶點故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內(nèi)容