<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          service worker輕度探索 - 解決運(yùn)營活動(dòng)需求中的圖片加載問題?

          共 7079字,需瀏覽 15分鐘

           ·

          2022-01-10 05:47

          大廠技術(shù)  高級(jí)前端  Node進(jìn)階

          點(diǎn)擊上方 程序員成長指北,關(guān)注公眾號(hào)

          回復(fù)1,加入高級(jí)Node交流群


          寫在前面


          做過運(yùn)營活動(dòng)需求的同學(xué)都知道,一般一個(gè)運(yùn)營活動(dòng)中會(huì)用到很多的圖片資源。用戶訪問首頁時(shí),都會(huì)看到一個(gè)loading態(tài),表示頁面正在加載所需的所有圖片資源。像下面這樣:

          手動(dòng)加載一個(gè)圖片的代碼也很簡單:

          var img = new Image();img.onload = function(){ ... }img.src = '圖片地址';

          之所以要提前加載所有的圖片,是為了在后續(xù)的頁面中使用圖片時(shí),不會(huì)因?yàn)樾枰虞d圖片而產(chǎn)生耗時(shí),導(dǎo)致體驗(yàn)問題。本文所要討論的場景就是:怎么樣做到在首頁加載圖片后,直接在后面的業(yè)務(wù)邏輯中直接使用提前加載好的圖片呢?答案就是:把圖片存下來。



          緩存首頁加載的圖片


          我能想到的這種場景下的緩存圖片方法有兩種:


          1. 使用瀏覽器的緩存。圖片在第一次請求成功后,一般都會(huì)設(shè)置緩存。在頁面后續(xù)的業(yè)務(wù)邏輯中,如果說想使用某圖片,直接正常發(fā)起圖片請求即可,瀏覽器會(huì)走緩存,甚至是從內(nèi)存中直接返回這個(gè)圖片。

          2. 將加載好的Image對象直接保存在內(nèi)存中。這種方法很適用canvas中畫圖的場景,直接把保存下來的Image對象扔到canvas的drawImage中即可。

          做業(yè)務(wù)需要不斷的總結(jié),思考。還能用什么方法來實(shí)現(xiàn)圖片的緩存呢 ?  我嘗試了一下Service Worker,本文將介紹一下Service Worker在這種業(yè)務(wù)場景下的應(yīng)用。

          本文只是輕度嘗試了一下Service Worker,并未在線上應(yīng)用。



          service worker


          Service Worker是PWA的重要組成部分,其包含安裝、激活、等待、銷毀等四個(gè)生命周期。主要有以下的特性:

          • 一個(gè)獨(dú)立的 worker 線程,獨(dú)立于當(dāng)前網(wǎng)頁進(jìn)程,有自己獨(dú)立的 worker context。

          • 一旦被 install,就永遠(yuǎn)存在,除非被手動(dòng) unregister

          • 用到的時(shí)候可以直接喚醒,不用的時(shí)候自動(dòng)睡眠

          • 可編程攔截代理請求和返回,緩存文件,緩存的文件可以被網(wǎng)頁進(jìn)程取到(包括網(wǎng)絡(luò)離線狀態(tài))

          • 離線內(nèi)容開發(fā)者可控

          • 能向客戶端推送消息

          • 不能直接操作 DOM

          • 必須在 HTTPS 環(huán)境下才能工作( 或 http://localhost )

          • 異步實(shí)現(xiàn),內(nèi)部大都是通過 Promise 實(shí)現(xiàn)

          在本文所描述的業(yè)務(wù)場景中,主要是應(yīng)用service worker的攔截代理請求和返回的功能。

          關(guān)于service worker的基礎(chǔ),谷歌開發(fā)者網(wǎng)站上有詳細(xì)的介紹,這里就不贅述了。

          地址在這里:https://developers.google.com/web/fundamentals/primers/service-workers/?hl=zh-cn

          需要注意的是,service worker一定要謹(jǐn)慎使用,因?yàn)樗匾?,一旦注冊,站點(diǎn)的所有請求都會(huì)被控制。



          service worker的示例


          結(jié)合文章開頭所描述的場景,我們先來寫一些必要的業(yè)務(wù)函數(shù)。

          // 加載一個(gè)圖片function loadImage(imgUrl) {    return new Promise((resolve, reject)=>{        const img = new Image();        img.onload = function() {            resolve();        };        img.src = imgUrl;    });}
          // 加載一堆圖片function loadImageList(imgList) { return Promise.all(imgList.map(function (imgUrl) { return loadImage(imgUrl); }));}

          下面是service worker的代碼:

          self.addEventListener('install', function (event) {    console.log('install');});
          self.addEventListener('fetch', function (evt) { evt.respondWith( caches.match(evt.request).then(function(response) { if (response) { return response; } const request = evt.request.clone(); return fetch(request).then(function (response) { if (!response || response.status !== 200 || !response.headers.get('Content-type').match(/image/)) { return response; } const responseClone = response.clone(); // 流數(shù)據(jù)需要克隆一份。注意事項(xiàng)② caches.open('test-cache').then(function (cache) { cache.put(evt.request, responseClone); }); return response; }); }) )});
          self.addEventListener('activate', function () { console.log('activate'); clients.claim(); // 首次activate后,就控制頁面。注意事項(xiàng)①});

          注冊完service worker后,我們就劫持了頁面的所有請求。每一次請求經(jīng)過service worker時(shí),都會(huì)判斷剛請求是否已有緩存,如果有緩存,就直接返回結(jié)果。沒有緩存時(shí),才會(huì)向服務(wù)器發(fā)起請求,并且將圖片請求的結(jié)果緩存起來。

          在業(yè)務(wù)代碼中,我們注冊并使用這個(gè)service worker的代碼如下:

          // 需要加載的圖片列表const imgArr = ['http://xxx.png''...'];
          // 注冊service workerfunction registerServiceWorker() { if ('serviceWorker' in navigator) { return navigator.serviceWorker.register('http://localhost:8080/service.js'); } else { // 沒有service的處理邏輯省略 }}
          registerServiceWorker().then(registration => { // 注意事項(xiàng)③ let serviceWorker; if (registration.installing) { console.log('registration.installing'); serviceWorker = registration.installing; } else if (registration.waiting) { console.log('registration.waiting'); serviceWorker = registration.waiting; } else if (registration.active) { console.log('registration.active'); serviceWorker = registration.active; loadImageList(imgArr); } if (serviceWorker) { serviceWorker.addEventListener('statechange', function (e) { if(e.target.state === 'activated') { // 首次注冊時(shí) console.log('首次注冊sw時(shí),sw激活'); loadImageList(imgArr); } }); }}).catch(e => { console.log(e);});

          注意事項(xiàng):

          1. 正常情況下,service worker剛注冊時(shí),是不會(huì)控制頁面的,即無法攔截到頁面的請求。需要用戶刷新頁面,再次訪問時(shí),service worker才會(huì)攔截頁面請求。這與我們的需求場景不符合。我們的需求是:用戶首次訪問請求圖片資源時(shí),就需要對返回的圖片進(jìn)行緩存。所以,需要在service worker進(jìn)入activate狀態(tài)后,通過clients.claim()來獲得頁面的控制權(quán)。不過,這種方式并不被提倡

          2. service worker攔截到請求后,我們需要拷貝返回的數(shù)據(jù)流,才能存入緩存。

          3. 在業(yè)務(wù)代碼中,我們每次都需要調(diào)用navigator.serviceWorker.register來拿到一個(gè)service worker。瀏覽器會(huì)判斷當(dāng)前service worker的狀態(tài),返回對應(yīng)的對象。我們需要保證在service worker準(zhǔn)備無誤后,再發(fā)起圖片的請求。由于server worker的自身邏輯需要一定的時(shí)間,所以我們發(fā)起圖片請求的時(shí)間會(huì)被延后。



          使用service worker后的效果


          以我做的運(yùn)營活動(dòng)項(xiàng)目為例,使用service worker之前,網(wǎng)絡(luò)請求是這樣的:


          • 活動(dòng)頁首頁,首次集中請求圖片:

          • 活動(dòng)頁后續(xù)頁面中,使用加載好的圖片:

          使用service-worker之后,網(wǎng)絡(luò)請求是這樣的:

          • 活動(dòng)頁首頁,首次集中請求圖片:


          • 活動(dòng)頁后續(xù)頁面中,使用加載好的圖片:


          可以看到,我們成功使用service worker劫持了頁面的請求,并且將圖片緩存到了瀏覽器的cache storage中。我們來看一下瀏覽器的緩存。這里的緩存都是http response:

          另外這里多說一句,可以使用下面的代碼,來查看當(dāng)前網(wǎng)站可以使用的瀏覽器本地存儲(chǔ)空間

          if ('storage' in navigator && 'estimate' in navigator.storage) {  navigator.storage.estimate().then(({usage, quota}) => {    console.log(`Using ${usage} out of ${quota} bytes.`);  });}



          一些思考


          在本文提到的場景中,我們在用戶首次訪問頁面時(shí),先注冊了service worker,并且使service worker立即控制頁面,然后再開始請求圖片。這種做法延后了圖片請求的發(fā)起時(shí)間,并且從上面的圖中可以看到,通過service worker加載圖片的耗時(shí)比正常直接請求圖片耗時(shí)略長。這些因素導(dǎo)致首屏?xí)r間被延后了。另外,作為運(yùn)營活動(dòng)頁,同一個(gè)用戶也不會(huì)在幾天內(nèi)多次訪問,因此service worker的【繞過網(wǎng)絡(luò),立即響應(yīng)請求】的特性并不能很好地發(fā)揮出來。綜上所述,在本文的場景中,使用service worker來做緩存并不是最佳實(shí)踐。

          關(guān)于service worker做緩存的最佳實(shí)踐以及使用場景,可以查看這篇文章:

          https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading?hl=zh-cn

          service worker最適合的場景還是資源離線化,用戶二次進(jìn)入頁面時(shí)可以達(dá)到資源秒加載,不會(huì)受網(wǎng)絡(luò)狀況的影響。



          寫在后面


          本文從業(yè)務(wù)的角度出發(fā),輕度探索了service worker在文章開頭給出的業(yè)務(wù)場景中的應(yīng)用,了解了service worker的使用方法。后續(xù)會(huì)考慮在合適的業(yè)務(wù)場景中進(jìn)行應(yīng)用。符合預(yù)期。


          Node 社群


          我組建了一個(gè)氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學(xué)習(xí)感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。


             “分享、點(diǎn)贊、在看” 支持一波??

          瀏覽 67
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲性无码视频 | 国产成人久久视频 | 日本A∨在线 | 人人摸,人人操,人人揉 | 淫妻大神|