成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

構建 Web 應用之 Service Worker 初探

voidking / 2969人閱讀

摘要:誕生之初,是單線程的。當接收到服務端的響應之后,便通過回調函數(shù)執(zhí)行之后的操作。沖鋒基于事件驅動。擁有攔截請求消息推送靜默更新地理圍欄等服務??刂茣r處于兩種狀態(tài)之一終止以節(jié)省內(nèi)存監(jiān)聽獲取和消息事件。支持的所有事件五銷毀瀏覽器決定是否銷毀。

這次體驗一種新的博客風格,我們長話短說,針針見“血”。

備馬

在深入 Service Worker 之前,我們需要快速回顧如下基礎。

誕生之初,JavaScript 是單線程的。

進程有私有的虛擬地址空間、代碼、數(shù)據(jù)和其它系統(tǒng)資源,進程申請創(chuàng)建和使用的系統(tǒng)資源會隨其終止而銷毀。線程運行在進程之中,系統(tǒng)創(chuàng)建進程之后就開始啟動執(zhí)行進程的主線程,并隨主線程的退出而終止。

JavaScript 作為瀏覽器腳本語言,為方便準確無誤的操作 DOM,誕生之初便采用了單線程的方式。舉個例子,若多線程同時分別刪除和修改同一個 DOM,我們很難預知其執(zhí)行結果。

但單線程中,必須通過異步和回調來優(yōu)化耗時操作。

我們在網(wǎng)頁上提交一個表單,并不希望在提交后頁面卡頓,一直等待服務端返回的提交結果。這時我們需要能在單線程中發(fā)送異步請求,點擊提交表單后可以先在頁面進行其他操作。

Ajax 讓我們可以向后端發(fā)送異步請求,同時不影響用戶在界面中繼續(xù)操作。當 Ajax 接收到服務端的響應之后,便通過回調函數(shù)執(zhí)行之后的操作。一個典型的異步 Ajax 實戰(zhàn)場景如下:

// 生成可發(fā)送同步/異步請求的 XMLHttpRequest 對象實例
var oReq = new XMLHttpRequest();
// open 方法初始化請求方法、地址,第三個參數(shù) true 聲明進行異步請求
oReq.open("GET", "http://www.jianshu.com/", true);
// 請求的整個過程中有五種狀態(tài),且同一時刻只能存在一種狀態(tài):
// 1. 未打開
// 2. 未發(fā)送
// 3. 已獲取響應體
// 4. 正在下載響應體
// 5. 請求完成
// 當請求狀態(tài)發(fā)生改變時,觸發(fā) onreadystatechange 會被調用
oReq.onreadystatechange = function (oEvent) {
  // 如果已經(jīng)開始下載響應體了
  if (oReq.readyState === 4) {
    // 如果響應體成功下載,并且服務端返回 200 狀態(tài)碼
    if (oReq.status === 200) {
      // 打印響應信息
      console.log(oReq.responseText);
    } else {
      console.log("Error", oReq.statusText);
    }
  }
};
// send 方法發(fā)送請求,由于此請求是異步的,該方法立刻返回
oReq.send(null);

當我們的多個請求需要依賴于上一個請求的服務端響應時,回調函數(shù)中 Ajax 的層級逐步提高,可維護性極度下降,這就是回調地獄。

I Promise U that I`ll Marry U!!!

Promise 由 ES6 標準原生支持。正如題名,Promise 作出諾言,也要因此承擔成功(fulfilled)或失敗(rejected)的結果,以便解決回調地獄問題:

// 生成一個 Promise 實例,傳入有特定的兩個參數(shù)的匿名函數(shù)
// Promise 初始狀態(tài)是 pending
// resolve 被調用時,將 Promise 狀態(tài)改為成功(fulfilled)
// reject 被調用時,將 Promise 狀態(tài)改為失敗(rejected)
// 該匿名函數(shù)拋出錯誤時,Promise 狀態(tài)為失敗(rejected)
var a = new Promise(function(resolve, reject) {
  // setTimeout() 模擬異步請求,成功后執(zhí)行 resolve() 方法
  setTimeout(function() {
      resolve("1")
  }, 2000)
})

a.then(function(val){
    // then() 有兩個函數(shù)作為參數(shù),onfulfilled 和 onrejected
    // 當 Promise 狀態(tài)為 fulfilled 時,調用 then 的 onfulfilled 方法
    // 當 Promise 狀態(tài)為 rejected 時,調用 then 的 onrejected 方法
    console.log(val)
    // then() 方法返回 Promise 對象實例,所以可被鏈式調用
    return new Promise(function(resolve, reject) {
      setTimeout(function() {
          resolve("2")
      }, 2000)
    })
  })
  .then(function(val) {
    // 鏈式調用的第二個環(huán)節(jié),處理上一個環(huán)節(jié)返回的 Promise 對象
    console.log(val)
  })

Promise 對象的生命周期如下圖。

除了異步編程,我們還可以有 Web Worker。

通過異步編程,我們的頁面可以邊響應用戶的下一步操作邊等待服務端的回應,不再擁有阻塞感,但 JavaScript 的單線程問題并沒有得到相應的解決。通過 HTML 5 標準支持的 Web Worker,我們可以為 JavaScript 創(chuàng)建運行在后臺的額外線程,并被多個頁面共享。

在一個簡單的 Web Worker 實例中,main.js 和 task.js 的源碼如下。

// main.js
// 實例化 Worker 對象,其實質為新創(chuàng)建的工作線程在主線程的引用
var worker = new Worker("task.js")
// postMessage 方法與新創(chuàng)建的工作線程通信
worker.postMessage({
        id:1,
        msg:"Hello World"
});
// 當 Worker 線程返回數(shù)據(jù)時,onmessage 回調函數(shù)執(zhí)行
worker.onmessage = function(message) {
    var data = message.data;
    console.log(JSON.stringify(data))
    // terminate 方法終止 worker 線程的運行
    worker.terminate()
};
// 當 Worker 線程出錯時,onerror 回調函數(shù)執(zhí)行
// error 參數(shù)中封裝了錯誤對象的文件名、出錯行號和具體錯誤信息
worker.onerror = function(error) {
    console.log(error.filename, error.lineno, error.message)
}
// task.js
onmessage = function(message) {
    var data = message.data
    data.msg = "Hi from task.js"
    postMessage(data)
}

在 Chrome 瀏覽器里,以上代碼必須運行在 Web 容器如 Apache 中。同時,WebKit 內(nèi)核加載并執(zhí)行 Worker 線程的流程如下圖所示。

上述知識點的詳盡博客盡請期待,您可以先查閱其它資料進行補充。
沖鋒

Service Worker 基于 Web Worker 事件驅動。

Service Worker 同樣可以在瀏覽器后臺掛起新線程,來緩解 JavaScript 的單線程問題。并且,我們可以用 Service Worker 攔截網(wǎng)絡請求進行本地緩存或請求轉發(fā),相當于充當服務端與瀏覽器、瀏覽器與 Web 應用程序之間的代理服務器。

Service Worker 帶來了速度,極大的提高了用戶體驗。

Service Worker 可有效加快重復訪問網(wǎng)絡應用的速度。

擁有攔截請求、消息推送、靜默更新、地理圍欄等服務。

可以在客戶端通過 indexedDB API 保存持久化信息。

Service Worker 大量使用 Promise 對象。

因為通常 Service Worker 會等待響應后繼續(xù),并根據(jù)響應返回一個成功或者失敗的操作。Promise 非常適合這種場景。

零、Service Worker 的生命周期。

所謂生命周期,包括 Service Worker 的注冊、安裝、激活、控制和銷毀時的全部過程。我們需要對 Service Worker 的生命周期有所了解。

先決條件:

瀏覽器支持:Service Worker。

在 localhost 域或 HTTPS 域下運行:介于我們能夠通過使用 Service Worker 劫持連接、編撰以及過濾響應來進行權限較高的操作。

注冊:注冊過程獨立于網(wǎng)頁,先在頁面執(zhí)行注冊,之后在瀏覽器后臺啟動安裝步驟。

安裝:通常需要緩存某些靜態(tài)資源。當所有文件已成功緩存,則安裝完畢。如果任何文件下載失敗或緩存失敗,則安裝失敗,無法激活。

激活:管理就緩存的絕佳機會。激活后它將會對作用域頁面實時控制,不過首次注冊該服務工作線程的頁面需要再次加載才會受其控制。

控制時:處于兩種狀態(tài)之一:

①、終止以節(jié)省內(nèi)存;

②、監(jiān)聽獲取 fetch 和消息 message 事件。

銷毀:由瀏覽器決定,因此盡量不要留存全局變量。

一、注冊 Service Worker。

當瀏覽器對 Service Worker 提供原生支持時,我們便可以在頁面加載后注冊指定的 JavaScript 文件,并運行在后臺線程之中,以下代碼是這一過程的實例。




  ServiceWorker


  

Hello World!

這里通過 php 內(nèi)置命令監(jiān)聽項目目錄,便能看到 Service Worker 注冊成功。同時,在 Chrome 瀏覽器里,可以訪問 chrome://inspect/#service-workerschrome://serviceworker-internals/ 來檢查 Service Worker 是否已經(jīng)啟用。

二、安裝 Service Worker。

安裝階段,我們可以執(zhí)行任何任務。這里我們逐步打開緩存、緩存文件和確認所有需要的資產(chǎn)是否緩存。ServiceWorker.js 中的實例安裝代碼如下:

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);
      })
  );
});

這要求我們在與項目根目錄下建立 main.jsmain.css 空文件。我們可以在 Chrome 開發(fā)者工具里的“Application”菜單的“Cache Storage”中看到相應的緩存。并且在圖中的“Service Workers”選項卡中看到正在運行的 Service Workers。

且從上面的代碼可以看到,通過 Service Worker 對象加載的文件擁有全局變量 caches 等,并且 self 關鍵字指向這個對象本身。cache 使我們可以存儲網(wǎng)絡響應發(fā)來的資源,并且根據(jù)它們的請求來生成 key。這個 API 和瀏覽器的標準的緩存工作原理很相似,且會持久存在,直到我們釋放主動空間——我們擁有全部的控制權。

三、激活 Service Worker。

當 Service Worker 安裝成功后,便被激活,這時可實時控制作用域中的所有網(wǎng)站,進行緩存文件等操作。不過首次使用 Service Worker 的頁面需要再次加載才會受其控制。

四、控制 Service Worker

以下列舉幾個常見的 Service Worker 應用場景。

1. 文件緩存

self.addEventListener("fetch", function(event) {
  event.respondWith(
      // 以下方法檢視請求,并從服務工作線程所創(chuàng)建的任何緩存中查找緩存的結果。
    caches.match(event.request)
      .then(function(response) {
          console.log(event.request)
          console.log(caches)
        // 如果發(fā)現(xiàn)匹配的響應,則返回緩存的值
        if (response) {
          return response;
        }
        return fetch(event.request);
      }
    )
  );
});

通過上述文件緩存過程,我們可以告訴 Service Worker 如何使用這些緩存文件,并通過 fetch 事件來捕獲。fetch 事件只會在瀏覽器準備請求 Service Worker 控制的資源時才會被觸發(fā)。這些資源包括了指定的 scope 內(nèi)的文檔,和這些文檔內(nèi)引用的其他任何資源。

2. 多頁面?zhèn)鬟f消息

我們可以打開多個 https://nzv3tos3n.qnssl.com/m... 測試頁面來進行測試,效果如下。

其中,index.js 源碼為:

(function () {
    if (navigator.serviceWorker) {
        // 獲取頁面 DOM 元素
        var msgIpt = document.getElementById("ipt"),
            showArea = document.getElementById("show"),
            sendBtn = document.getElementById("sendBtn");

        navigator.serviceWorker.register("service-worker3.js");

        navigator.serviceWorker.addEventListener("message", function (event) {
            // 接受數(shù)據(jù),并填充在 DOM 中
            showArea.innerHTML = showArea.innerHTML + ("
  • " + event.data.message + "
  • "); }); sendBtn.addEventListener("click", function () { // 綁定點擊事件,點擊后發(fā)送數(shù)據(jù) navigator.serviceWorker.controller.postMessage(msgIpt.value); msgIpt.value = ""; }); } })();

    3. 更新 Service Worker

    每次用戶導航至使用 Service Worker 的站點時,瀏覽器會嘗試在后臺重新下載該腳本文件。這時新的 Service Worker 將會在后臺安裝,并在第二次訪問時獲取控制權,為了不與新的 Service Worker 緩存的文件沖突,我們可以使用類似 caches.open("v2") 語句來創(chuàng)建新的緩存目錄。

    this.addEventListener("install", function(event) {
      event.waitUntil(
        // 創(chuàng)建新的緩存目錄,并指定
        caches.open("v2").then(function(cache) {
          return cache.addAll([
            "/sw-test/",
            "/sw-test/index.html",
            …
          ]);
        });
      );
    });

    當新的 Service Worker 激活,記得刪除 v1 緩存目錄,代碼如下。

    this.addEventListener("activate", function(event) {
      // 聲明緩存白名單,該名單內(nèi)的緩存目錄不會被生成
      var cacheWhitelist = ["v2"];
      event.waitUntil(
        // 傳給 waitUntil() 的 promise 會阻塞其他的事件,直到它完成
        // 確保清理操作會在第一次 fetch 事件之前完成
        caches.keys().then(function(keyList) {
          return Promise.all(keyList.map(function(key) {
            if (cacheWhitelist.indexOf(key) === -1) {
              return caches.delete(key);
            }
          }));
        })
      );
    });

    4. 預緩存

    Service Worker 也可以在后臺主動發(fā)送請求,優(yōu)化用戶體驗,圖片來源于《餓了么的 PWA 升級實踐》。

    5. Service Worker 支持的所有事件

    五、銷毀 Service Worker

    瀏覽器決定是否銷毀 Service Worker。在無痕瀏覽中,當頁面關閉時相應的 Service Worker 會被銷毀,因此盡量不要在代碼中留存全局變量??梢栽L問 chrome://inspect/#service-workers和 chrome://serviceworker-internals/ 來檢查 Service Worker 是否已經(jīng)停用。

    小結

    困擾 Web 用戶多年的難題——丟失網(wǎng)絡連接,從 APPCache 到 Service Worker,解決辦法一直在完善。Service Worker 開啟的服務工作線程,對如何步入 Web 應用開發(fā)之旅,提供了很棒的切入角度。

    那么,如何從本文開始,更好的學習 Service Worker?結合更多其它技術博客與 Service Worker 的 API 文檔會更好。本文圖片素材、寫作思路多取源于此。

    接口列表
    Cache CacheStorage
    Client Clients
    ExtendableEvent FetchEvent
    InstallEvent Navigator.serviceWorker
    NotificationEvent PeriodicSyncEvent
    PeriodicSyncManager PeriodicSyncRegistration
    ServiceWorker ServiceWorkerContainer
    ServiceWorkerGlobalScope ServiceWorkerRegistration
    SyncEvent SyncManager
    SyncRegistration WindowClient
    相關代碼

    本文所有關于 Web Worker、Service Worker 的代碼,持續(xù)更新在我的 gist 中:

    https://gist.github.com/hyler...

    Hello,我是韓亦樂,現(xiàn)任本科軟工男一枚。軟件工程專業(yè)的一路學習中,我有很多感悟,也享受持續(xù)分享的過程。如果想了解更多或能及時收到我的最新文章,歡迎訂閱我的個人微信號:韓亦樂。我的簡書個人主頁中,有我的訂閱號二維碼和 Github 主頁地址;[我的知乎主頁]中也會堅持產(chǎn)出,歡迎關注。

    本文內(nèi)部編號經(jīng)由我的 Github 相關倉庫統(tǒng)一管理;本文可能發(fā)布在多個平臺但僅在上述倉庫中長期維護;本文同時采用【知識共享署名-非商業(yè)性使用-禁止演繹 4.0 國際許可協(xié)議】進行許可。

    文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

    轉載請注明本文地址:http://systransis.cn/yun/88793.html

    相關文章

    • PWA初探整理

      摘要:站點在同一瀏覽器中被訪問至少兩次,兩次訪問間隔至少為分鐘。詢問授權發(fā)送給后端存儲服務端向發(fā)送消息,同時帶上根據(jù)再下發(fā)給對應的瀏覽器觸發(fā)的事件后續(xù)處理參考使用發(fā)送推送 關鍵特性 Web App Manifest – 在主屏幕添加app圖標,定義手機標題欄顏色之類 App Shell – 先顯示APP的主結構,再填充主數(shù)據(jù),更快顯示更好體驗 Service Worker - 緩存,離線開...

      jifei 評論0 收藏0
    • 初探IndexedDB

      背景 隨著前端技術日新月異地快速發(fā)展,web應用功能和體驗也逐漸發(fā)展到可以和原生應用媲美的程度,前端緩存技術的應用對這起到了不可磨滅的貢獻,因此想一探前端的緩存技術,這篇文章主要會介紹在日常開發(fā)中比較少接觸的IndexedDB IndexedDB 什么是IndexedDB IndexedDB簡單理解就是前端數(shù)據(jù)庫,提供了一種在用戶瀏覽器中持久存儲數(shù)據(jù)的方法,但是和前端關系型數(shù)據(jù)不同的是,Index...

      jsyzchen 評論0 收藏0
    • 前端每周清單半年盤點 PWA 篇

      摘要:前端每周清單專注前端領域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點分為新聞熱點開發(fā)教程工程實踐深度閱讀開源項目巔峰人生等欄目。 前端每周清單專注前端領域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點;分為新聞熱點、開發(fā)教程、工程實踐、深度閱讀、開源項目、巔峰人生等欄目。歡迎關注【前端之巔】微信公眾號(ID:frontshow),及時獲取前端每周清單;本文則是對于...

      崔曉明 評論0 收藏0

    發(fā)表評論

    0條評論

    最新活動
    閱讀需要支付1元查看
    <