最近公司的web app項目,使得我有幸一直接觸和學習jQuery Mobile。這確實是一個很不錯的移動開發庫,有助於擅長web開發的工程師,快速入門並構建自己的移動應用。但是在前兩天,我碰到了一個問題,使我查了很多資料都沒有找到很好的解決方案,最終只能逼着我讀jQuery Mobile的源碼,再寫了個擴展,才得以解決。下面請讓我娓娓道來。
問題描述
假設在項目中,有三個頁面,分別是main.html、test1.html、test2.html(后面分別簡稱main、test1、test2),其中main頁面是包含一個轉向到test1頁面的鏈接(即a標簽),test1中有一個屬性為data-rel=”back”的鏈接和一個轉向到test2的鏈接,test2只有一個屬性為data-rel=”back”的鏈接。main轉向到test1后,點擊back鏈接返回main(相當於點擊瀏覽器的返回按鈕),無需重新發送get請求;但是當test1轉向到test2,在test2頁面點擊back鏈接想返回test1時,會重新發送一個get請求。這樣導致的問題就是:test1做的所有操作,在test2返回后,都會失效。比如A是一個分頁的列表頁面,翻到第二頁后轉向到B,那么當返回A后,就無法地位到第二頁。
原因分析
我首先用firebug看了一下html的結構,發現jQuery Mobile會把main和test1加入到頁面結構中去,當從test1轉向到test2后,test1會被自動刪除,這樣dom樹中,只包含了main和test2,所以在test2返回test1就會在發送一個get請求。那么是不是意味着,只要能把歷史頁面緩存到dom中(就像main和test1一樣),就可以解決這個問題。
解決問題
經過一番查找,在jQuery Mobile官網看到一段《Caching pages in the DOM》的描述:
從這段引文中應該可以看到,這三種方法都可以緩存頁面到dom中,於是我就使用了第二種方法,即在page的div上增加了data-dom-cache=”true”屬性,但是卻出現了以下兩個問題:
1、如下圖所示,當我的訪問路徑是main->test1->test2->test1(test2是history.back()返回的)時,用firebug可以看到,test2仍然存在於dom中,這樣的結果就如紅色部分描述的:DOM會變得很大,結果會使頁面變慢和一些設備上的內存錯誤。
2、當我存在這樣一個頁面,它通過不同的參數顯示不同的內容,並且頁面上,有一段js腳本,會對頁面上的元素做些處理,而我們常用的方式就是用id來獲得目標元素,由於我們是用了cache緩存page,就會導致js事件或者操作混亂。比如在這里我增加了一個test1_1頁面,它的內容幾乎和test1一樣,他們都有相同id的div和相同事件處理的button,這個事件做的事情就是往這個div中增加內容,當訪問路徑為main->test1->test1_1,在test1_1上點擊按鈕,會發現好像沒有觸發這個事件,其實它已經觸發了,只是內容增加到test1中的div中了,分別入下圖
所以對於目前大多數應用,這個方案是不可取的,除非自己管理dom中頁面的生命周期。
優化方案
通過上面的實驗,我也知道了需要滿足我的需求,就只能自己管理dom中頁面的生命周期了。那么就涉及到一個問題:頁面什么時候過期(即從dom中刪除)呢?根據我的需求,當從test2返回到test1時,就應該從dom中刪除test2,同理從test1返回main時,從dom中刪除test1。如果再次從main導航到test1,就得發起一個get請求,我認為這是合理的,因為用戶不會認為點擊鏈接到新頁面還需要緩存。所以我應該在頁面顯示前或者顯示后,刪除它之后的history,於是我就在pagebeforeshow、pageshow的時候做了刪除操作,即如下腳本(插件形式):
(function($, undefined) {
$.fn.subpage = function(options) {
$(document).bind(
"pagebeforeshow",
function() {
var forword = $.mobile.urlHistory.getNext();
if (forword) {
var dataUrl = forword.url;
var forwordPage=$.mobile.pageContainer
.children(":jqmData(url='" + dataUrl + "')");
if(forwordPage){
forwordPage.remove();
}
}
$.mobile.urlHistory.clearForward();
});
};
$(document).bind("pagecreate create", function(e) {
$(":jqmData(role='page')", e.target).subpage();
});
})(jQuery);
結果事與願違,在頁面返回時,出現了js腳本錯誤,如下圖:
那么是什么原因呢?不在這個事件里做處理,那在哪里處理呢?於是我仔細研讀了一下jQuery Mobile源碼,發現了下面一段:
transitionPages( toPage, fromPage, settings.transition, settings.reverse )
.done(function() {
removeActiveLinkClass();
//if there's a duplicateCachedPage, remove it from the DOM now that it's hidden
if ( settings.duplicateCachedPage ) {
settings.duplicateCachedPage.remove();
}
//remove initial build class (only present on first pageshow)
$html.removeClass( "ui-mobile-rendering" );
releasePageTransitionLock();
// Let listeners know we're all done changing the current page.
mpc.trigger( "pagechange", triggerData );
});
頁面在切換完后,會觸發pagechange事件,於是我把pagebeforeshow改成了pagechange,一切都按預期運行,並且沒有錯誤,終於大功告成了。
總結
在使用該插件時,請注意以下幾點:
1、必須在引用該腳本之前,引用jquery和jquery mobile腳本文件;
2、必須在page上增加data-dom-cache="true"。




