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

資訊專(zhuān)欄INFORMATION COLUMN

Service Worker 學(xué)習(xí)筆記

mj / 3372人閱讀

摘要:學(xué)習(xí)筆記挺有意思的,前段時(shí)間看了相關(guān)的資料,自己動(dòng)手調(diào)了調(diào),記錄一下學(xué)習(xí)過(guò)程。在端通過(guò)監(jiān)聽(tīng)事件就可以監(jiān)聽(tīng)到主線程的,在的里即可找到主線程傳過(guò)來(lái)的,之后就可以用來(lái)向主線程發(fā)送信息了。這是一個(gè)很簡(jiǎn)單的,完成在兩個(gè)不同的中通信的功能。

Service Worker 學(xué)習(xí)筆記

Service Worker挺有意思的,前段時(shí)間看了相關(guān)的資料,自己動(dòng)手調(diào)了調(diào)demo,記錄一下學(xué)習(xí)過(guò)程。文中不僅會(huì)介紹Service Worker的使用,對(duì)fetchpushcache等Service Worker配套的API都會(huì)涉及,畢竟Service Worker與這些API配合使用才能發(fā)揮出真正的威力

Chrome對(duì)Service Worker的開(kāi)發(fā)者支持較好,Dev tools里可以簡(jiǎn)單的調(diào)試,F(xiàn)irefox還未提供調(diào)試用的工具,但是對(duì)API的支持更好。建議開(kāi)發(fā)和測(cè)試的話,在Chrome上進(jìn)行

文中有把Service Worker簡(jiǎn)寫(xiě)SW,不要覺(jué)得奇怪~

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

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

Installing 這個(gè)階段可以監(jiān)聽(tīng)install事件,并使用event.waitUtil來(lái)做Install完成前的準(zhǔn)備,比如cache一些數(shù)據(jù)之類(lèi)的,另外還有self.skipWaiting在serviceworker被跳過(guò)install過(guò)程時(shí)觸發(fā)

> 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).

**這說(shuō)明serviceWorker被替換是有條件的,即使有新的serviceworker,也得等舊的沒(méi)有被使用才能替換**。最明顯的體現(xiàn)是,刷新頁(yè)面并不一定能加載到新聞serviceworker

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

Redundant 被替換,即被銷(xiāo)毀

Fetch

fetch是新的Ajax標(biāo)準(zhǔn)接口,已經(jīng)有很多瀏覽器原生支持了,用來(lái)代替繁瑣的XMLHttpRequestjQuery.ajax再好不過(guò)了。對(duì)于還未支持的瀏覽器,可以用isomorphic-fetch polyfill。

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

Response

Response 寫(xiě)個(gè)fetch的栗子

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

Response的API,列幾個(gè)比較常用的:

Response.clone() Creates a clone of a Response object. 這個(gè)經(jīng)常用在cache直接緩存返回結(jié)果的場(chǎng)景

Body.blob() 這里寫(xiě)的是Body,其實(shí)調(diào)用接口還是用response,這里取Blob數(shù)據(jù)的數(shù)據(jù)流。MDN是這么說(shuō)的:

> 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應(yīng)該不會(huì)多帶帶new出來(lái)使用,因?yàn)楹芏郣equest相關(guān)的參數(shù),在Request的實(shí)例中都是只讀的,而真正可以配置Request屬性的地方,是fetch的第二個(gè)參數(shù):

// fetch的第一個(gè)參數(shù)是URI路徑,第二個(gè)參數(shù)則是生成Request的配置,
// 而如果直接傳給fetch一個(gè)request對(duì)象,其實(shí)只有URI是可配置的,
// 因?yàn)槠渌呐渲萌鏷eaders都是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衍生出來(lái)的API,配合Service Worker實(shí)現(xiàn)對(duì)資源請(qǐng)求的緩存。

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

API

caches.open(cacheName) 打開(kāi)一個(gè)cache,cachesglobal對(duì)象,返回一個(gè)帶有cache返回值的Promise

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

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

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

cache.add(Request|url) 并不是單純的add,因?yàn)閭魅氲氖莚equest或者url,在cache.add內(nèi)部會(huì)自動(dòng)去調(diào)用fetch取回request的請(qǐng)求結(jié)果,然后才是把response存入cache;cache.addAll類(lèi)似,通常在sw install的時(shí)候用cache.addAll把所有需要緩存的文件都請(qǐng)求一遍

cache.put(Request, Response) 這個(gè)相當(dāng)于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一樣,不在瀏覽器的主線程里運(yùn)行,因而和Web Worker一樣,有跟主線程通信的能力。

postMessage

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

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

target 需要傳輸?shù)腢RL域,具體看API文檔

transfer 用mdn的說(shuō)法,是一個(gè)transferable的對(duì)象,比如MessagePortArrayBuffer

另外說(shuō)明一點(diǎn),postMessage的調(diào)用者是被push數(shù)據(jù)一方的引用,即我要向sw post數(shù)據(jù),就需要sw的引用

注意,上面的postMessage是在document中使用的。在sw的context里使用略有不同:沒(méi)有target參數(shù)具體看這個(gè)API文檔

在sw中與主線程通信

先看個(gè)栗子:

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

    // port2給對(duì)方
        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傳來(lái)的port2
    ev.ports[0].postMessage("Hi, hello too");
});

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

MessageChannel

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

在上面已經(jīng)提到了,MessageChannel在一端創(chuàng)建,然后用channel.port1.onmesssage監(jiān)聽(tīng)另一端post的message,而將channel.port2通過(guò)postMessage的第二個(gè)參數(shù)(transfer)傳給另一端,讓另一端也能用MessagePort做同樣的操作。

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

Push API

如果說(shuō)fetch事件是sw攔截客戶端請(qǐng)求的能力,那么push事件就是sw攔截服務(wù)端“請(qǐng)求”的能力。這里的“請(qǐng)求”打了引號(hào),你可以把Push當(dāng)成WebSocket,也就是服務(wù)端可以主動(dòng)推送消息到客戶端。

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

push-api-demo

MDN上有個(gè)push-api-demo,是個(gè)簡(jiǎn)易聊天器。具體搭建的方法在這個(gè)repo上有,不再贅述。因?yàn)橛行㏄ush API只有Firefox Nightly版本支持,所以demo也只能跑在這個(gè)瀏覽器上,我還沒(méi)下好,沒(méi)跑起來(lái),等明天看吧~

記幾個(gè)Push API:

ServiceWorkerRegistration.showNotification(title, options) 這個(gè)可以理解成alert的升級(jí)版,網(wǎng)頁(yè)版的wechat的通知就是這個(gè)。

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

PushManager Push API的核心對(duì)象,注冊(cè)Push API從這里開(kāi)始,放在 ServiceWorkerRegistration

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

PushManager.getSubscription() 獲取當(dāng)前注冊(cè)好的PushSubscription對(duì)象

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

還是看個(gè)栗子:

// 瀏覽器端的main.js, 代碼來(lái)自push-api-demo
navigator.serviceWorker.ready.then(function(reg) {
    // 注冊(cè)push
        reg.pushManager.subscribe({userVisibleOnly: true})
           // 得到PushSubscription對(duì)象
          .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");
            // 這一步是個(gè)ajax,把公鑰和endpoint傳給server,因?yàn)槭莌ttps所以不怕公鑰泄露
            updateStatus(endpoint,key,"subscribe");
          })
});
    
// 服務(wù)端 server.js,接收并存下公鑰、endpoint
...
} else if(obj.statusType === "subscribe") {
// bodyArray里是ajax傳上來(lái)的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]
          }));
        };
      });
    });
  }
  ...
  
  // 還是服務(wù)端 server.js,推送信息到service worker
  if(obj.statusType === "chatMsg") {
      // 取出客戶端傳來(lái)的公鑰和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這個(gè)node的庫(kù),sendNotification里有key,說(shuō)明對(duì)信息加密了
        webPush.sendNotification(subscriber[2], 200, obj.key, JSON.stringify({
          action: "chatMsg",
          name: obj.name,
          msg: obj.msg
        }));
      };
    });
  }
Client端

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

然后創(chuàng)建一個(gè)MessageChannelServiceWorker通信

準(zhǔn)備工作到這里就做完了。Client與Server端的通信還是ajax,聊天室嘛就是傳用戶發(fā)送的消息。ServiceWorker去監(jiān)聽(tīng)push事件接住Server端push來(lái)的數(shù)據(jù),在這個(gè)demo里都是Server端接到Client的ajax請(qǐng)求的響應(yīng),當(dāng)然也可以又Server端主動(dòng)發(fā)起一個(gè)push。當(dāng)同時(shí)有兩個(gè)以上的Client都與這個(gè)Server通信,那么這幾個(gè)Client能看到所有與Server的消息,這才是聊天室嘛,不過(guò)要驗(yàn)證至少需要兩臺(tái)機(jī)器

Server端

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

Web-push這個(gè)庫(kù)還得看看

MDN Demo:sw-test

MDN上有一個(gè)完整的使用Service Worker的Demo,一個(gè)簡(jiǎn)易的聊天室,可以自己玩玩兒。

這個(gè)demo的思路是:install時(shí)fetch需要緩存的文件,用cache.addAll緩存到cacheStorage里。在fetch事件觸發(fā)時(shí),先cache.match這些緩存,若存在則直接返回,若不存在則用fetch抓這個(gè)request,然后在cache.put進(jìn)緩存。

調(diào)試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選項(xiàng)卡里已經(jīng)添加了Service Workers,可以查看當(dāng)前頁(yè)面是否有使用Service Worker,和它當(dāng)前的生命周期

卸載上一個(gè)activated的service worker的方法

service worker很頑強(qiáng),一個(gè)新的service worker install之后不能直接active,需要等到所有使用這個(gè)service worker的頁(yè)面都卸載之后能替換,不利于調(diào)試。今天試出來(lái)一個(gè)100%能卸載的方法

chrome://inspect/#service-workers中terminate相應(yīng)的service worker

chrome://serviceworker-internals/中unregister相應(yīng)的service worker

關(guān)閉調(diào)試頁(yè)面,再打開(kāi)

調(diào)試service worker可以在chrome://inspect/#service-workers里inspect相應(yīng)的Devtool

Tricks

如果在緩存中找不到對(duì)應(yīng)的資源,把攔截的請(qǐng)求發(fā)回原來(lái)的流程

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)

復(fù)制response的返回結(jié)果,下次直接從cache里取出來(lái)用

   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未命中且網(wǎng)絡(luò)不可用的情況,這里Promise用了兩次catch,第一次還報(bào)錯(cuò)的話第二次catch才會(huì)執(zhí)行

   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的請(qǐng)求被攔截,返回偽造信息
    if (reqUrl.indexOf("test.css") > -1) {
        console.log("hijack text.css");
        ev.respondWith(
            new Response("hahah", {
                headers: {"Content-Type": "text/css"}
            })
        );
    }
    // 繼續(xù)請(qǐng)求
    else {
        ev.respondWith(fetch(ev.request));
    }
});
// app.js
window.onload = () => {
    // 請(qǐng)求test.css
    fetch("/service-worker-demo/test.css")
    .then(response => {
        return response.text();
    })
    .then(text => {
        console.log("text.css: " + text); // 在service worker install時(shí)返回真實(shí)的文本,在sw active時(shí)返回hahah,即偽造的文本
        return text;
    });

## 未解之謎

1. `serviceworker.register(url, { scope: "xxx" })`,這里的`scope`似乎沒(méi)用。在這個(gè)scope上級(jí)的靜態(tài)資源請(qǐng)求也會(huì)被`fetch`攔截,在`HTTPS`上也無(wú)效,可以看看[這個(gè)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/)
- [當(dāng)前端也擁有 Server 的能力](http://www.barretlee.com/blog/2016/02/16/when-fe-has-the-power-of-server)

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/78987.html

相關(guān)文章

  • Service Worker實(shí)現(xiàn)離線應(yīng)用

    摘要:同時(shí)抓取一個(gè)請(qǐng)求及其響應(yīng),并將其添加到給定的。返回一個(gè)對(duì)象,的結(jié)果是對(duì)象值組成的數(shù)組。代碼以下是一個(gè)實(shí)現(xiàn)離線應(yīng)用的這個(gè)是一個(gè)簡(jiǎn)陋的離線應(yīng)用,會(huì)緩存所有靜態(tài)資源請(qǐng)求,即使你修改了和文件,刷新頁(yè)面還是沒(méi)有變化。 開(kāi)始有興趣了解Service Worker,是因?yàn)閷W(xué)習(xí)react時(shí)使用create-react-app創(chuàng)建的應(yīng)用,src下面會(huì)有一個(gè)registerServiceWorker.js...

    jindong 評(píng)論0 收藏0
  • Service Worker實(shí)現(xiàn)離線應(yīng)用

    摘要:同時(shí)抓取一個(gè)請(qǐng)求及其響應(yīng),并將其添加到給定的。返回一個(gè)對(duì)象,的結(jié)果是對(duì)象值組成的數(shù)組。代碼以下是一個(gè)實(shí)現(xiàn)離線應(yīng)用的這個(gè)是一個(gè)簡(jiǎn)陋的離線應(yīng)用,會(huì)緩存所有靜態(tài)資源請(qǐng)求,即使你修改了和文件,刷新頁(yè)面還是沒(méi)有變化。 開(kāi)始有興趣了解Service Worker,是因?yàn)閷W(xué)習(xí)react時(shí)使用create-react-app創(chuàng)建的應(yīng)用,src下面會(huì)有一個(gè)registerServiceWorker.js...

    2json 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

mj

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<