摘要:寫在前面本文首發(fā)于公眾號符合預期的做過運營活動需求的同學都知道,一般一個運營活動中會用到很多的圖片資源。注意事項注冊完后,我們就劫持了頁面的所有請求。
寫在前面
本文首發(fā)于公眾號:符合預期的CoyPan
做過運營活動需求的同學都知道,一般一個運營活動中會用到很多的圖片資源。用戶訪問首頁時,都會看到一個loading態(tài),表示頁面正在加載所需的所有圖片資源。像下面這樣:
手動加載一個圖片的代碼也很簡單:
var img = new Image(); img.onload = function(){ ... } img.src = "圖片地址";
之所以要提前加載所有的圖片,是為了在后續(xù)的頁面中使用圖片時,不會因為需要加載圖片而產生耗時,導致體驗問題。本文所要討論的場景就是:怎么樣做到在首頁加載圖片后,直接在后面的業(yè)務邏輯中直接使用提前加載好的圖片呢?答案就是:把圖片存下來。
緩存首頁加載的圖片我能想到的這種場景下的緩存圖片方法有兩種:
使用瀏覽器的緩存。圖片在第一次請求成功后,一般都會設置緩存。在頁面后續(xù)的業(yè)務邏輯中,如果說想使用某圖片,直接正常發(fā)起圖片請求即可,瀏覽器會走緩存,甚至是從內存中直接返回這個圖片。
將加載好的Image對象直接保存在內存中。這種方法很適用canvas中畫圖的場景,直接把保存下來的Image對象扔到canvas的drawImage中即可。
做業(yè)務需要不斷的總結,思考。還能用什么方法來實現(xiàn)圖片的緩存呢 ? 我嘗試了一下Service Worker,本文將介紹一下Service Worker在這種業(yè)務場景下的應用。
本文只是輕輕嘗試了一下Service Worker,并未在線上項目中應用。Service Worker
Service Worker是PWA的重要組成部分,其包含安裝、激活、等待、銷毀等四個生命周期。主要有以下的特性:
一個獨立的 worker 線程,獨立于當前網頁進程,有自己獨立的 worker context。
一旦被 install,就永遠存在,除非被手動 unregister
用到的時候可以直接喚醒,不用的時候自動睡眠
可編程攔截代理請求和返回,緩存文件,緩存的文件可以被網頁進程取到(包括網絡離線狀態(tài))
離線內容開發(fā)者可控
能向客戶端推送消息
不能直接操作 DOM
必須在 HTTPS 環(huán)境下才能工作( 或 http://localhost )
異步實現(xiàn),內部大都是通過 Promise 實現(xiàn)
在本文所描述的業(yè)務場景中,主要是應用service worker的攔截代理請求和返回的功能。
關于service worker的基礎,谷歌開發(fā)者網站上有詳細的介紹,這里就不贅述了。
地址在這里:https://developers.google.com...
需要注意的是,service worker一定要謹慎使用,因為它太重要了,一旦注冊,站點的所有請求都會被控制。
Service Worker的示例結合文章開頭所描述的場景,我們先來寫一些必要的業(yè)務函數。
// 加載一個圖片 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(); // 流數據需要克隆一份。注意事項② caches.open("test-cache").then(function (cache) { cache.put(evt.request, responseClone); }); return response; }); }) ) }); self.addEventListener("activate", function () { console.log("activate"); clients.claim(); // 首次activate后,就控制頁面。注意事項① });
注冊完service worker后,我們就劫持了頁面的所有請求。每一次請求經過service worker時,都會判斷剛請求是否已有緩存,如果有緩存,就直接返回結果。沒有緩存時,才會向服務器發(fā)起請求,并且將圖片請求的結果緩存起來。
在業(yè)務代碼中,我們注冊并使用這個service worker的代碼如下:
// 需要加載的圖片列表 const imgArr = ["http://xxx.jpg", "..."]; // 注冊service worker function registerServiceWorker() { if ("serviceWorker" in navigator) { return navigator.serviceWorker.register("http://localhost:8080/service.js"); } else { // 沒有service的處理邏輯省略 } } registerServiceWorker().then(registration => { // 注意事項③ 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") { // 首次注冊時 console.log("首次注冊sw時,sw激活"); loadImageList(imgArr); } }); } }).catch(e => { console.log(e); });
注意事項:
正常情況下,service worker剛注冊時,是不會控制頁面的,即無法攔截到頁面的請求。需要用戶刷新頁面,再次訪問時,service worker才會攔截頁面請求。這與我們的需求場景不符合。我們的需求是:用戶首次訪問請求圖片資源時,就需要對返回的圖片進行緩存。所以,需要在service worker進入activate狀態(tài)后,通過clients.claim()來獲得頁面的控制權。不過,這種方式并不被提倡。
service worker攔截到請求后,我們需要拷貝返回的數據流,才能存入緩存。
在業(yè)務代碼中,我們每次都需要調用navigator.serviceWorker.register來拿到一個service worker。瀏覽器會判斷當前service worker的狀態(tài),返回對應的對象。我們需要保證在service worker準備無誤后,再發(fā)起圖片的請求。由于server worker的自身邏輯需要一定的時間,所以我們發(fā)起圖片請求的時間會被延后。
使用service worker后的效果以我做的運營活動項目為例,使用service worker之前,網絡請求是這樣的:
活動頁首頁,首次集中請求圖片
活動頁后續(xù)頁面中,使用加載好的圖片:
使用service-worker之后,網絡請求是這樣的:
活動頁首頁,首次集中請求圖片:
活動頁后續(xù)頁面中,使用加載好的圖片:
可以看到,我們成功使用service worker劫持了頁面的請求,并且將圖片緩存到了瀏覽器的cache storage中。我們來看一下瀏覽器的緩存。這里的緩存都是http response。
另外這里多說一句,可以使用下面的代碼,來查看當前網站可以使用的瀏覽器本地存儲空間
if ("storage" in navigator && "estimate" in navigator.storage) { navigator.storage.estimate().then(({usage, quota}) => { console.log(`Using ${usage} out of ${quota} bytes.`); }); }一些思考
在本文提到的場景中,我們在用戶首次訪問頁面時,先注冊了service worker,并且使service worker立即控制頁面,然后再開始請求圖片。這種做法延后了圖片請求的發(fā)起時間,并且從上面的圖中可以看到,通過service worker加載圖片的耗時比正常直接請求圖片耗時略長。這些因素導致首屏時間被延后了。另外,作為運營活動頁,同一個用戶也不會在幾天內多次訪問,因此service worker的【繞過網絡,立即響應請求】的特性并不能很好地發(fā)揮出來。因此,在本文描述的場景中,使用service worker來做緩存并不是最佳實踐。
關于service worker做緩存的最佳實踐以及使用場景,可以查看這篇文章:
https://developers.google.com...
service worker最適合的場景還是資源離線化,用戶二次進入頁面時可以達到資源秒加載,不會受網絡狀況的影響。
寫在后面本文從業(yè)務的角度出發(fā),輕度探索了service worker在文章開頭給出的業(yè)務場景中的應用。后續(xù)會考慮在合適的業(yè)務場景中進行應用。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/102329.html
摘要:目標是探索是否能夠加快頁面首屏速度。實驗組瀏覽器支持,本次時,進行初始化。從上面的直觀對比可以看出,個指標,組的分位值都略微大于組的分位值,差距在幾十毫秒左右。最終,我也沒有采用來優(yōu)化首屏速度。 寫在前面 本文首發(fā)于公眾號:符合預期的CoyPan 不久之前,我簡單探索了service worker在一個活動運營頁面中的應用,可以參考我之前的這篇文章: service worker輕度探...
摘要:對來說主要兩個事件。是當前的變量,執(zhí)行該方法表示強制當前處在狀態(tài)的進入狀態(tài)。頁面關閉之后,老的會被干掉,新的接管頁面新的生效后會觸發(fā)事件。 一、傳統(tǒng)web 應用 當前web應用在移動時代并沒有達到其在桌面設備上流行的程度,下面有張圖來對比與原生應用之間的差別。 showImg(https://segmentfault.com/img/bVbaD44?w=1920&h=1080)...
摘要:參與者流量來自于內部系統(tǒng)和外部流量,其中大部分來自于外部流量。水平擴容服務的水平擴容重要性不言而喻。 背景 目前微店中臺團隊為了滿足公司大部分產品、運營以及部分后端開發(fā)人員的嘗鮮和試錯的需求,提供了一套基于圖形化搭建的服務端接口交付方案,利用該方案及提供的系統(tǒng)可生成一副包含運行時環(huán)境定義可立即運行的工程代碼,最后,通過 某種serverless平臺 實現(xiàn)生成后代碼的部署、CI、運行、反...
摘要:在端,盡管開發(fā)人員對其功能的需求很高,但出于某些原因,推送通知被引入的時間比較晚。發(fā)送推送通知在服務器上實現(xiàn)調用,該調用觸發(fā)到用戶設備的推送消息。推送服務推送服務是接收請求驗證請求并將推送消息發(fā)送到對應的瀏覽器。 這是專門探索 JavaScript 及其所構建的組件的系列文章的第9篇。 想閱讀更多優(yōu)質文章請猛戳GitHub博客,一年百來篇優(yōu)質文章等著你! 如果你錯過了前面的章節(jié),可以在...
閱讀 2299·2021-11-10 11:35
閱讀 919·2021-09-26 09:55
閱讀 2411·2021-09-22 15:22
閱讀 2329·2021-09-22 15:17
閱讀 3707·2021-09-09 09:33
閱讀 1837·2019-08-30 11:22
閱讀 979·2019-08-30 10:57
閱讀 652·2019-08-29 16:10