- 原文地址:Progressive Web AMPs
- 原文作者:Paul Bakaus
- 譯文出自:掘金翻譯計劃
- 譯者:L9m
- 校對者:marcmoore,sqrthree
如果你最近幾個月一直關注着 Web 開發社區,可能你對漸進增強的 Web 應用(Progressive Web App 簡稱 PWA)已有所瞭解。它是應用體驗能與原生應用媲美的 Web 應用的統稱:不依賴網絡連接,易安裝,支持視網膜屏幕,支持無邊距圖像,支持登錄和個性化,快速且流暢的應用體驗,支持推送通並且有一個好看的界面。
一些 Google 的漸進式 Web 應用示例。
雖然新的 Service Worker API 允許離線緩存所有的網站資源以便在後續加載中瞬時加載,就像陌生人的第一印象很關鍵一樣。最新的 DoubleClick 研究 (譯者注:DoubleClick 是谷歌旗下一家公司)表明,如果首次加載超過 3 秒,超過 53% 的用戶將放棄訪問。
老實說,3 秒已是一個相當嚴峻的目標。移動端連接通常有平均 300ms 延遲,而且附帶有帶寬限制和時不時信號弱等不利情況,你可能只剩下不到 1 秒時間留給應用初始化等事情。
用戶和內容之間的延遲。
當然,有一些方法能緩解首次加載緩慢的問題 — 在服務器上預先渲染好一個基礎結構,再懶加載各個功能模塊等等 — 但是使用此種策略達到的優化程度也有限,而且不得不僱傭一個前端優化專家,或者自己成長爲一個專家。
那麼,我們有什麼方法來做一個從根本上和原生應用不同的首次瞬時加載呢?
AMP,爲移動頁面加速
網站的最重要的優勢之一是跨平臺 — 無需安裝和即刻加載,用戶通常只需輕擊一下鼠標即可。
要想輕鬆地從短瀏覽(ephemeral browsing)的機會中收益,所需的就是一個瞬時加載(crazy-fast-loading)的網站。讓網站瞬時加載,你需要做些什麼呢?你所需做的只是一個適當的節制:沒有兆字節大小圖片,阻塞渲染的廣告,沒有十萬行 JavaScript,就只有這些要求。
AMPs,是加速移動網頁(Accelerated Mobile Pages)的簡稱,它擅長於此,實際上,這是它們存在的原因(raison d’être)。它就像一個駕駛輔助功能,通過實行一套合理的規則,優化你的網頁主體內容,讓它們處於快車道。並通過創建這種嚴格的,靜態的佈局環境,使譬如谷歌搜索等平臺僅預渲染首屏,得以進一步接近“瞬時”。
此 AMP 的首屏橫幅圖片(hero image)和標題將被提前渲染,以便訪問者瞬時看到首屏內容。
AMP 還是 PWA?
AMP 可靠快速的體驗,在實現時也伴隨着一些限制。當你需要高度動態的功能時,AMP 是不適用的,譬如推送通知、網絡支付和依靠額外 JavaScript 的功能。此外,因爲 AMP 頁面通常從 AMP 緩存中提供,你的 Service Worker 不能運行,首次訪問享受不到漸進式 Web 應用的最重要的好處。另一方面,在首次訪問的速度上,漸進式 Web 應用永遠不及 AWP,因爲平臺能順利且毫不費力地預渲染 AMP 頁面 — 內嵌更簡單(比如在內嵌瀏覽器中)。
一旦用戶點擊內部鏈接,離開 AMP 緩存,你就能通過安裝 service worker 來增強網站,讓網站支持離線和更多的功能。
那麼,是 AMP 還是漸進式 Web 應用?瞬時交付還是優化交付,或是最先進的平臺功能和靈活的應用代碼?有沒有一種結合兩者的好處的方式呢?
完美的用戶旅程(User Journey)
終究,重要的是針對用戶旅程的理想體驗。它大概是這樣的:
- 用戶發現了一個指向你的內容的鏈接,並且點擊了它。
- 內容快速加載是一種愉快的體驗。
- 用戶被通知並進階到有推送通知和支持離線的更流暢的體驗。
- 立即重定向到一個類原生的體驗,並且可將網站放在你的主屏幕上。用戶驚呼:“怎麼回事?好神奇!”。
訪問網站的第一步應該讓人感覺快速,其後的瀏覽體驗應該越來越引人入勝。
聽起來是不是好的難以置信?好吧,儘管乍看,它們解決不同的問題且不相關,要是我們結合兩種技術會怎麼樣呢?
PWAMP 結合模式
要獲得瞬時加載,漸進增強的體驗,你所需做的是將 AMPs 和漸進式 Web 應用的豐富功能用下列之一(或多)的方式相結合:
-
AMP 作爲 PWA
當你可以接受 AMP 的限制時。 -
AMP 轉作 PWA
當你想要在兩者之間平滑過渡時。 -
AMP 在 PWA 中
當你重用 AMP 爲 PWA 的數據源時。
讓我們每個都過一遍吧。
AMP 作爲 PWA
許多網站其實用不到超出 AMP(功能)範圍。Amp by Example 就是一個例子,它既是 AMP 也是一個漸進式 Web 應用。
- 它有 service worker,因此允許包括離線訪問等在內的其他功能。
- 它有清單(manifest),在橫幅(banner)上會提醒“添加到主屏幕“。
當用戶從谷歌搜索訪問 Amp by Example,然後點擊網站上鍊接,它們將從 AMP 緩存頁面轉到源頁面上。當然,網站仍在使用 AMP 庫,但是現在由於它處於源頁面上,它能使用 service worker 或提示安裝等等。
你可以使用此項技術讓你的 AMP 網站支持離線訪問,一旦他們訪問源頁面就進行擴展,因爲你可以通過 service worker 的 fetch 事件來修改響應(response),並返回你想要的響應 (response)。
function createCompleteResponse (header, body) {
return Promise.all([
header.text(),
getTemplate(RANDOM STUFF AMP DOESN’T LIKE),
body.text()
]).then(html => {
return new Response(html[0] + html[1] + html[2], {
headers: {
'Content-Type': 'text/html'
}
});
});
}
這一技術也允許你在 AMP 後續訪問中插入腳本,提供超出 AMP 範圍外的更進階的功能。
AMP 轉作 PWA
當上述不能滿足,並且你想讓內容有一個完全不同的 PWA 體驗時,是時候用一種更高級一點的模式了:
- 爲了接近瞬時加載的體驗,所有內容“葉”頁(指有特定內容,不是概述的頁面)被髮布成 AMP。
- 這些 AMP 使用 AMP 的特殊元素 <amp-install-serviceworker> 來預備緩存,並且當用戶喜歡你的內容時用 PWA 的外殼。
- 當用戶點擊你網站上的另一個鏈接(比如,在底部的行爲召喚(按鈕),使其更像原生)service worker 攔截請求並接管頁面,然後加載 PWA 外殼替代之。
假如你熟悉 service worker 的運作,你可以通過以上三個簡單步驟來實現這種體驗。(如果你不清楚的話,強烈推薦我的同事傑克在優達學城(Udacity)上的課程)。第一步,在你所有的 AMP 上放置 service worker。
<amp-install-serviceworker
src="https://www.your-domain.com/serviceworker.js"
layout="nodisplay">
</amp-install-serviceworker>
第二步,在 service worker 安裝過程中,緩存 PWA 所需的所有資源。
var CACHE_NAME = 'my-site-cache-v1';
var urlsToCache = [
'/',
'/styles/main.css',
'/script/main.js'
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
最後,又回到 service worker,攔截 AMP 的導航請求,用 PWA 替代響應。(下面的代碼是功能簡化版本,後面還有一個更進階的示例。)
self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate') {
event.respondWith(fetch('/pwa'));
// Immediately start downloading the actual resource.
fetch(event.request.url);
}
});
現在,每當用戶點擊從 AMP 緩存頁面上的鏈接,service worker 註冊 navigate 請求模式(request mode)並接管,然後用已緩存的成熟(full-brown)的 PWA 代替。
你可以通過在網站上安裝一些 service worker 來實現漸進增強。對於不支持 service worker 的瀏覽器,它們僅會轉移到 AMP 緩存頁面。
此項技術很有意思之處在於從 AMP 漸進增強到 PWA。然而,這也意味着,暫時不支持 service worker 的瀏覽器將從 AMP 跳到 AMP 並且不會導航到 PWA。
AMP 通過 Shell URL 重寫 來跳轉。通過在 <amp-install-serviceworker> 標籤中添加一個備用 URL 模式(URL pattern),如果檢測到不支持 service worker,就指示 AMP 重寫特定頁面上所有匹配的鏈接,用另一個傳統的 shell URL 替代(外殼(Shell)是應用的用戶界面所需的最基本的 HTML、CSS 和 JavaScript,也是一個用來確保應用有好多性能的組件。它的首次加載將會非常快,加載後立刻被緩存下來。這意味着應用的外殼不需要每次使用時都被下載,而是隻加載需要的數據。 ):
<amp-install-serviceworker
src="https://www.your-domain.com/serviceworker.js"
layout="nodisplay"
data-no-service-worker-fallback-url-match=".*"
data-no-service-worker-fallback-shell-url="https://www.your-domain.com/pwa">
</amp-install-serviceworker>
在有 service worker 的情況下具有了這些屬性,AMP 上所有後續點擊都將轉到 PWA。挺巧妙的,是吧?
AMP 在 PWA 中
那麼,現在用戶處於漸進式 Web 應用中,你可能會使用一些 AJAX 驅動(AJAX-driven)的導航,通過 JSON 來獲取內容。你當然可以這麼做,但是現在有兩個完全不同的內容後端和基礎架構需求 — 一個生成 AMP 頁面,另外一個爲你的漸進式 Web 應用提供基於 JSON 格式的接口。
但請想一想 AMP 的本質是什麼。它不只是一個網站,它被設計成一個超輕便的內容單元。AMP 是獨立的且可以順利地嵌入到其他網站。我們是否可以拋棄 JSON 接口,使用 AMP 作爲我們漸進式 Web 應用的數據格式,從而大大降低後端複雜性呢?
AMP 頁面能順利地嵌入其他網站中 — PWA 的 AMP 庫只會編譯並加載一次。
當然,一個簡單的方法是在 frames 中加載 AMP 頁面。但是使用 iframes 比較慢,並且需要你一遍又一遍地重新編譯和初始化 AMP 庫。現在前沿的 Web 技術提供了一種更好的方式:Shadow DOM。
處理過程看起來是這樣的:
- PWA 操縱所有的導航點擊事件。
- 然後,用 XMLHttpRequest 獲取請求的 AMP 頁面。
- 將內容放入一個新的 shadow root 中。
- 然後返回給主 AMP 庫,“嘿,我有一個新文檔給你。請查收!”(在運行時調用 attachShadowDoc)。
使用此種技術,整個 PWA 只會編譯和加載一次 AMP 庫,並且然後,因爲你是通過 XMLHttpRequest 獲取的頁面,你能在 AMP 源插入新的 shadow document 之前進行一些修改,你可以像這樣做:
- 去掉不必要的內容,比如頁眉(header)和頁腳(footer);
- 插入額外的內容,比如令人反感的廣告或信息提示;
- 用更動態的內容替換特定內容。
現在,你使你的漸進式 Web 應用更簡單了,而且大大簡化了後端結構。
準備,配置,實行!
AMP 團隊做了一個名爲 The Scenic 的 React 示例 來演示
shadom DOM 方法(也就是:PWA 中的 AMP),它是一本假的旅行雜誌:
整個示例的代碼在 Github 上,但關鍵代碼在 React 組件 amp-document.js 中。
看點真東西
一個真實產品的例子是 Mic 新式 PWA(beta 階段),研究一下 :如果你按住 shift 重新刷新(shift-reload)任意文章(這樣暫時忽略 service worker)查看源代碼, 你會注意到這是一個 AMP 頁面。現在嘗試點擊一下菜單:它會重新加載當前頁面, 但由於 <amp-install-serviceworker> 已存在於 PWA 應用外殼中,重載幾乎是瞬間完成的,並且菜單在刷新後打開,使其看起來不像是重新加載過一樣。但現在你處於擁有其他豐富功能的(內嵌 AMP 頁面)PWA 中。狡猾,但很了不起。
結語
無需多說,我非常激動地憧憬着新結合的潛力。這個結合集兩者之所長。
優點概括:
- 不論什麼情況,都很快;
- 良好的內置支持(通過 AMP 的平臺夥伴);
- 漸進增強;
- 只需一種後端接口;
- 降低客戶端複雜性;
- 成本低;
但是我們纔開始發掘這種模式的變型,也是全新的一種。除提供構建 2016 最好的 Web 體驗之外,繼續向前到達 Web 的新篇章。