Cache API

255 阅读4分钟

在用户普遍3g网络的条件下,如何加快用户对于游戏体验是非常重要的。一般对于资源加载采取的方式主要是懒加载、预加载和缓存。对于h5游戏需要对游戏资源加载进行预加载,同时需要对于这些相对不会变更的资源进行缓存,这样用户二次进入的话,就不会浪费太多少时间在等待。针对这种缓存,本文就Cache进行调研测试

1、Cache

浏览器的缓存主要有memoryCachediskCacheServiceWorker cache,本文主要以ServiceWorker cache为主。缓存存储的结构数据,主要是一种包含请求和响应的存储格式。当用户访问当前页面时,浏览器会发出若干请求,服务器端响应内容是HTML 结构,或结构化数据,或其他媒体资源等。cache缓存允许浏览器本地存储这些请求和响应。

  • 注意:
    • a、Cache 接口像 workers 一样,是暴露在 window 作用域下的。尽管它被定义在 service worker 的标准中,但是它不必一定要配合 service worker 使用
    • b、除非明确地更新缓存,否则缓存将不会被更新;除非删除,否则缓存数据不会过期
    • c、因为每个浏览器都硬性限制了一个域下缓存数据的大小。缓存配额使用估算值,可以使用 StorageEstimate API 获得。浏览器尽其所能去管理磁盘空间,但它有可能删除一个域下的缓存数据。浏览器要么自动删除特定域的全部缓存,要么全部保留

2、谷歌缓存位置

缓存存储数据位置: image.png 加载中使用缓存资源:

image.png

3、缓存方法

  • 创建缓存对象:用于创建或访问缓存对象的方法是 caches.open() 。caches 是一个缓存存储对象的全局变量。open() 方法接受一个参数,即我们要访问的缓存对象的名称
caches.open("demo-cache").then((myCache) => {
    // 在缓存对象 myCache 中进行操作
});
  • 添加缓存:
//接受一个请求作为参数,请求可以是完整请求对象,也可以只是一个 UR
caches.open("demo-cache").then((myCache) => {
    // 仅 URL
    myCache.add("/subscribe");

    // 完整的 Request 对象
    myCache.add(new Request('/subscribe', {
        method: "GET",
        headers: new Headers({
		    'Content-Type': 'text/html'
	    }),
        /* 其他 Request 配置 */
    });
});
//添加多个请求,我们可以使用 addAll() 方法,该方法接受一组请求
caches.open("demo-cache").then((myCache) => {
    myCache.addAll([
        "/subscribe", 
        "/images/demo.png"
    ]);
});
  • 获取缓存:
//在于特定的一个缓存对象中,指定缓存对象
caches.open("demo-cache").then((myCache) => {
    myCache.match("/subscribe").then((cachedResponse) => {
        if (cachedResponse) {
            // 找到相关响应后操作
        } else {
            // 未找到相关响应后操作
        }
        
    });
});
//搜索与 URL 片段匹配的所有请求(例如 /images/ 路径中包含的所有请求),返回一个响应数组
caches.open("demo-cache").then((myCache) => {
    myCache.matchAll("/images/").then((cachedResponses) => {
        if (cachedResponses) {
             // 找到相关响应后操作
        } else {
            // 未找到相关响应后操作
        }
    });
});
  • 更新缓存:
//调用相同的 cache.add() 方法,执行请求页面的提取并替换相同 URL 的缓存中的响应
caches.open("demo-cache").then((myCache) => {
    // 浏览器会重新请求并缓存响应
    myCache.add("/subscribe");
});
//使用 cache.put() 方法,将请求/响应直接存储到缓存对象中
const request = new Request("/subscribe");

fetch(request).then((fetchResponse) => {
    caches.open('demo-cache').then((myCache) => {
        cache.put(request, fetchResponse);
    });
});
  • 删除缓存:
//删除特定对象的缓存
caches.open("demo-cache").then((myCache) => {
    myCache.delete("/subscribe");
});
//删除缓存对象
caches.delete("demo-cache");

4、测试案例

//html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <iframe src="./web/index.html" frameborder="0" height="667px" width="375px"></iframe>
    <img src="./2022-12-18-11-06-11.png" alt="" srcset="" style="width: 100px;height: 100px;">
    <script>
        if ('serviceWorker' in navigator) {
            navigator.serviceWorker.register('./demo-cache.js');
        }
    </script>
</body>
</html>
//demo-cache.js
var VERSION = 'v12';

// 缓存
self.addEventListener('install', function (event) {
    event.waitUntil(
        caches.open(VERSION).then(function (cache) {
            //安装的时候,提前加载资源
            return cache.addAll([
                './web/index.html',
                './2022-12-18-11-06-11.png',
                './cache.html'
            ]);
        })
    );
});

// 缓存更新
self.addEventListener('activate', function (event) {
    event.waitUntil(
        caches.keys().then(function (cacheNames) {
            return Promise.all(
                cacheNames.map(function (cacheName) {
                    // 如果当前版本和缓存版本不一致
                    if (cacheName !== VERSION) {
                        return caches.delete(cacheName);
                    }
                })
            );
        })
    );
});

// 捕获请求并返回缓存数据
self.addEventListener('fetch', function (event) {
    event.respondWith(
        caches.match(event.request).catch(function () {
            return fetch(event.request);
        }).then(function (response) {
            if (response) {
                caches.open(VERSION).then(function (cache) {
                    cache.put(event.request, response);
                });
                return response.clone();
            }
            return fetch(event.request).then(response => {
                //资源不存在,重新加载且同时添加到缓存中
                if (response) {
                    caches.open(VERSION).then(function (cache) {
                        cache.put(event.request, response);
                    });
                    return response.clone();
                }
            });
        }).catch(function (error) {
            // 处理 match() 或 fetch() 引起的异常。
            console.error('  Error in fetch handler:', error);

            throw error;
        })
    );
});

停用memoryCachediskCache,且低网速3g条件下,结果发现资源全部被缓存了,第二次打开速度:

1683091883547.jpg

没被缓存下来,游戏资源加载的速度如图所示:

1683092038921.jpg

5、参考:

MDN Cache

Service Worker和cacheStorage缓存及离线开发

Cache 简易概念