国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Service Worker 學習筆記

mj / 3371人閱讀

摘要:學習筆記挺有意思的,前段時間看了相關的資料,自己動手調了調,記錄一下學習過程。在端通過監聽事件就可以監聽到主線程的,在的里即可找到主線程傳過來的,之后就可以用來向主線程發送信息了。這是一個很簡單的,完成在兩個不同的中通信的功能。

Service Worker 學習筆記

Service Worker挺有意思的,前段時間看了相關的資料,自己動手調了調demo,記錄一下學習過程。文中不僅會介紹Service Worker的使用,對fetchpushcache等Service Worker配套的API都會涉及,畢竟Service Worker與這些API配合使用才能發揮出真正的威力

Chrome對Service Worker的開發者支持較好,Dev tools里可以簡單的調試,Firefox還未提供調試用的工具,但是對API的支持更好。建議開發和測試的話,在Chrome上進行

文中有把Service Worker簡寫SW,不要覺得奇怪~

Service Worker

Service workers essentially act as proxy servers that sit between web applications, and the browser and network (when available). They are intended to (amongst other things) enable the creation of effective offline experiences, intercepting network requests and taking appropriate action based on whether the network is available and updated assets reside on the server. They will also allow access to push notifications and background sync APIs.

Lifecycle

一個ServiceWorker從被加載到生效,有這么幾個生命周期:

Installing 這個階段可以監聽install事件,并使用event.waitUtil來做Install完成前的準備,比如cache一些數據之類的,另外還有self.skipWaiting在serviceworker被跳過install過程時觸發

> for example by creating a cache using the built in storage API, and placing assets inside it that you"ll want for running your app offline.

Installed 加載完成,等待被激活,也就是新的serverworker替換舊的

Activating 也可以使用event.waitUtil事件,和self.clients.clainm

> If there is an **existing** service worker available, the new version is installed in the background, but not yet **activated** — at this point it is called the worker in waiting. **It is only activated when there are no longer any pages loaded that are still using the old service worker**. As soon as there are no more such pages still loaded, the new service worker activates (becoming the active worker).

**這說明serviceWorker被替換是有條件的,即使有新的serviceworker,也得等舊的沒有被使用才能替換**。最明顯的體現是,刷新頁面并不一定能加載到新聞serviceworker

Activated 文章上的解釋是the service worker can now handle functional events

Redundant 被替換,即被銷毀

Fetch

fetch是新的Ajax標準接口,已經有很多瀏覽器原生支持了,用來代替繁瑣的XMLHttpRequestjQuery.ajax再好不過了。對于還未支持的瀏覽器,可以用isomorphic-fetch polyfill。

fetch的API很簡潔,這篇文檔講的很清晰。下面記錄一下之前被我忽略的2個API

Response

Response 寫個fetch的栗子

fetch("/style.css")
        // 這里的response,就是一個Response實例
    .then(response => response.text())
    .then(text => {
        console.log(text);
    });

Response的API,列幾個比較常用的:

Response.clone() Creates a clone of a Response object. 這個經常用在cache直接緩存返回結果的場景

Body.blob() 這里寫的是Body,其實調用接口還是用response,這里取Blob數據的數據流。MDN是這么說的:

> Response implements Body, so it also has the following methods available to it:

Body.json()

Body.text()

Body.formData() Takes a Response stream and reads it to completion. It returns a promise that resolves with a FormData object.

Request

Request應該不會多帶帶new出來使用,因為很多Request相關的參數,在Request的實例中都是只讀的,而真正可以配置Request屬性的地方,是fetch的第二個參數:

// fetch的第一個參數是URI路徑,第二個參數則是生成Request的配置,
// 而如果直接傳給fetch一個request對象,其實只有URI是可配置的,
// 因為其他的配置如headers都是readonly,不能直接從Request處配置
let request = new Request("./style.css");

request.method = "POST"; // Uncaught TypeError: Cannot set property method of # which has only a getter

fetch(request).then(response => response.text())
    .then(text => {
        console.log(text);
    });
Cache

Cache是Service Worker衍生出來的API,配合Service Worker實現對資源請求的緩存。

有意思的是cache并不直接緩存字符串(想想localstorage),而是直接緩存資源請求(css、js、html等)。cache也是key-value形式,一般來說key就是request,value就是response

API

caches.open(cacheName) 打開一個cache,cachesglobal對象,返回一個帶有cache返回值的Promise

cache.keys() 遍歷cache中所有鍵,得到value的集合

caches.open("v1").then(cache => {
    // responses為value的數組
    cache.keys().then(responses => {
        responses.forEach((res, index) => {
            console.log(res);
        });
    });
});

cache.match(Request|url) 在cache中匹配傳入的request,返回Promisecache.matchAll只有第一個參數與match不同,需要一個request的數組,當然返回的結果也是response的數組

cache.add(Request|url) 并不是單純的add,因為傳入的是request或者url,在cache.add內部會自動去調用fetch取回request的請求結果,然后才是把response存入cache;cache.addAll類似,通常在sw install的時候用cache.addAll把所有需要緩存的文件都請求一遍

cache.put(Request, Response) 這個相當于cache.add的第二步,即fetch到response后存入cache

cache.delete(Request|url) 刪除緩存

Tips

Note: Cache.put, Cache.add, and Cache.addAll only allow GET requests to be stored in the cache.

As of Chrome 46, the Cache API will only store requests from secure origins, meaning those served over HTTPS.

Service Worker通信

Service Worker是worker的一種,跟Web Worker一樣,不在瀏覽器的主線程里運行,因而和Web Worker一樣,有跟主線程通信的能力。

postMessage

window.postMessage(message, target[, transfer])這個API之前也用過,在iframe之間通信(onmessage接收信息)。簡單記下參數:

message 可以是字符串,或者是JSON序列化后的字符串,在接收端保存在event.data

target 需要傳輸的URL域,具體看API文檔

transfer 用mdn的說法,是一個transferable的對象,比如MessagePortArrayBuffer

另外說明一點,postMessage的調用者是被push數據一方的引用,即我要向sw post數據,就需要sw的引用

注意,上面的postMessage是在document中使用的。在sw的context里使用略有不同:沒有target參數具體看這個API文檔

在sw中與主線程通信

先看個栗子:

// main thread
if (serviceWorker) {
    // 創建信道
        var channel = new MessageChannel();
        // port1留給自己
        channel.port1.onmessage = e => {
            console.log("main thread receive message...");
            console.log(e);
        }

    // port2給對方
        serviceWorker.postMessage("hello world!", [channel.port2]);
        serviceWorker.addEventListener("statechange", function (e) {
            // logState(e.target.state);
        });
    }
    
// sw
self.addEventListener("message", ev => {
    console.log("sw receive message..");
    console.log(ev);
    // 取main thread傳來的port2
    ev.ports[0].postMessage("Hi, hello too");
});

在sw里需要傳遞MessagePort,這個是由MessageChannel生成的通信的兩端,在己方的一端為channel.port1,使用channel.port1.onmessage即可監聽從另一端返回的信息。而需要在postMessage里傳的是channel.port2,給另一端postMessage使用。在sw端通過監聽message事件就可以監聽到主線程的postMessage,在messageevent.ports[0]里即可找到主線程傳過來的port,之后就可以用event.ports[0].postMessage來向主線程發送信息了。

MessageChannel

這里用到了MessageChannel。這是一個很簡單的APi,完成在兩個不同的cotext中通信的功能。

在上面已經提到了,MessageChannel在一端創建,然后用channel.port1.onmesssage監聽另一端post的message,而將channel.port2通過postMessage的第二個參數(transfer)傳給另一端,讓另一端也能用MessagePort做同樣的操作。

需要注意的是channel的port1和port2的區別:port1是new MessageChannel的一方需要使用的,port2是另一方使用的

Push API

如果說fetch事件是sw攔截客戶端請求的能力,那么push事件就是sw攔截服務端“請求”的能力。這里的“請求”打了引號,你可以把Push當成WebSocket,也就是服務端可以主動推送消息到客戶端。

與WebSocket不同的是,服務端的消息在到達客戶端之前會被sw攔截,要不要給瀏覽器,給什么,可以在sw里控制,這就是Push API的作用。

push-api-demo

MDN上有個push-api-demo,是個簡易聊天器。具體搭建的方法在這個repo上有,不再贅述。因為有些Push API只有Firefox Nightly版本支持,所以demo也只能跑在這個瀏覽器上,我還沒下好,沒跑起來,等明天看吧~

記幾個Push API:

ServiceWorkerRegistration.showNotification(title, options) 這個可以理解成alert的升級版,網頁版的wechat的通知就是這個。

Notification.requestPermission() 提示用戶是否允許瀏覽器通知

PushManager Push API的核心對象,注冊Push API從這里開始,放在 ServiceWorkerRegistration

PushManager.subscribe 返回一個帶有PushSubscription的Promise,通過PushSubscription對象才能生成公鑰(PushSubscription.getKey(),這個方法只有firefox有,這也是chrome不能執行的原因),獲取endpoint

PushManager.getSubscription() 獲取當前注冊好的PushSubscription對象

atob()btob() 意外撿到兩個API,用于瀏覽器編碼、解碼base64

還是看個栗子:

// 瀏覽器端的main.js, 代碼來自push-api-demo
navigator.serviceWorker.ready.then(function(reg) {
    // 注冊push
        reg.pushManager.subscribe({userVisibleOnly: true})
           // 得到PushSubscription對象
          .then(function(subscription) {
            // The subscription was successful
            isPushEnabled = true;
            subBtn.textContent = "Unsubscribe from Push Messaging";
            subBtn.disabled = false;
            
            // Update status to subscribe current user on server, and to let
            // other users know this user has subscribed
            var endpoint = subscription.endpoint;
            // 生成公鑰
            var key = subscription.getKey("p256dh");
            // 這一步是個ajax,把公鑰和endpoint傳給server,因為是https所以不怕公鑰泄露
            updateStatus(endpoint,key,"subscribe");
          })
});
    
// 服務端 server.js,接收并存下公鑰、endpoint
...
} else if(obj.statusType === "subscribe") {
// bodyArray里是ajax傳上來的key和endpoint
    fs.appendFile("endpoint.txt", bodyArray + "
", function (err) {
      if (err) throw err;
      fs.readFile("endpoint.txt", function (err, buffer) {
        var string = buffer.toString();
        var array = string.split("
");
        for(i = 0; i < (array.length-1); i++) {
          var subscriber = array[i].split(",");
          webPush.sendNotification(subscriber[2], 200, obj.key, JSON.stringify({
            action: "subscribe",
            name: subscriber[1]
          }));
        };
      });
    });
  }
  ...
  
  // 還是服務端 server.js,推送信息到service worker
  if(obj.statusType === "chatMsg") {
      // 取出客戶端傳來的公鑰和endpoint
    fs.readFile("endpoint.txt", function (err, buffer) {
      var string = buffer.toString();
      var array = string.split("
");
      for(i = 0; i < (array.length-1); i++) {
        var subscriber = array[i].split(",");
     // 這里用了web-push這個node的庫,sendNotification里有key,說明對信息加密了
        webPush.sendNotification(subscriber[2], 200, obj.key, JSON.stringify({
          action: "chatMsg",
          name: obj.name,
          msg: obj.msg
        }));
      };
    });
  }
Client端

進入頁面后先注冊ServiceWorker,然后subscribe PushManager,把公鑰endpoint傳給Server端(ajax)保存下來,便于之后的通信(都是加密的)

然后創建一個MessageChannelServiceWorker通信

準備工作到這里就做完了。Client與Server端的通信還是ajax,聊天室嘛就是傳用戶發送的消息。ServiceWorker去監聽push事件接住Server端push來的數據,在這個demo里都是Server端接到Client的ajax請求的響應,當然也可以又Server端主動發起一個push。當同時有兩個以上的Client都與這個Server通信,那么這幾個Client能看到所有與Server的消息,這才是聊天室嘛,不過要驗證至少需要兩臺機器

Server端

一個HTTPS服務,加了Web-Push這個module,這里面肯定有用公鑰和endpoint給push信息加密的功能。webPush.sendNotification這個API能把Server端的push消息廣播到所有的Client端

Web-push這個庫還得看看

MDN Demo:sw-test

MDN上有一個完整的使用Service Worker的Demo,一個簡易的聊天室,可以自己玩玩兒。

這個demo的思路是:installfetch需要緩存的文件,用cache.addAll緩存到cacheStorage里。在fetch事件觸發時,先cache.match這些緩存,若存在則直接返回,若不存在則用fetch抓這個request,然后在cache.put進緩存。

調試ServiceWorker Dev tools

Chrome has chrome://inspect/#service-workers, which shows current service worker activity and storage on a device, and chrome://serviceworker-internals, which shows more detail and allows you to start/stop/debug the worker process. In the future they will have throttling/offline modes to simulate bad or non-existent connections, which will be a really good thing.

最新的Chrome版本,Dev toolsResource選項卡里已經添加了Service Workers,可以查看當前頁面是否有使用Service Worker,和它當前的生命周期

卸載上一個activated的service worker的方法

service worker很頑強,一個新的service worker install之后不能直接active,需要等到所有使用這個service worker的頁面都卸載之后能替換,不利于調試。今天試出來一個100%能卸載的方法

chrome://inspect/#service-workers中terminate相應的service worker

chrome://serviceworker-internals/中unregister相應的service worker

關閉調試頁面,再打開

調試service worker可以在chrome://inspect/#service-workers里inspect相應的Devtool

Tricks

如果在緩存中找不到對應的資源,把攔截的請求發回原來的流程

If a match wasn’t found in the cache, you could tell the browser to simply fetch the default network request for that resource, to get the new resource from the network if it is available:

fetch(event.request)

復制response的返回結果,下次直接從cache里取出來用

   this.addEventListener("fetch", function(event) {
     event.respondWith(
       caches.match(event.request).catch(function() {
         return fetch(event.request).then(function(response) {
           return caches.open("v1").then(function(cache) {
             cache.put(event.request, response.clone());
             return response;
           });  
         });
       })
     );

cache未命中且網絡不可用的情況,這里Promise用了兩次catch,第一次還報錯的話第二次catch才會執行

   this.addEventListener("fetch", function(event) {
     event.respondWith(
       caches.match(event.request).catch(function() {
         return fetch(event.request).then(function(response) {
           return caches.open("v1").then(function(cache) {
             cache.put(event.request, response.clone());
             return response;
           });  
         });
       }).catch(function() {
         return caches.match("/sw-test/gallery/myLittleVader.jpg");
       })
     );

activated之前清除不需要的緩存

this.addEventListener("activate", function(event) {
  var cacheWhitelist = ["v2"];
    
  event.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (cacheWhitelist.indexOf(key) === -1) {
          return caches.delete(key);
        }
      }));
    })
  );
});

偽造Response

// service-worker.js
self.addEventListener("fetch", ev => {
    var reqUrl = ev.request.url;
    console.log("hijack request: " + reqUrl);
    console.log(ev.request);
    
    // 若是text.css的請求被攔截,返回偽造信息
    if (reqUrl.indexOf("test.css") > -1) {
        console.log("hijack text.css");
        ev.respondWith(
            new Response("hahah", {
                headers: {"Content-Type": "text/css"}
            })
        );
    }
    // 繼續請求
    else {
        ev.respondWith(fetch(ev.request));
    }
});
// app.js
window.onload = () => {
    // 請求test.css
    fetch("/service-worker-demo/test.css")
    .then(response => {
        return response.text();
    })
    .then(text => {
        console.log("text.css: " + text); // 在service worker install時返回真實的文本,在sw active時返回hahah,即偽造的文本
        return text;
    });

## 未解之謎

1. `serviceworker.register(url, { scope: "xxx" })`,這里的`scope`似乎沒用。在這個scope上級的靜態資源請求也會被`fetch`攔截,在`HTTPS`上也無效,可以看看[這個demo](https://ydss.github.io/service-worker-demo/)



## Reference

- [Using Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers)
- [Service Worker API](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
- [Using the Push API](https://developer.mozilla.org/en-US/docs/Web/API/Push_API/Using_the_Push_API)
- [PushManager](https://developer.mozilla.org/en-US/docs/Web/API/PushManager)
- [Notifications API](https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API)
- [Service Worker MDN demo](https://github.com/mdn/sw-test/)
- [當前端也擁有 Server 的能力](http://www.barretlee.com/blog/2016/02/16/when-fe-has-the-power-of-server)

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

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/78987.html

相關文章

  • Service Worker實現離線應用

    摘要:同時抓取一個請求及其響應,并將其添加到給定的。返回一個對象,的結果是對象值組成的數組。代碼以下是一個實現離線應用的這個是一個簡陋的離線應用,會緩存所有靜態資源請求,即使你修改了和文件,刷新頁面還是沒有變化。 開始有興趣了解Service Worker,是因為學習react時使用create-react-app創建的應用,src下面會有一個registerServiceWorker.js...

    jindong 評論0 收藏0
  • Service Worker實現離線應用

    摘要:同時抓取一個請求及其響應,并將其添加到給定的。返回一個對象,的結果是對象值組成的數組。代碼以下是一個實現離線應用的這個是一個簡陋的離線應用,會緩存所有靜態資源請求,即使你修改了和文件,刷新頁面還是沒有變化。 開始有興趣了解Service Worker,是因為學習react時使用create-react-app創建的應用,src下面會有一個registerServiceWorker.js...

    2json 評論0 收藏0

發表評論

0條評論

mj

|高級講師

TA的文章

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