摘要:類似于我們熟知的,可以脫離主線程,處理一些臟累活,干完后通過向主線程匯報工作結(jié)果。所以,也是脫離主線程的存在,與不同的是,具有持久化的能力。
什么是 PWA
Progressive Web App, 簡稱 PWA,是「漸進(jìn)式」提升 Web App 體驗的一種新方法,能給用戶類似原生應(yīng)用的體驗。
「高可靠,高性能,優(yōu)體驗」是 PWA 慣用的形容詞,他的另外一個優(yōu)點就是「漸進(jìn)式」,開發(fā)者可以對照 PWA Checklist 逐步對自己站點進(jìn)行 PWA 化升級。
PWA 的發(fā)展史
2007
蘋果前 CEO,Steve Jobs,2007 年 WWDC 上提出了為初代 iPhone 開發(fā)應(yīng)用的概念,當(dāng)時所介紹的,就是 Web App——可以從主屏直接啟動的 Web 應(yīng)用。
圖片來源 appleinsider.com
可惜當(dāng)時這個理念太過超前,并沒有引發(fā)太多關(guān)注,反而是后來的原生 App 應(yīng)用更符合當(dāng)時的市場需求,互聯(lián)網(wǎng)公司更愿意投入人力在原生 App 的開發(fā)上,而忽略了 Web。因此原生 App 的大量出現(xiàn),占據(jù)了移動時代的主流地位,Web 似乎就要被 App 所取代。
2014
隨著 Web 技術(shù)的發(fā)展,時間來到 2014 年, W3C 公布了 Service Worker 的相關(guān)草案,其生產(chǎn)環(huán)境在 2015 年被 Chrome 支持。隨后 PWA 加以完善,相關(guān)技術(shù)不斷升級優(yōu)化,在用戶體驗和用戶?;顑煞矫娴目砂l(fā)掘價值越來越大。
2017
繼移動站點噴井式發(fā)展之焰末,原生 App 的弊端越發(fā)明顯,對于它來講,最大的痛點便是其天生封閉的基因?qū)е碌膬?nèi)容無法被索引,相對的 Web 站點可索引的優(yōu)勢開始凸顯,與此同時,PWA 遵循 W3C 標(biāo)準(zhǔn)開發(fā)的技術(shù),完全開放,能夠快速地被各大瀏覽器廠商支持,市場支持度一夜崛起。
另外一邊,App 的推廣并不順利,據(jù)調(diào)查統(tǒng)計,移動設(shè)備用戶 80% 的時間花費在了常用的 5 個應(yīng)用上,16年近一半的美國用戶平均每月安裝「0」個新 App,用戶積極探索新 App 已經(jīng)成為了過去式,拉新和保活的成本越來越高。
原生 App 的發(fā)展遇到了天花板,推廣也正向瓶頸一步步靠近,Web 看到了自己的機(jī)遇,PWA 以及支撐 PWA 的一系列關(guān)鍵技術(shù)應(yīng)運而生。
2018
2018 年對于 PWA 來說是里程碑的一年,萬眾矚目的 Apple 終于在 iOS 11.3 里支持了 Web App Manifest,以及內(nèi)置的 Safari 11.1 支持了 Service Worker。
與此同時,全球頂級瀏覽器廠商,Google、Microsoft、Apple 已經(jīng)全數(shù)宣布支持 PWA 技術(shù),這預(yù)示著,Web App 將會迎來全新的時代
2019
截至當(dāng)下的 PWA 支持度
依據(jù) Can I use 的統(tǒng)計(20190515)
App Manifest 的支持度達(dá)到 58.82%
Service Worker 的支持度達(dá)到 90.36%
Notifications API 的支持度達(dá)到 76.12%
Push API 的支持度達(dá)到 78.35%
Background Sync 的支持度達(dá)到 71.35%
Service Worker 以全數(shù)「登船」。信息來源于 jakearchibald.github.io/isservicewo…
PWA 的核心
PWA 有幾個核心功能,分別是「離線,安裝,推送」
離線瀏覽
弱網(wǎng)或離線的情況下依然可以「正常訪問」甚至「秒開」,這種體驗甚至超過了 app。主要的技術(shù)點就是 Service Worker。
SW 類似于我們熟知的 Web Worker,Web Worker 可以脫離主線程,處理一些「臟累」活,干完后通過 postMessage 向主線程匯報工作結(jié)果。所以,SW 也是脫離主線程的存在,與 Web Worker 不同的是,SW 具有持久化的能力。
SW 還具備有以下功能和特性:
一個獨立的 worker 線程,獨立于當(dāng)前網(wǎng)頁進(jìn)程,有自己獨立的 worker context。
一旦被 install,就永遠(yuǎn)存在,除非被手動 unregister
用到的時候可以直接喚醒,不用的時候自動睡眠
可編程攔截代理請求和返回,緩存文件,緩存的文件可以被網(wǎng)頁進(jìn)程取到(包括網(wǎng)絡(luò)離線狀態(tài))
離線內(nèi)容開發(fā)者可控
能向客戶端推送消息
不能直接操作 DOM
必須在 HTTPS 環(huán)境下才能工作
異步實現(xiàn),內(nèi)部大都是通過 Promise 實現(xiàn)
基于以上我們可以看到 SW 要讓緩存做到極致優(yōu)雅的偉大使命。
想要靈活的使用 SW 功能,就要充分了解他的生命周期,以及各階段的狀態(tài)。
以下是 MDN 給出的 SW 的詳細(xì)生命周期圖。
可以看到,SW 的生命周期包含這么幾個狀態(tài) 安裝中, 安裝后, 激活中, 激活后和廢棄
安裝( installing ):這個狀態(tài)發(fā)生在 Service Worker 注冊之后,表示開始安裝,觸發(fā) install 事件回調(diào)指定一些靜態(tài)資源進(jìn)行離線緩存。 install 事件回調(diào)中有兩個方法:
event.waitUntil():傳入一個 Promise 為參數(shù),等到該 Promise 為 resolve 狀態(tài)為止。
self.skipWaiting():self 是當(dāng)前 context 的 global 變量,執(zhí)行該方法表示強(qiáng)制當(dāng)前處在 waiting 狀態(tài)的 Service Worker 進(jìn)入 activate 狀態(tài)。
安裝后( installed ):Service Worker 已經(jīng)完成了安裝,并且等待其他的 Service Worker 線程被關(guān)閉。
激活( activating ):在這個狀態(tài)下沒有被其他的 Service Worker 控制的客戶端,允許當(dāng)前的 worker 完成安裝,并且清除了其他的 worker 以及關(guān)聯(lián)的舊緩存資源,等待新的 Service Worker 線程被激活。
activate 回調(diào)中有兩個方法:
event.waitUntil():傳入一個 Promise 為參數(shù),等到該 Promise 為 resolve 狀態(tài)為止。
self.clients.claim():在 activate 事件回調(diào)中執(zhí)行該方法表示取得頁面的控制權(quán), 這樣之后打開頁面都會使用版本更新的緩存。舊的 Service Worker 腳本不再控制頁面,之后會被停止。
激活后( activated ):在這個狀態(tài)會處理 activate 事件回調(diào) (提供了更新緩存策略的機(jī)會)。并可以處理功能性的事件,fetch (請求)、sync (后臺同步)和 push (推送)。
廢棄狀態(tài) ( redundant ):這個狀態(tài)表示一個 Service Worker 生命周期的結(jié)束。
這里特別說明一下,進(jìn)入廢棄狀態(tài)的原因可能為這幾種:
安裝 (install) 失敗
激活 (activating) 失敗
新版本的 Service Worker 替換了它并成功激活
MDN 也列出了 Service Worker 所有支持的事件:
install:Service Worker 安裝成功后被觸發(fā)的事件,在事件處理函數(shù)中可以添加需要緩存的文件
activate:當(dāng) Service Worker 安裝完成后并進(jìn)入激活狀態(tài),會觸發(fā) activate 事件。通過監(jiān)聽 activate 事件你可以做一些預(yù)處理,如對舊版本的更新、對無用緩存的清理等。
message:Service Worker 運行于獨立 context 中,無法直接訪問當(dāng)前頁面主線程的 DOM 等信息,但是通過 postMessage API,可以實現(xiàn)他們之間的消息傳遞,這樣主線程就可以接受 Service Worker 的指令操作 DOM。
Service Worker 有幾個重要的「功能性事件」,這些功能性的事件支撐和實現(xiàn)了 Service Worker 的特性。
fetch (請求):當(dāng)瀏覽器在當(dāng)前指定的 scope 下發(fā)起請求時,會觸發(fā) fetch 事件,并得到傳有 response 參數(shù)的回調(diào)函數(shù),回調(diào)中可以做各種代理和緩存操作。
push (推送):push 事件是為推送準(zhǔn)備的。不過首先需要了解一下 Notification API 和 PUSH API。通過 PUSH API,當(dāng)訂閱了推送服務(wù)后,可以使用推送方式喚醒 Service Worker 以響應(yīng)來自系統(tǒng)消息傳遞服務(wù)的消息,即使用戶已經(jīng)關(guān)閉了頁面。
sync (后臺同步):sync 事件由 background sync (后臺同步)發(fā)出。background sync 配合 Service Worker 推出的 API,用于為 Service Worker 提供一個可以實現(xiàn)注冊和監(jiān)聽同步處理的方法。但它還不在 W3C Web API 標(biāo)準(zhǔn)中。在 Chrome 中這也只是一個實驗性功能,需要訪問 chrome://flags/#enable-experimental-web-platform-features ,開啟該功能,然后重啟生效。
有了以上 SW 的事件及 API,接下來就是實戰(zhàn)部分了。
瀏覽器支持,包括 Cache API,Promise,HTML5 fetch API。關(guān)于目前 SW 的瀏覽器支持情況,后面將有介紹。
HTTPS,可用 127.0.0.1 和 localhost 測試,但部署須在 https 協(xié)議下。
巧婦難為無米之炊,以上兩個先決條件是必須要滿足的。
if ("serviceWorker" in navigator) { window.addEventListener("load", function() { navigator.serviceWorker.register("/sw.js", {scope: "/"}).then(function(registration) { // 注冊成功 console.log("ServiceWorker registration successful with scope: ", registration.scope); }, function(err) { // 注冊失敗 :( console.log("ServiceWorker registration failed: ", err); }); }); }
其實,關(guān)鍵代碼只有一行
navigator.serviceWorker.register("/sw.js", {scope: "/"})
注意,此處有坑
Service Worker 的注冊路徑?jīng)Q定了其 scope 默認(rèn)作用域,如 SW 注冊文件的路徑為 https://www.a.com/public/sw.js 時,對應(yīng)默認(rèn) scope 是 /public/,其作用范圍如下
域名 | 是否生效 |
---|---|
www.a.com/ | 否 |
www.a.com/page/ | 否 |
www.a.com/public/ | 是 |
www.a.com/public/page… | 是 |
www.b.com/ | 否 |
www.b.com/public/ | 否 |
以上可看出,當(dāng)作用域 scope 為 /public/ 后,其作用范圍只限于本身和子域,父域和兄弟域皆無效,跨域就更免談了。
當(dāng)然,我們可以通過設(shè)置 scope 來限定自己的作用域,但是!請注意,『以下寫法是錯誤的』。
navigator.serviceWorker.register("/public/sw.js", {scope: "/"}) // 錯誤寫法 navigator.serviceWorker.register("/public/sw.js", {scope: "/page"}) // 錯誤寫法
以上寫法均會報錯
The path of the provided scope ("/") is not under the max scope allowed ("/public/"). Adjust the scope, move the Service Worker script, or use the Service-Worker-Allowed HTTP header to allow the scope.
所以,sw.js 文件最好放在根域名下
如
navigator.serviceWorker.register("/sw.js", {scope: "/page"})
當(dāng) scope 不同時,請求被監(jiān)控情況也有不同
代號 | 請求 |
---|---|
r1 | www.a.com/api |
r2 | www.a.com/page1/api |
r3 | www.a.com/page2/api |
r4 | www.a.com/static/img1.png |
r5 | www.b.com/api2 |
r6 | www.b.com/static/img2.png |
域名 | scope | 被監(jiān)控請求 |
---|---|---|
www.a.com/ | / | r1-6 |
www.a.com/ | /page1 | 無 |
www.a.com/page1 | / | r1-6 |
www.a.com/page1 | /page1 | r1-6 |
www.a.com/page1 | /page2 | 無 |
所以,scope 與被監(jiān)控請求的域并沒有什么關(guān)系,他只與站點域名有關(guān)
我們可以通過打開 Chrome 的 DevTools -> Application -> Service Workers 查看 SW 的注冊情況。
看到類如 Status: #xxxx activated and is running,即說明注冊并激活成功。
也可以通過打開 Chrome 的管理頁 chrome://inspect/#service-workers 查看
在受控頁面啟動注冊流程后,我們來看看處理 install 事件的 Service Worker 腳本。
最基本的例子是,您需要為安裝事件定義回調(diào),并處理想要緩存的文件。
self.addEventListener("install", function(event) { // Perform install steps });
在 install 回調(diào)的內(nèi)部,我們可以執(zhí)行以下步驟(當(dāng)然也可以啥也不干):
打開緩存。
緩存文件。
確認(rèn)所有需要的資產(chǎn)是否已緩存。
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); }) ); });
此處,我們以所需的緩存名稱調(diào)用 caches.open(),之后再調(diào)用 cache.addAll() 并傳入文件數(shù)組。 這是一個 promise 鏈(caches.open() 和 cache.addAll())。 event.waitUntil() 方法帶有 promise 參數(shù)并使用它來判斷安裝所花費的時間,以及安裝是否成功。
如果所有文件都成功緩存,則將安裝 Service Worker。 如有任意文件無法下載,則安裝失敗。此設(shè)計可保證 SW 啟動的正確性,但過長的資源列表也增加了安裝失敗的幾率,可根據(jù)項目情況自行定義,也可不定義。
在安裝 Service Worker 且用戶轉(zhuǎn)至其他頁面或刷新當(dāng)前頁面后,Service Worker 將開始接收 fetch 事件。下面提供了一個示例。
self.addEventListener("fetch", function(event) { event.respondWith( //匹配緩存 caches.match(event.request) .then(function(response) { //命中走觀察 if (response) { return response; } //未命中則透傳向網(wǎng)絡(luò) return fetch(event.request); } ) ); });
如果希望連續(xù)緩存新請求,可以通過處理 fetch 請求的響應(yīng)并將其添加到緩存來實現(xiàn),如下所示。
self.addEventListener("fetch", function(event) { event.respondWith( caches.match(event.request) .then(function(response) { if (response) { return response; } /** * 通過檢查,則克隆響應(yīng)。 * 這樣做的原因在于,該響應(yīng)是數(shù)據(jù)流, 因此主體只能使用一次。 * 由于我們想要返回能被瀏覽器使用的響應(yīng),并將其傳遞到緩存以供使用, * 因此需要克隆一份副本。我們將一份發(fā)送給瀏覽器,另一份則保留在緩存。 */ var fetchRequest = event.request.clone(); return fetch(fetchRequest).then( function(response) { // 只緩存成功的請求,第三方資源不緩存,當(dāng)然也可以處理緩存。 if(!response || response.status !== 200 || response.type !== "basic") { return response; } var responseToCache = response.clone(); caches.open(CACHE_NAME) .then(function(cache) { cache.put(event.request, responseToCache); }); return response; } ); }) ); });
上一部分介紹了 SW 的使用,以及自定義請求響應(yīng),實際上,fetch 的玩法有很多,但也都是大同小異。
因此,為了使 SW 更容易使用,GoogleChrome 團(tuán)隊在 Chrome Submit 2017 上首次推出的一套 Web App 靜態(tài)資源和請求結(jié)果本地存儲的解決方案 workbox。
來直接感受下 workbox 的語法
// sw.js。 SW 的注冊不變,改變的只是 `sw.js` 的寫法 importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js"); // 不同的資源使用不同的緩存策略,并存儲在不同的 storage 中 workbox.routing.registerRoute( /.(");
這里有個緩存策略,簡略介紹一下。
Stale While Revalidate
此策略會優(yōu)先匹配緩存,如果未命中,則透傳網(wǎng)絡(luò),如果命中則返回緩存響應(yīng),同時在后臺更新緩存網(wǎng)絡(luò)響應(yīng),此策略比較安全,更像是競爭策略,誰快誰響應(yīng)。
Network First
網(wǎng)絡(luò)優(yōu)先策略,如果網(wǎng)絡(luò)通暢,返回網(wǎng)絡(luò)響應(yīng)并緩存,如果離線,則返回緩存響應(yīng)
Cache First
緩存優(yōu)先策略,如果緩存匹配,返回響應(yīng),如果不匹配則透傳網(wǎng)絡(luò),并緩存「有效」響應(yīng)
Network Only
強(qiáng)制網(wǎng)絡(luò)響應(yīng),即為普通請求
Cache Only
強(qiáng)制緩存響應(yīng),無匹配則返回 404
更多關(guān)于 workbox 請前往 developers.google.com/web/tools/w…
可「安裝」
PWA 另外一個爆點就是「可安裝」,學(xué)名叫作「添加到主屏幕」,這歸功于一個配置文件 manifest.json,它給予開發(fā)者自定義圖標(biāo)、顯示名稱、啟動方式等信息并添加至桌面的能力,同時也提供 API 方便開發(fā)者管理網(wǎng)絡(luò)應(yīng)用安裝橫幅,讓用戶可以方便快捷地將站點添加到主屏幕中。
支持度
當(dāng)前 manifest.json 的標(biāo)準(zhǔn)仍屬于草案階段,Chrome、Firefox 和 Apple 都已經(jīng)實現(xiàn)了這個功能或者功能的部分,微軟正努力在 Edge 瀏覽器上實現(xiàn)。
在 caniuse 中可查到 manifest 的支持度,數(shù)據(jù)顯示 92.64% 的移動瀏覽器已經(jīng)達(dá)到了支持或者部分支持的程度,想必在不久以后,當(dāng)規(guī)范標(biāo)準(zhǔn)通過后,manifest 的支持度可以達(dá)到一個新高度。
配置
我以一個相對完整的 manifest.json 配置文件進(jìn)行講解
配置介紹
// manifest.json { /* 自定義名稱 */ "short_name": "短名稱", "name": "這是一個完整名稱", /** 自定義安裝 icon * 當(dāng)PWA添加到主屏幕時,瀏覽器會根據(jù)有效圖標(biāo)的 sizes 字段進(jìn)行選擇。 * 首先尋找與顯示密度相匹配并且尺寸調(diào)整到 48dp 屏幕密度的圖標(biāo); * 如果未找到任何圖標(biāo),則會查找與設(shè)備特性匹配度最高的圖標(biāo); * 如果匹配到的圖標(biāo)路徑錯誤,將會顯示瀏覽器默認(rèn) icon。 * * 在啟動應(yīng)用時,啟動畫面圖像會從圖標(biāo)列表中提取最接近 128dp 的圖標(biāo)進(jìn)行顯示 */ "icons": [ { "src": "path-to-images/icon-96x96.png", "type": "image/png", "sizes": "96x96" }, { "src": "path-to-images/icon-144x144.png", "type": "image/png", "sizes": "144x144" } ], /* 設(shè)置啟動網(wǎng)址 */ "start_url": "index.html", /** 設(shè)置啟動背景顏色 * 完整色值 "#0000ff" * 縮寫 "#00f" * 預(yù)設(shè)色值 "blue" * rgb "rgb(0, 0, 255)" * transparent 背景色顯示為黑色 */ "background_color": "#0000ff", /** 設(shè)置啟動顯示類型 * fullscreen 應(yīng)用的顯示界面將占滿整個屏幕 * standalone 瀏覽器相關(guān)UI(如導(dǎo)航欄、工具欄等)將會被隱藏 * minimal-ui 顯示形式與standalone類似,瀏覽器相關(guān)UI會最小化為一個按鈕,不同瀏覽器在實現(xiàn)上略有不同 * browser 瀏覽器模式,與普通網(wǎng)頁在瀏覽器中打開的顯示一致 */ "display": "fullscreen", /** 指定頁面顯示方向 * 更多配置介紹:https://lavas.baidu.com/pwa/engage-retain-users/add-to-home-screen/improved-webapp-experience#%E6%8C%87%E5%AE%9A%E9%A1%B5%E9%9D%A2%E6%98%BE%E7%A4%BA%E6%96%B9%E5%90%91 */ "orientation": "landscape", /* 設(shè)置主題顏色 */ "theme_color": "#000", /** 設(shè)置作用域 * start_url 必須在作用域內(nèi) */ "scope": "/" }
測試
配置完之后就可以在 Chrome 的 DevTools 中進(jìn)行驗證測試了。
圖片來源于 developers.google.com
可推送消息
消息推送是 App ?;顩_績效的常用手段,由于 HTTP 是一個無狀態(tài)協(xié)議,推送功能在用戶關(guān)閉了瀏覽器之后便沒了辦法,這一次 PWA 賦予了 Web 這個能力。
其中便包含了兩個技術(shù)點
推送 push:連接服務(wù)端和 SW 進(jìn)行消息傳遞
通知 notification:控制客戶端(瀏覽器)進(jìn)行消息提示
下面我們來解析一個通知體
// 消息體的 title self.addEventListener("push", event => { const title = "Credit Card"; const options = { // 主內(nèi)容 "body": "Did you make a $1,000,000 purchase at Dr. Evil...", // 視覺配置,如 icon,Badge,image 等,不同的視覺配置展示的位置也不同 // 詳情參看 https://lavas.baidu.com/pwa/engage-retain-users/notification/notification-display "icon": "images/ccard.png", // 震動設(shè)置,其中的數(shù)字以2個為一組,分別表示震動的毫秒數(shù),和不震動的毫秒數(shù) "vibrate": [200, 100, 200, 100, 200, 100, 400], // 鈴聲 "sound": "path/to/sound.mp3", // 標(biāo)簽,用于客戶端消息歸類 "tag": "request", // actions,用戶操作后會將結(jié)果反饋給瀏覽器 "actions": [ { "action": "yes", "title": "Yes", "icon": "images/yes.png" }, { "action": "no", "title": "No", "icon": "images/no.png" } ] } // 激活通知 self.registration..showNotification(title, options); }); self.addEventListener("notificationclick", event => { // Do something with the event event.notification.close(); }); self.addEventListener("notificationclose", event => { // Do something with the event });
以上的消息配置,展示的結(jié)果如下圖。
關(guān)于推送功能的更多實操不屬于本文探究的范疇,有實際需求的同學(xué)可以前往官網(wǎng)進(jìn)行了解。
傳送門>>
developers.google.com/web/fundame…
lavas.baidu.com/pwa/engage-…
PWA & 小程序
有人說「PWA 是小程序的祖宗」,不無道理,PWA 對小程序肯定存在一定的借鑒意義,但是否會擠壓 PWA 的市場?我們應(yīng)該放心,小程序的設(shè)計并不是 Web 的替代者,而是介于原生 App 和 Web 之間的存在。
小程序更傾向于輕便及時觸手可得。既沒有原生 App 的「沉重」也沒有 Web 「遲鈍」。在此得天獨厚的基礎(chǔ)之上加之以「社交流量」的加持,微信小程序的存在并非偶然。
但是,如果沒了網(wǎng)絡(luò),一樣玩不轉(zhuǎn);主流的搜索引擎并無法捕獲小程序的內(nèi)容。所以,App、Web 和小程序是相輔相成的。
另外,筆者想表達(dá)另外一個觀點
「存在即合理,合理未必長久」
在經(jīng)歷過一段痛苦的微信小程序洗禮之后,我們「欣然」接受了。奈何眾XX小程序『競相開放,爭奇斗艷,不亦樂乎』。殊不知,我等不才,竟要為了這區(qū)區(qū)語法之差異,徹夜無法停歇,然,產(chǎn)與出相比,孰輕孰重?
唉~程序員何必為難程序員~~
總結(jié)
關(guān)于 PWA 的技術(shù)早在 2 年前即已相對完整,只是由于「天朝人民太過賦予」,對支持與否未發(fā)布意見的 Apple 在天朝市場有著舉足輕重的地位,而「外圍」仿佛對 Android 機(jī)更為推崇,所以,PWA 在國內(nèi)的發(fā)展和推廣并不理想。
此時此刻,PWA 的支持度也達(dá)到了一個相對讓人滿意的水平,雖然體驗依然無法和原生 App 相提并論,但作為 App 短板的補(bǔ)丁已是綽綽有余。
所以,『架構(gòu)師』們,可以盤起來了。
以上就是 PWA 的相關(guān)知識點,希望對你有所幫助。
[1]. developers.google.com/web/fundame…
[2]. developers.google.com/web/fundame…
[3]. developers.google.com/web/tools/w…
[4].下一代 Web 應(yīng)用模型 —— Progressive Web App
[5]. lavas.baidu.com/
首發(fā):zwwill/blog#33
作者:木羽
轉(zhuǎn)載請標(biāo)明出處
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/6679.html
摘要:手機(jī)型號圖標(biāo)大小分辨率大小豎屏豎屏豎屏豎屏添加智能廣告條,頂部會自動彈出一條關(guān)于應(yīng)用下載的廣告條,下載地址就是你設(shè)置的文檔參考 字符編碼 兩種寫法等效,推薦寫第一種 語言設(shè)置 簡體中文 繁體中文 不是寫lang=zh-CN 瀏覽器內(nèi)核控制 設(shè)置IE的優(yōu)先版本為edge,若不存在則選擇當(dāng)前最高版本,含有谷歌內(nèi)核的會選擇谷歌,如果設(shè)置多條的話只有第一條生效 針對多內(nèi)核瀏覽器設(shè)置優(yōu)...
摘要:熱門文章我在淘寶做前端的這三年紅了櫻桃,綠了芭蕉。文章將在淘寶的三年時光折射為入職職業(yè)規(guī)劃招聘晉升離職等與我們息息相關(guān)的經(jīng)驗分享,值得品讀。 showImg(https://segmentfault.com/img/remote/1460000018739018?w=1790&h=886); 【Alibaba-TXD 前端小報】- 熱門前端技術(shù)快報,聚焦業(yè)界新視界;不知不覺 2019 ...
摘要:熱門文章我在淘寶做前端的這三年紅了櫻桃,綠了芭蕉。文章將在淘寶的三年時光折射為入職職業(yè)規(guī)劃招聘晉升離職等與我們息息相關(guān)的經(jīng)驗分享,值得品讀。 showImg(https://segmentfault.com/img/remote/1460000018739018?w=1790&h=886); 【Alibaba-TXD 前端小報】- 熱門前端技術(shù)快報,聚焦業(yè)界新視界;不知不覺 2019 ...
摘要:原生越來越好了,如果是簡單的頁面,沒必要引入一個龐大的,尤其在手機(jī)端,對速度流量敏感的地方,另外最好自己簡單封裝一些常用的函數(shù),比如等。。。這時候它就不知所措了,只好待在中間。參考前端界有哪些越早知道越好的小技巧小知識 1.You Might Not Need jQuery 不用jQuery,原生js如何實現(xiàn),可以參考這里:You Might Not Need jQuery 。原生js...
閱讀 2577·2021-09-06 15:02
閱讀 3206·2021-09-02 10:18
閱讀 2828·2019-08-30 15:44
閱讀 690·2019-08-30 15:43
閱讀 1955·2019-08-30 14:08
閱讀 2764·2019-08-30 13:16
閱讀 1404·2019-08-26 13:52
閱讀 935·2019-08-26 12:21