建構跨裝置網頁應用程式時沒有反應的方法

波瑞斯 (Boris Smus)
Boris Smus

媒體查詢很實用,但...

媒體查詢是絕佳的老師,可讓網站開發人員微調樣式表,為使用不同尺寸的裝置使用者提供更優質的體驗。媒體查詢基本上可讓您根據螢幕大小自訂網站的 CSS。閱讀本文前,可以先進一步瞭解回應式設計,並查看媒體查詢的使用範例:mediaqueri.es

正如 Brad Frost 在先前文章中所指出,變更外觀只是建構行動版網站時需要考慮的因素之一。如果您建立行動網站的唯一目的,就是使用媒體查詢自訂版面配置,則會發生以下狀況:

  • 所有裝置都會取得相同的 JavaScript、CSS 和素材資源 (圖片、影片),導致載入時間超過必要時間。
  • 所有裝置都有相同的初始 DOM,導致開發人員必須編寫過於複雜的 CSS。
  • 針對每部裝置指定專屬的自訂互動幾乎沒有彈性。

網頁應用程式需要的不只是媒體查詢

別想錯了。我不討厭使用媒體查詢的回應式設計 我一定會認為它有全球適用此外,您可以透過回應式圖片、動態指令碼載入等方法,解決上述部分問題。不過,在特定時間點,您可能會發現自己執行過多漸進調整,以提供不同版本更好。

隨著建構的 UI 越來越複雜,您渴望單頁網頁應用程式,就必須投入更多心力,才能自訂每種裝置類型的 UI。本文將說明如何以最簡便的方式完成這些自訂。一般做法是將訪客的裝置歸類到適當的裝置類別,為該裝置提供適當的版本,同時盡量在版本之間重複使用程式碼。

您指定哪些裝置類別?

市面上已有許多可連上網際網路的裝置,而且幾乎所有裝置都有瀏覽器。相關的小工具相當多元,包括 Mac 筆記型電腦、Windows 工作站、iPhone、iPad、可觸控輸入、滾輪、鍵盤、語音輸入的 Android 手機、具有壓力感測功能的裝置、智慧型手錶、烤麵包機和冰箱等。有些裝置非常普遍,但很少完全很少。

五花八門,
各式各樣的裝置 (來源)。

您必須瞭解使用者的身分和所用裝置,才能打造良好的使用者體驗。如果您使用滑鼠和鍵盤為電腦使用者建構使用者介面,並提供給智慧型手機的使用者,則介面是專為另一種螢幕大小而設計,並且是另一種輸入模式而設計造成困擾。

做法有兩種:

  1. 建立一個適用於所有裝置的版本。使用者體驗可能會因此降低,因為不同裝置的設計考量不同。

  2. 為要支援的每部裝置分別建立版本。這可能會非常耗時,因為您要建構的應用程式版本太多。此外,當下一支新款智慧型手機送達 (大約每週一次) 時,您必須建立另一個版本。

兩者的優缺點如下:擁有的裝置類別越多,提供的使用者體驗就越好,但設計、實作和維護需要付出的努力也越多。

基於效能考量,或是要為不同裝置類別提供的版本差異很大,建議您為決定的每個裝置類別分別建立專屬版本。否則,回應式網頁設計是相當合理的做法。

可能的解決方案

這包括分類:將裝置分門別類,並為每個類別設計最佳體驗。選擇的類別則視產品和目標使用者而定。這裡的分類範例適用於目前市面上具備網路連線的熱門裝置。

  1. 小螢幕 + 觸控 (大部分是手機)
  2. 大螢幕 + 觸控 (大部分為平板電腦)
  3. 大螢幕 + 鍵盤/滑鼠 (主要為電腦/筆電)

這只是許多可能的細目之一,但在本文撰寫時尤其合理。上方清單所缺少的是沒有觸控螢幕的行動裝置 (例如功能型手機、部分專用電子書閱讀器)。不過,其中大多數軟體都安裝了鍵盤導覽或螢幕閱讀器軟體,因此如果在建立網站時考量到無障礙功能,就很適合採用。

特定板型規格網頁應用程式範例

有許多版本的網站資源會根據不同的板型規格提供完全不同的版本。Google 搜尋執行這項動作,跟 Facebook 相同。需要考量的因素包括效能 (擷取資產、轉譯頁面) 和一般使用者體驗。

在原生應用程式的世界中,許多開發人員會選擇根據裝置類別打造專屬體驗。舉例來說,iPad 適用的 Flipboard 與 iPhone 上的 Flipboard 有何差異。平板電腦版本已針對雙手和水平翻轉進行最佳化,而手機版本則適合用來進行單手互動和垂直翻轉。許多其他 iOS 應用程式也提供了明顯不同的手機和平板電腦版本,例如物業 (待辦事項清單) 和 Showyou (社交影片),如下所示:

針對手機和平板電腦提供重要的使用者介面自訂功能。
針對手機和平板電腦提供重要的使用者介面自訂功能。

方法 1:伺服器端偵測

在伺服器中,我們對於處理的裝置要有一定程度的瞭解。目前最實用的線索可能是使用者代理程式字串,可在每個要求中透過 User-Agent 標頭提供。因此,這裡將會使用相同的通用 Analytics (分析) 縮小做法。事實上,DeviceAtlas 和 WURFL 專案就已經執行這項作業 (以及裝置有許多額外資訊)。

然而,每個挑戰都有各自的挑戰。WURFL 非常大,包含 20 MB 的 XML,因此可能對每項要求造成大量的伺服器端負擔。有些專案會基於效能考量分割 XML。DeviceAtlas 並非開放原始碼,而且需要付費授權才能使用。

還有更簡單的替代方案,例如「偵測行動瀏覽器」專案。但缺點是裝置偵測功能的全面性不足。此外,它只能區分行動裝置和非行動裝置,因此只能透過「臨時調整組合」來提供有限的平板電腦支援。

方法 2:用戶端偵測

我們可以透過功能偵測功能,深入瞭解使用者的瀏覽器和裝置。我們需要判斷裝置是否具備觸控功能,以及是大型或小型螢幕。

我們需要在某處繪製線條,以便區分小型和大型觸控裝置。那麼 5 吋 Galaxy Note 這類極端案例呢?下圖顯示了許多熱門的 Android 和 iOS 裝置 (有相對應的螢幕解析度)。星號表示裝置目前為雙重密度,或是可能發生雙倍密度。雖然像素密度可能會加倍,但 CSS 仍會回報相同的大小。

CSS 顯示像素的快速優點:行動版網站上的 CSS 像素與螢幕像素不相同。iOS Retina 裝置導入了像素密度增加一倍 (例如 iPhone 3GS 與 4、iPad 2 與 3),Retina Mobile Safari UA 仍回報相同的裝置寬度,以避免中斷網路。與其他裝置 (例如Android) 的螢幕解析度較高,而且會執行相同的裝置寬度技巧。

裝置解析度 (以像素為單位)。
裝置解析度 (以像素為單位)。

不過,複雜的決策是同時考慮直向和橫向模式的重要性。我們不想在每次重新調整裝置位置時重新載入頁面或載入其他指令碼,不過我們可能會想以不同的方式轉譯網頁。

在下圖中,正方形代表每部裝置的尺寸上限,原因在於重疊直向和橫向外框 (並結束正方形):

直向 + 橫向解析度 (以像素為單位)
直向 + 橫向解析度 (以像素為單位)

將門檻設為 650px 後,我們會將 iPhone、Galaxy Nexus 和 iPad、Galaxy Tab 歸類為「平板電腦」。androgynous Galaxy Notes 在本範例中歸類為「電話」,並取得手機版面配置。

因此,合理的策略看起來可能會像這樣:

if (hasTouch) {
  if (isSmall) {
    device = PHONE;
  } else {
    device = TABLET;
  }
} else {
  device = DESKTOP;
}

查看功能偵測方法的基本範例。

這裡的另一種做法是使用通用 Analytics (分析) Snffc 功能偵測裝置類型。基本上,您需要建立一組經驗法則,並與使用者的 navigator.userAgent 進行比對。虛擬程式碼看起來像這樣:

var ua = navigator.userAgent;
for (var re in RULES) {
  if (ua.match(re)) {
    device = RULES[re];
    return;
  }
}

查看通用 Analytics (分析) 偵測方法的實際應用範例。

用戶端載入的注意事項

如果您在伺服器上執行通用 Analytics (分析) 偵測功能,您可以決定收到新要求時要提供的 CSS、JavaScript 和 DOM。不過,在進行用戶端偵測時,情況會更加複雜。您有以下幾個選擇:

  1. 重新導向至裝置類型專用網址,其中包含此裝置類型的版本。
  2. 動態載入裝置類型專屬的素材資源。

第一種方式十分簡單,需要使用 window.location.href = '/tablet' 等重新導向。但是,位置現在會附加此裝置類型資訊,因此建議您使用 History API 來清理網址。但這個方法牽涉到重新導向,因此處理速度可能相當慢,在行動裝置上更是如此。

第二種實作方式則比較複雜。您需要採用動態載入 CSS 和 JS 的機制,而且可能無法執行自訂 <meta viewport> 等操作 (視瀏覽器而定)。此外,由於沒有重新導向,所以會卡在原本提供的 HTML 版本中。當然,您可以使用 JavaScript 來操控,但根據您的應用程式而定,這會是緩慢且/或未完成的作業。

決定用戶端或伺服器

以下是方法的優缺點:

Pro 客戶

  • 根據螢幕尺寸/功能 (而非通用 Analytics (分析)) 提供未來的驗證方式。
  • 不需要持續更新通用 Analytics (分析) 名單。

Pro 伺服器

  • 可完全控管要在哪個裝置提供的版本。
  • 效能更佳:不需要用戶端重新導向或動態載入。

我的個人偏好從 device.js 和用戶端偵測開始;隨著應用程式演進,如果您發現用戶端重新導向造成極大的缺點,您可以輕鬆移除 device.js 指令碼,並在伺服器上實作通用 Analytics (分析) 偵測。

device.js 簡介

Device.js 是執行語意、媒體查詢式裝置偵測的起點,不需要特殊的伺服器端設定,節省剖析使用者代理程式字串所需的時間和精力。

這麼做的用意是在 <head> 上方提供適用於搜尋引擎的標記 (link rel=alternate),指明您要提供的網站版本。

<link rel="alternate" href="http://foo.com" id="desktop"
    media="only screen and (touch-enabled: 0)">

接下來,您可以自行執行伺服器端通用 Analytics (分析) 偵測及處理版本重新導向,或使用 device.js 指令碼執行功能式用戶端重新導向。

詳情請參閱 device.js 專案頁面,以及使用 device.js 進行用戶端重新導向的假應用程式

建議:搭配板型規格特定觀看的 MVC

您現在可能會認為 您建構三個完全獨立的應用程式,每種裝置類型各一個。不!共用程式碼至關重要。

希望您已使用類似 MVC 的架構,例如 Backbone、Ember 等。如果您已熟悉關注點分離原則,尤其是您的 UI (檢視層) 應與邏輯 (模型層) 分離。如果您是第一次使用,請先參閱一些 MVC 上的資源以及 JavaScript 中的 MVC

跨裝置故事與現有的 MVC 架構相輔相成。您可以輕鬆將檢視畫面移到個別檔案,為每種裝置類型建立自訂檢視畫面。接著,您可以將相同的程式碼提供給所有裝置 (檢視畫面層除外)。

跨裝置 MVC。
跨裝置 MVC。

您的專案可能具有下列結構 (當然,您可以自行選擇要根據應用程式使用的結構):

model/ (共用模型) item.js item-collection.js

Controllers/ (共用控制器) item-controller.js

version/ (裝置專屬內容) Table/ 電腦/ 手機/ (手機專用程式碼) style.css index.html view/ item.js item-list.js

由於每部裝置都有自訂 HTML、CSS 和 JavaScript,因此這種結構可讓您完全控制每個版本載入的素材資源。這項功能非常強大,因此不必依賴自動調整式圖片等技巧,為跨裝置網頁開發出最理想、效能最佳的方法。

當您執行喜愛的建構工具後,請將所有 JavaScript 和 CSS 串連並壓縮成單一檔案,加快載入速度。正式的 HTML 看起來會像這樣 (適用於手機,使用 device.js):

<!doctype html>
<head>
  <title>Mobile Web Rocks! (Phone Edition)</title>

  <!-- Every version of your webapp should include a list of all
        versions. -->
  <link rel="alternate" href="http://foo.com" id="desktop"
      media="only screen and (touch-enabled: 0)">
  <link rel="alternate" href="http://m.foo.com" id="phone"
      media="only screen and (max-device-width: 650px)">
  <link rel="alternate" href="http://tablet.foo.com" id="tablet"
      media="only screen and (min-device-width: 650px)">

  <!-- Viewport is very important, since it affects results of media
        query matching. -->
  <meta name="viewport" content="width=device-width">

  <!-- Include device.js in each version for redirection. -->
  <script src="device.js"></script>

  <link rel="style" href="phone.min.css">
</head>
<body>
  <script src="phone.min.js"></script>
</body>

請注意,(touch-enabled: 0) 媒體查詢屬於非標準查詢 (僅在以 moz 供應商前置字元後方在 Firefox 中實作),但 device.js 會正確處理 (不符合 Modernizr.touch)。

版本覆寫

裝置偵測功能有時可能會發生錯誤,在某些情況下,使用者可能會想查看手機的平板電腦版面配置 (也許他們使用的是 Galaxy Note),因此請務必讓使用者選擇想手動覆寫的網站版本。

常見的做法是提供行動版的連結。雖然實作方式簡單,但 device.js 可透過 device GET 參數支援這項功能。

結語

總結來說,如果建構的跨裝置單頁 UI 無法完全融入回應式設計,請執行以下操作:

  1. 挑選一組支援的裝置類別,以及將裝置分類依據的條件。
  2. 建構 MVC 應用程式時,需要適當的關注點分離,並拆分其他程式碼集的檢視畫面。
  3. 使用 device.js 偵測用戶端裝置類別。
  4. 準備就緒後,請將指令碼和樣式表封裝到每個裝置類別之一中。
  5. 如果用戶端重新導向效能有問題,請放棄 device.js,然後切換至伺服器端 UA 偵測。