提高行動成效的 HTML5 技巧

威斯利哈爾斯
Wesley Hales

簡介

在現今的行動網路環境中,點擊事件旋轉、頁面是否間斷斷續續,以及輕觸事件定期延遲等等,只是其中幾個例子而已。開發人員嘗試盡可能以原生方式近乎原生,但經常受到駭客入侵、重設和嚴謹架構的限制。

本文將探討建立行動 HTML5 網頁應用程式所需的最低需求。重點是排除在現今行動架構中不得隱藏的複雜層面。您將看到一個極簡風的方法 (使用核心 HTML5 API) 和基本基本知識,可協助您撰寫自己的架構,或參與目前使用中的架構。

硬體加速

一般來說,GPU 會處理詳細的 3D 模型或 CAD 圖表,但在這個案例中,我們希望使用原始繪圖 (例如 Div、背景、含有投射陰影的文字、圖片等) 流暢呈現透過 GPU 呈現的流暢效果。可惜的是,大部分前端開發人員都會將動畫程序擴及第三方架構,而不考慮其語意,但這些核心 CSS3 功能是否應該遮蓋?以下將說明為何這點很重要:

  1. 記憶體配置和運算負擔:如果您只是為了硬體加速而組合 DOM 中的每個元素,另一位執行程式碼的人員可能會讓您受傷並擊敗您。

  2. 耗電量:顯而易見,硬體啟動後會消耗電量。開發行動裝置專用的應用程式時,開發人員必須在編寫行動網頁應用程式時,將各種裝置限制納入考量。當瀏覽器製造商開始開放使用者存取更多裝置硬體及更多硬體時,這個需求會更加普遍。

  3. 衝突 — 將硬體加速功能套用至已加速的網頁部分時,我遇到異常行為。因此瞭解是否有重疊的加速功能非常重要

為了讓使用者互動體驗更順暢、與原生廣告相近,我們必須讓瀏覽器正常運作。在理想情況下,我們希望行動裝置 CPU 能設定初始動畫,然後讓 GPU 在動畫過程中只組合不同圖層。而轉譯 3d、scale3d 和 translationZ 都是透過獨立的圖層,為動畫元素提供專屬圖層,藉此讓裝置流暢地呈現所有內容。如要進一步瞭解加速合成和 WebKit 的運作方式,Ariya Hidayat 可以在他的網誌參閱許多實用資訊

頁面轉換

讓我們來看看開發行動版網站應用程式時,最常見的三種使用者互動方法:滑動、翻轉和旋轉效果。

如要查看這個程式碼的實際運作情形,請前往 http://slidfast.appspot.com/slide-flip-rotate.html (注意:本示範是專為行動裝置而設計,因此可以啟動模擬器、使用手機或平板電腦,或是將瀏覽器視窗的大小縮減至約 1024 像素以下)。

我們先來解析投影片、翻轉、旋轉轉場效果,以及轉換的加速方式。您會發現每個動畫只需要三到四行 CSS 和 JavaScript。

滑塊

滑動頁面轉換效果最好是三種最常見的轉換方法,就像是行動應用程式的原生風格。系統會叫用投影片轉場效果,將新的內容區域帶進檢視區。

針對滑動效果,首先我們宣告標記:

<div id="home-page" class="page">
  <h1>Home Page</h1>
</div>

<div id="products-page" class="page stage-right">
  <h1>Products Page</h1>
</div>

<div id="about-page" class="page stage-left">
  <h1>About Page</h1>
</div>

您可以看到我們將這個預備頁面概念 (由左或右至上)。基本上可能是任何方向,但最常見的情況是這樣。

現在只要加入幾行 CSS,就能加入動畫和硬體加速功能。我們在切換頁面 div 元素上的類別時,會發生實際動畫。

.page {
  position: absolute;
  width: 100%;
  height: 100%;
  /*activate the GPU for compositing each page */
  -webkit-transform: translate3d(0, 0, 0);
}

translate3d(0,0,0) 稱為「銀色條紋」方法。

當使用者按一下導覽元素時,我們會執行以下 JavaScript 來替換類別。沒有使用任何第三方架構,這是簡化的 JavaScript!;)

function getElement(id) {
  return document.getElementById(id);
}

function slideTo(id) {
  //1.) the page we are bringing into focus dictates how
  // the current page will exit. So let's see what classes
  // our incoming page is using. We know it will have stage[right|left|etc...]
  var classes = getElement(id).className.split(' ');

  //2.) decide if the incoming page is assigned to right or left
  // (-1 if no match)
  var stageType = classes.indexOf('stage-left');

  //3.) on initial page load focusPage is null, so we need
  // to set the default page which we're currently seeing.
  if (FOCUS_PAGE == null) {
    // use home page
    FOCUS_PAGE = getElement('home-page');
  }

  //4.) decide how this focused page should exit.
  if (stageType > 0) {
    FOCUS_PAGE.className = 'page transition stage-right';
  } else {
    FOCUS_PAGE.className = 'page transition stage-left';
  }

  //5. refresh/set the global variable
  FOCUS_PAGE = getElement(id);

  //6. Bring in the new page.
  FOCUS_PAGE.className = 'page transition stage-center';
}

stage-leftstage-right 會變成 stage-center,並強制頁面滑入中心檢視連接埠。我們完全仰賴 CSS3 進行繁重的工作。

.stage-left {
  left: -480px;
}

.stage-right {
  left: 480px;
}

.stage-center {
  top: 0;
  left: 0;
}

接著來看看處理行動裝置偵測和螢幕方向的 CSS。我們可以處理所有裝置和各種解析度 (請參閱媒體查詢解析度)。在這個示範中,我只用了幾個簡單的範例,涵蓋行動裝置上的大多數直向和橫向檢視畫面。這個方式也適合套用每部裝置的硬體加速功能。舉例來說,由於電腦版 WebKit 會加速所有轉換元素 (無論是 2-D 或 3-D),因此建立媒體查詢並從該層級排除加速,才很合理。請注意,在 Android Froyo 2.2 以上版本中,硬體加速技巧並無法提供任何提升速度。所有組成元素都在軟體中完成。

/* iOS/android phone landscape screen width*/
@media screen and (max-device-width: 480px) and (orientation:landscape) {
  .stage-left {
    left: -480px;
  }

  .stage-right {
    left: 480px;
  }

  .page {
    width: 480px;
  }
}

翻轉

在行動裝置上,跳轉是指實際將網頁滑開。我們在此使用一些簡單的 JavaScript 在 iOS 和 Android (以 WebKit 為基礎的) 裝置上處理這個事件。

操作示範:http://slidfast.appspot.com/slide-flip-rotate.html

處理觸控事件和轉換時,請先取得元素目前位置的控點。如要進一步瞭解 WebKitCSSMatrix,請參閱這份文件。

function pageMove(event) {
  // get position after transform
  var curTransform = new WebKitCSSMatrix(window.getComputedStyle(page).webkitTransform);
  var pagePosition = curTransform.m41;
}

由於我們採用 CSS3 簡化轉換功能來翻轉頁面,因此一般的 element.offsetLeft 將無法運作。

接著,我們來想一下使用者翻轉的方向,並設定事件 (網頁瀏覽) 的門檻。

if (pagePosition >= 0) {
 //moving current page to the right
 //so means we're flipping backwards
   if ((pagePosition > pageFlipThreshold) || (swipeTime < swipeThreshold)) {
     //user wants to go backward
     slideDirection = 'right';
   } else {
     slideDirection = null;
   }
} else {
  //current page is sliding to the left
  if ((swipeTime < swipeThreshold) || (pagePosition < pageFlipThreshold)) {
    //user wants to go forward
    slideDirection = 'left';
  } else {
    slideDirection = null;
  }
}

您也會發現,我們同樣是以毫秒為單位測量 swipeTime。這樣一來,在使用者快速滑動畫面並翻頁時觸發導覽事件。

為了調整頁面位置,並讓動畫在手指觸碰螢幕時呈現原生畫面,我們會在每次觸發事件後使用 CSS3 轉場效果。

function positionPage(end) {
  page.style.webkitTransform = 'translate3d('+ currentPos + 'px, 0, 0)';
  if (end) {
    page.style.WebkitTransition = 'all .4s ease-out';
    //page.style.WebkitTransition = 'all .4s cubic-bezier(0,.58,.58,1)'
  } else {
    page.style.WebkitTransition = 'all .2s ease-out';
  }
  page.style.WebkitUserSelect = 'none';
}

我試著與立方針做嘗試,為轉場效果營造出最佳的原住民氛圍,但緩解過程確實能發揮效果。

最後,為進行導覽,我們必須呼叫在上一個示範中使用的先前定義的 slideTo() 方法。

track.ontouchend = function(event) {
  pageMove(event);
  if (slideDirection == 'left') {
    slideTo('products-page');
  } else if (slideDirection == 'right') {
    slideTo('home-page');
  }
}

旋轉

接下來,我們來看看這個示範中使用的旋轉動畫。你隨時可以輕觸「聯絡人」選單選項,將目前檢視的頁面旋轉 180 度,讓畫面相反方向。再次強調,您只需要幾行 CSS 程式碼和一些 JavaScript,就能指派轉換類別 onclick。注意:在大多數的 Android 版本中,旋轉轉場效果缺少 3D CSS 轉換功能,因此無法正確轉譯。不巧的是,Android 不會忽略翻滾頁面,而是以旋轉而非翻轉的方式「瀏覽購物車」。建議您謹慎使用這項轉換功能,直到支援功能改善為止。

標記 (正面和背面的基本概念):

<div id="front" class="normal">
...
</div>
<div id="back" class="flipped">
    <div id="contact-page" class="page">
        <h1>Contact Page</h1>
    </div>
</div>

JavaScript:

function flip(id) {
  // get a handle on the flippable region
  var front = getElement('front');
  var back = getElement('back');

  // again, just a simple way to see what the state is
  var classes = front.className.split(' ');
  var flipped = classes.indexOf('flipped');

  if (flipped >= 0) {
    // already flipped, so return to original
    front.className = 'normal';
    back.className = 'flipped';
    FLIPPED = false;
  } else {
    // do the flip
    front.className = 'flipped';
    back.className = 'normal';
    FLIPPED = true;
  }
}

CSS:

/*----------------------------flip transition */
#back,
#front {
  position: absolute;
  width: 100%;
  height: 100%;
  -webkit-backface-visibility: hidden;
  -webkit-transition-duration: .5s;
  -webkit-transform-style: preserve-3d;
}

.normal {
  -webkit-transform: rotateY(0deg);
}

.flipped {
  -webkit-user-select: element;
  -webkit-transform: rotateY(180deg);
}

偵錯硬體加速

既然我們已經完成基本轉場,接著來看看這些機制的運作方式和合成做法。

為了讓這個神奇偵錯工作階段順利執行,請啟動幾個瀏覽器和您選擇的 IDE。請先從指令列啟動 Safari,以使用某些環境變數偵錯。我使用的是 Mac,所以指令可能會因作業系統而異。 開啟終端機並輸入以下內容:

  • $> 匯出 CA_COLOR_OPAQUE=1
  • $> 匯出 CA_LOG_MEMORY_USAGE=1
  • $> /Applications/Safari.app/Contents/MacOS/Safari

這會在 Safari 中啟動,並顯示幾個偵錯輔助程式。CA_COLOR_OPAQUE 會顯示實際合成或加速的元素。CA_LOG_MEMORY_USAGE 會顯示我們將繪圖作業傳送至支援存放區時的記憶體用量。這可讓您確切瞭解行動裝置的負荷程度,並可能給予提示,說明 GPU 用量可能會耗盡目標裝置的電池電量。

現在啟動 Chrome,讓我們看到每秒影格數 (FPS) 的資訊:

  1. 開啟 Google Chrome 網路瀏覽器。
  2. 在網址列中輸入 about:flags
  3. 向下捲動幾個項目,然後按一下 FPS 計數器的「啟用」。

如果您在升級後的 Chrome 查看這個頁面,畫面左上角會顯示紅色的每秒影格數計數器。

Chrome 每秒影格數

這就是我們知道硬體加速功能開啟的原因。此外,這也讓我們瞭解動畫的執行方式,以及是否有任何外洩情形 (連續執行中的動畫應該停止)。

實際以視覺化方式呈現硬體加速的另一種方法,是在 Safari 中開啟相同網頁 (使用我剛才提到的環境變數)。每個加速 DOM 元素都有紅色色調。我們得以確切顯示出各圖層合成的內容。請注意,白色導覽不會加速,因此不會是紅色。

已合併的聯絡人

Chrome 的 about:flags 部分也提供 Chrome 的類似設定「複合轉譯圖層邊框」。

另一個查看合成圖層的好方法,就是在套用這個模組時查看 WebKit 落葉示範

同形葉子

最後,為了真正瞭解應用程式的圖形硬體效能,讓我們來看看記憶體的使用情形。這裡可以看到,在 Mac OS 上,我們將 1.38 MB 的繪圖操作說明推送到 CoreAnimation 緩衝區。核心動畫記憶體緩衝區會在 OpenGL ES 和 GPU 之間共用,藉此建立畫面上顯示的最終像素。

Coreanimation 1

當我們調整瀏覽器視窗大小或把瀏覽器視窗放到最大時,記憶體也會隨之展開。

Coreanimation 2

因此,只有在您將瀏覽器尺寸調整為正確尺寸時,您才能夠瞭解行動裝置的記憶體用量。如果您之前是針對 iPhone 環境進行偵錯或測試,請將大小調整為 480 x 320 像素。我們現在明確瞭解硬體加速的運作方式,以及偵錯作業要採取什麼動作。值得一看,這是值得閱讀的,但實際上,要看到 GPU 記憶體緩衝區的實際運作情形,確實有助於呈現各種內容。

幕後花絮:擷取與快取

讓我們進一步提升網頁和資源快取功能。如同 JQuery Mobile 與類似架構使用的方法,我們會使用並行 AJAX 呼叫來預先擷取及快取網頁。

以下將說明幾項行動網路核心問題,並說明原因:

  • 擷取:預先擷取網頁可讓使用者離線執行應用程式,也不必等待瀏覽動作之間出現。當然,我們不希望在裝置連上網路時浪費裝置的頻寬,因此需要謹慎使用這項功能。
  • 快取:接下來,我們希望在擷取及快取這些網頁時,採用並行或非同步的做法。幸好,由於 localStorage 有許多裝置廣泛支援,因此也需要使用 localStorage,但很遺憾,並非非同步。
  • AJAX 並剖析回應:使用 innerHTML() 將 AJAX 回應插入 DOM 並不安全 (而且不可靠?)。我們會改為採用可靠的機制,針對 AJAX 回應插入及處理並行呼叫。此外,我們也會利用 HTML5 的新功能剖析 xhr.responseText

我們是根據投影片、翻轉和旋轉示範的程式碼為基礎,先新增一些次要頁面並連結到這些頁面。我們會即時剖析連結並建立轉場效果。

iPhone 主畫面

如要查看擷取和快取示範,請按這裡

如畫面所示,我們使用語意標記。只是連結到其他網頁的連結。子頁面遵循與父項相同的節點/類別結構。我們可以進一步採取這個步驟,並在「網頁」節點等中使用 data-* 屬性。這是詳細資料頁面 (子元素),位於獨立的 HTML 檔案 (/demo2/home-detail.html)中,系統便會載入、快取及設定應用程式載入的轉換。

<div id="home-page" class="page">
  <h1>Home Page</h1>
  <a href="demo2/home-detail.html" class="fetch">Find out more about the home page!</a>
</div>

現在來看看 JavaScript。為簡化作業,我會忽略程式碼的任何輔助程式或最佳化設定。我們正在執行迴圈,透過指定的 DOM 節點陣列找出要擷取和快取的連結。注意 - 在這個示範中,會在載入網頁時呼叫這個方法 fetchAndCache()。我們會在偵測到網路連線時重新加以處理,並判斷何時應該呼叫連線。

var fetchAndCache = function() {
  // iterate through all nodes in this DOM to find all mobile pages we care about
  var pages = document.getElementsByClassName('page');

  for (var i = 0; i < pages.length; i++) {
    // find all links
    var pageLinks = pages[i].getElementsByTagName('a');

    for (var j = 0; j < pageLinks.length; j++) {
      var link = pageLinks[j];

      if (link.hasAttribute('href') &amp;&amp;
      //'#' in the href tells us that this page is already loaded in the DOM - and
      // that it links to a mobile transition/page
         !(/[\#]/g).test(link.href) &amp;&amp;
        //check for an explicit class name setting to fetch this link
        (link.className.indexOf('fetch') >= 0))  {
         //fetch each url concurrently
         var ai = new ajax(link,function(text,url){
              //insert the new mobile page into the DOM
             insertPages(text,url);
         });
         ai.doGet();
      }
    }
  }
};

我們確保能夠使用「AJAX」物件,確保進行適當的非同步後續處理作業。如要進一步瞭解如何在 AJAX 呼叫中使用 localStorage,請參閱「使用 HTML5 離線版」的 AJAX 呼叫。在這個範例中,您會看到每項要求的基本快取用法,以及在伺服器傳回成功 (200) 回應以外的資料時提供快取物件。

function processRequest () {
  if (req.readyState == 4) {
    if (req.status == 200) {
      if (supports_local_storage()) {
        localStorage[url] = req.responseText;
      }
      if (callback) callback(req.responseText,url);
    } else {
      // There is an error of some kind, use our cached copy (if available).
      if (!!localStorage[url]) {
        // We have some data cached, return that to the callback.
        callback(localStorage[url],url);
        return;
      }
    }
  }
}

遺憾的是,由於 localStorage 使用 UTF-16 進行字元編碼,因此每個位元組將儲存成 2 個位元組,使儲存空間上限從 5MB 提高至總計 2.6MB。要在應用程式快取範圍之外擷取和快取這些網頁/標記的完整原因,將於下節說明。

我們最近對 HTML5 的 iframe 元素進行了改進,現在有一個簡單又有效的方法,可用來剖析 AJAX 呼叫傳回的 responseText。有許多 3000 行 JavaScript 剖析器和規則運算式會移除指令碼標記等。但為何不讓瀏覽器發揮最高效益?在這個範例中,我們會將 responseText 寫入臨時隱藏 iframe。我們使用的 HTML5「sandbox」屬性會停用指令碼,並提供許多安全性功能...

根據規格:如果已指定 sandbox 屬性,系統會針對 iframe 代管的任何內容啟用一組額外限制。這個值必須是一組未排序的專屬權杖 (以空格分隔,且不區分大小寫)。允許的值包括 allow-forms、allow-same-origin、allow-scripts 和 allow-top-navigation。設定這項屬性後,系統會將內容視為來自不重複來源、表單和指令碼、禁止連結其他瀏覽情境,以及停用外掛程式。

var insertPages = function(text, originalLink) {
  var frame = getFrame();
  //write the ajax response text to the frame and let
  //the browser do the work
  frame.write(text);

  //now we have a DOM to work with
  var incomingPages = frame.getElementsByClassName('page');

  var pageCount = incomingPages.length;
  for (var i = 0; i < pageCount; i++) {
    //the new page will always be at index 0 because
    //the last one just got popped off the stack with appendChild (below)
    var newPage = incomingPages[0];

    //stage the new pages to the left by default
    newPage.className = 'page stage-left';

    //find out where to insert
    var location = newPage.parentNode.id == 'back' ? 'back' : 'front';

    try {
      // mobile safari will not allow nodes to be transferred from one DOM to another so
      // we must use adoptNode()
      document.getElementById(location).appendChild(document.adoptNode(newPage));
    } catch(e) {
      // todo graceful degradation?
    }
  }
};

Safari 會正確拒絕以隱含方式將節點從某文件移到另一個文件。如果在其他文件中建立新的子節點,就會發生錯誤。我們這裡使用 adoptNode,一切都很好。

為什麼要使用 iframe?為什麼不直接使用 innerHTML?雖然 innerHTML 現已納入 HTML5 規格,但將回應從伺服器 (邪惡與壞) 插入至未勾選區域,仍是危險的做法。撰寫本文時,除了內部 HTML 以外,我並沒有找到任何人。我知道 JQuery 是以其核心為基礎,但僅有例外狀況的附加備用選項。而且 JQuery Mobile 也有用。不過,我還沒對內部 HTML「隨機停止運作」進行了大量測試,但能瞭解所有平台都受到影響,實在非常有趣。值得一提的是,哪種做法成效較佳...我也聽說兩方都說的話。

網路類型偵測、處理及剖析

我們現在有緩衝 (或預測快取) 網頁應用程式的能力,因此必須提供適當的連線偵測功能,讓我們的應用程式變得更聰明。行動應用程式開發對線上/離線模式和連線速度來說非常敏感。輸入 The Network Information API,每次我在簡報中展示這項功能時,聽眾會舉手並詢問「我能用在什麼地方?」,所以我們可以在這裡設定極具智慧的行動網頁應用程式。

這裡優先處理無聊的常見情境... 在高速火車上透過行動裝置與網路互動時,網路可能非常順利,因此不同地理位置可能支援不同的傳輸速度 (例如HSPA 或 3G 可能適用於部分都會區,但偏遠地區可能支援較慢的 2G 技術。以下程式碼可解決大多數的連線情境。

下列程式碼提供:

  • 透過「applicationCache」離線存取
  • 偵測是否已加入書籤或離線。
  • 偵測從離線切換至線上狀態,反之亦然。
  • 偵測連線速度緩慢,並根據網路類型擷取內容。

同樣地,所有功能只需極少的程式碼。首先,我們會偵測事件和載入情境:

window.addEventListener('load', function(e) {
 if (navigator.onLine) {
  // new page load
  processOnline();
 } else {
   // the app is probably already cached and (maybe) bookmarked...
   processOffline();
 }
}, false);

window.addEventListener("offline", function(e) {
  // we just lost our connection and entered offline mode, disable eternal link
  processOffline(e.type);
}, false);

window.addEventListener("online", function(e) {
  // just came back online, enable links
  processOnline(e.type);
}, false);

在上方的 EventListener 中,我們必須告知程式碼是經由事件或實際網頁要求或重新整理來呼叫。主要原因是在線上和離線模式之間切換時,不會觸發內文 onload 事件。

接下來,我們會對 ononlineonload 事件進行簡單的檢查。從離線切換至線上狀態時,這個程式碼會重設已停用的連結,但如果這個應用程式較複雜,您可以插入邏輯,以便繼續擷取內容或處理使用者體驗,即使連線不間斷。

function processOnline(eventType) {

  setupApp();
  checkAppCache();

  // reset our once disabled offline links
  if (eventType) {
    for (var i = 0; i < disabledLinks.length; i++) {
      disabledLinks[i].onclick = null;
    }
  }
}

processOffline() 也是如此。您將在離線模式下操控應用程式,並嘗試復原在幕後進行的任何交易。以下程式碼可以深入分析並停用所有外部連結,從我們的離線應用程式中追蹤使用者,永遠不可能了!

function processOffline() {
  setupApp();

  // disable external links until we come back - setting the bounds of app
  disabledLinks = getUnconvertedLinks(document);

  // helper for onlcick below
  var onclickHelper = function(e) {
    return function(f) {
      alert('This app is currently offline and cannot access the hotness');return false;
    }
  };

  for (var i = 0; i < disabledLinks.length; i++) {
    if (disabledLinks[i].onclick == null) {
      //alert user we're not online
      disabledLinks[i].onclick = onclickHelper(disabledLinks[i].href);

    }
  }
}

好,接著是「好東西」現在,應用程式已知道連線狀態,我們也能在連上網路時檢查連線類型,然後做出相應調整。我在每次互動的評論中,列出了一般北美地區的供應商下載作業和延遲時間。

function setupApp(){
  // create a custom object if navigator.connection isn't available
  var connection = navigator.connection || {'type':'0'};
  if (connection.type == 2 || connection.type == 1) {
      //wifi/ethernet
      //Coffee Wifi latency: ~75ms-200ms
      //Home Wifi latency: ~25-35ms
      //Coffee Wifi DL speed: ~550kbps-650kbps
      //Home Wifi DL speed: ~1000kbps-2000kbps
      fetchAndCache(true);
  } else if (connection.type == 3) {
  //edge
      //ATT Edge latency: ~400-600ms
      //ATT Edge DL speed: ~2-10kbps
      fetchAndCache(false);
  } else if (connection.type == 2) {
      //3g
      //ATT 3G latency: ~400ms
      //Verizon 3G latency: ~150-250ms
      //ATT 3G DL speed: ~60-100kbps
      //Verizon 3G DL speed: ~20-70kbps
      fetchAndCache(false);
  } else {
  //unknown
      fetchAndCache(true);
  }
}

我們可以對 getAndCache 程序進行許多調整,但我剛才做的都是以非同步 (true) 或同步 (false) 的方式擷取特定連線的資源。

邊緣 (同步) 要求時程

邊緣同步處理

WIFI (非同步) 要求時程

Wi-Fi 非同步

這可讓系統根據連線速度緩慢或快速連線,對使用者體驗做出至少一部分的使用者體驗調整。換句話說,這並不是萬無一失的解決方案。另一個做法是在點選連結 (連線緩慢時) 時擲回載入視窗,同時應用程式可能仍在背景擷取該連結頁面。 重點在於減少延遲,同時充分利用最新、最棒的 HTML5 功能,為使用者帶來的完整連結。 如要查看網路偵測示範,請按這裡

結語

行動 HTML5 應用程式的發展之道,現在,您可以看到完全以基本方式建構的 HTML5 行動「架構」及支援技術。我認為開發人員應該盡其核心來運用和處理這些功能,而不是以包裝函式遭到遮蓋。