
1. 為什么還值得花一小時了解jQuery Mobile如果你最近接手了一個移動端老項目大概率會在代碼里撞見滿屏的>!DOCTYPE html html head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1 titlejQuery Mobile 快速入門/title link relstylesheet hrefhttps://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css script srchttps://code.jquery.com/jquery-1.11.3.min.js/script script srchttps://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js/script /head body ... /body /html注意兩個細節(jié)。一是順序先引 jQuery 核心庫再引 jQuery Mobile順序反了頁面直接報錯二是版本jQuery Mobile 1.4.5 官方推薦搭配 jQuery 1.x 使用很多老項目用的是 1.10.x 或 1.11.x。如果你貪新用了 jQuery 3.x會發(fā)現(xiàn)大量 API 行為對不上這個問題我后面單獨講反正在快速入門階段就照 1.11.x 來。另一個重點是mobileinit事件。jQuery Mobile 在初始化流程里預留了配置鉤子但你必須在jquery.mobile.min.js加載之前綁定它所以正確的順序應該是jQuery → 綁定 mobileinit → jQuery Mobile。切忌把這個綁定放在 document ready 里那時框架早就跑完了。script srchttps://code.jquery.com/jquery-1.11.3.min.js/script script $(document).on(mobileinit, function() { $.mobile.defaultPageTransition slide; }); /script script srchttps://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js/script2.2 頁面標準骨架page、header、content、footer接下來是最核心的頁面結(jié)構(gòu)。一個 jQuery Mobile 頁面被規(guī)定為容器 三個區(qū)div>div>a hrefdetail.html查看詳情/a這里藏著兩個重要規(guī)則。第一被 AJAX 拉進來的外部頁面里非 page 容器外的頭部信息全部丟失比如新頁面的title不會更新、新頁面里的script基本不會按你預期執(zhí)行。第二如果你想讓某些鏈接強制走完整頁面跳轉(zhuǎn)——比如跳到一個不兼容 jQuery Mobile 的頁面或者需要刷新整站狀態(tài)——給鏈接加relexternal或>a href#about>$.mobile.pageContainer.pagecontainer(change, #about, { transition: slide, changeHash: true });1.4.x 時代統(tǒng)一用pagecontainer這個 widget 來做編程式導航以前版本里的$.mobile.changePage()雖然還能用但已經(jīng)屬于舊接口新寫的維護代碼建議用pagecontainer(change, ...)。3.4 頁面生命周期事件踩坑前的必修課多頁模式帶來一個老手都繞不開的問題事件該掛在哪。普通 jQuery 項目里你會很習慣地在$(document).ready()里初始化所有東西但在 jQuery Mobile 里同一個文檔里有很多頁面被切換、被加載document.ready只在首次加載時觸發(fā)一次后面 AJAX 進來的頁面根本不會再次觸發(fā)它。所以官方推薦的做法是把業(yè)務初始化代碼掛到頁面生命周期事件上。一套頁面從生成到消失經(jīng)歷的主要事件順序如下pagebeforecreate頁面 div 被識別但尚未創(chuàng)建任何 widgetpagecreate頁面里的組件正在進行初始化pageinit頁面初始化完成官方推薦在這里做自己的初始化pagebeforeshow頁面即將顯示pageshow頁面已經(jīng)完全顯示pagehide/pagebeforehide頁面被隱藏階段。最常用的兩個是pagecreate做一次性初始化和pageshow每次顯示都要刷新狀態(tài)。標準寫法是事件委托$(document).on(pagecreate, #home, function() { // 只在頁面創(chuàng)建時執(zhí)行一次 console.log(home 初始化); }); $(document).on(pageshow, #home, function() { // 每次切回首頁都會執(zhí)行適合刷新數(shù)據(jù) loadList(); });直接在具體 DOM 元素上綁pagecreate是無效的必須委托到 document 上原因很簡單元素可能是 AJAX 之后才進 DOM 的直接綁不上去。4. 高頻組件速覽從按鈕到表單4.1 按鈕一個>a href#page2>ul>ul>div>!-- 滑塊 -- div>div classui-grid-a div classui-block-a左列/div div classui-block-b右列/div /divui-grid-a是兩列ui-grid-b三列ui-grid-c四列對應的塊分別是ui-block-a、ui-block-b…… 柵格類名是固定的不會因為列數(shù)變化而改變字母起點。折疊面板適合做問與答、展開詳情代碼簡單div>a href#myPopup>a href#tips>$(document).on(swipeleft, .cards, function() { $(this).next().show(); });5.2 事件綁定的正確姿勢給元素綁定事件時務必用事件委托的方式把 handler 掛到 document 上而不是直接掛在目標元素上// 推薦 $(document).on(tap, #btn-submit, function() { submitOrder(); }); // 不推薦元素可能晚于綁定出現(xiàn) $(#btn-submit).on(tap, function() { submitOrder(); });原因和上一節(jié)說的一樣jQuery Mobile 的頁面 div 可能是 AJAX 加載后動態(tài)插入的直接綁定的代碼執(zhí)行時元素還不存在。事件委托利用事件冒泡不管元素什么時候出現(xiàn)只要事件冒泡到 document 都能被捕獲。這個習慣一旦養(yǎng)成能省掉后面一大半的為什么沒反應排查時間。5.3 tap 與 click 的差異和幽靈點擊移動端瀏覽器在觸摸屏上處理 click 事件時通常會等 300 毫秒左右確認你是不是要雙擊縮放這個延遲讓 click 手感遲鈍。jQuery Mobile 的 tap 事件通過監(jiān)聽 touchend 在手指離開瞬間觸發(fā)手感好很多。但它也引入了另一個經(jīng)典問題如果同時綁了 tap 和 click或者 tap 觸發(fā)后瀏覽器又在下層元素上補發(fā)了一個 click幽靈點擊會出現(xiàn)點了按鈕觸發(fā)兩次跳轉(zhuǎn)。老項目中遇到點一次頁面跳了兩下的 bug十有八九跟它有關(guān)。排查思路是看事件綁定里有沒有同時綁了 tap 和 click或者 tap 事件處理時是否把默認行為完全攔截了。必要時可以加一個延遲鎖$(document).on(tap, #btn-next, function() { if ($(this).data(locked)) return; $(this).data(locked, true); $.mobile.pageContainer.pagecontainer(change, #page2); setTimeout(function() { $(#btn-next).data(locked, false); }, 500); });這個鎖思路在雙事件、快速重復點擊的場景里都管用。5.4 區(qū)塊翻新讓動態(tài)內(nèi)容走一遍增強流程結(jié)合 5.2再補一個動態(tài)內(nèi)容的經(jīng)典操作。當你用 JS 往頁面里 append 了一段新的列表 HTML 時它會變成沒有樣式的裸列表因為框架加載時的增強流程早就跑完了。解決辦法是在新內(nèi)容插入后對容器執(zhí)行一次增強$(#some-list).html(newHtml); $(#some-list).enhanceWithin();enhanceWithin()是 1.4 以后推薦的寫法它會把容器內(nèi)部所有帶>div>.ui-icon-custom { background-image: url(icons/custom.png); background-size: 18px 18px; background-color: transparent; }組件上一寫>$(#amount).val(60).slider(refresh); $(#notify).prop(checked, true).flipswitch(refresh); $(#city).html(newOptions).selectmenu(refresh);也有一個通用兜底調(diào)用 refresh 前先確保元素已經(jīng)被增強過否則會報cannot call methods on slider prior to initialization之類的方法不存在錯誤。7.6 低端安卓的過渡動畫掉幀flip、pop 這類 3D 過渡動畫在低端 Android WebView 上經(jīng)常掉幀表現(xiàn)為頁面切換時白屏或卡頓。排查時先把頁面里同時運行的 CSS 動畫、圖片懶加載因素排除再考慮把全局過渡改成 none 或 fade$(document).on(mobileinit, function() { $.mobile.defaultPageTransition none; });fade 只做透明度過渡開銷小很多none 則最干凈配合項目本身的加載提示效果也不差。7.7 一個保命的啟動配置習慣最后說個維護老項目的習慣接手的第一個下午先不要急著改頁面把所有頁面的事件綁定清單、主題色板、jQuery 核心版本、是否使用 AJAX 導航這幾項記下來。我會在mobileinit里統(tǒng)一把全局切換動畫、頁面加載提示、按鈕點擊延遲這些參數(shù)配置好再開始動業(yè)務代碼。原因是這套框架的坑有很強的全局性一個配置能同時影響幾百個頁面先定底后改局部風險最小。我個人的做法是在這個啟動配置里順手把$.mobile.loader.prototype.options.textVisible這些細節(jié)也看一眼很多后期詭異的問題最后都回溯到某個全局配置被業(yè)務代碼改亂了。對 jQuery Mobile 這種強約定框架理解默認行為比急著寫特殊邏輯更重要把框架的脾氣摸透維護起來才會順。