sw.js 4.41 KB
var sw = {
    cacheName: "",
    cache: function (req, res) {
        return caches.open(this.cacheName).then(function (cache) {
            return cache.put(req, res);
        });
    },
    precache: function (files) {
        return caches.open(this.cacheName).then(function (cache) {
            return cache.addAll(files);
        });
    },
    fromCache: function (request) {
        return caches.open(this.cacheName).then(function (cache) {
            return cache.match(request).then(function (res) {
                if (res) { // 使用缓存
                    return Promise.resolve(res);
                }
                // 清理过期缓存 同时走网络请求获取新的资源
                sw.delExpiredCache(cache, request);
                return Promise.reject("no match");
            });
        });
    },
    delExpiredCache: function (cache, request) {
        var url = new URL(request.url);

        return cache.keys().then(function (keys) {
            keys.every(function (req) {
                var rUrl = new URL(req.url);

                // 版本变更, 删除旧缓存
                if (req.url != request.url && url.pathname === rUrl.pathname && url.search !== rUrl.search) {
                    cache.delete(req);
                    return false;
                }
                return true;
            });
        });
    },
    fromNetwork: function (request) {
        return fetch(request);
    },
    matchRoute: function (request) {
        var routes = this.routes;
        var nocaches = this.nocaches;
        var ismatch = false;

        routes.every(function (route) {
            // 匹配路由的同时 需要不匹配不缓存项
            ismatch = !!request.url.match(route) && !request.url.match(nocaches);

            return !ismatch;
        });
        return ismatch;
    },
    on: function (evtName, cb) {
        self.addEventListener(evtName, cb);
    },
    /**
     * settings
     * - precache []
     */
    init: function (cacheName, settings) {
        var precache = settings.precache || [];

        this.cacheName = cacheName;
        this.routes = settings.routes;
        this.nocaches = settings.nocaches;

        // 预缓存
        this.on("install", function (evt) {
            evt.waitUntil(
                sw.precache(precache).then(function () {
                    // 让sw跳过install阶段进入active
                    return self.skipWaiting();
                })
            );
        });

        // 监听请求 使用缓存并及时更新
        this.on("fetch", function (evt) {
            var request = evt.request;

            if (!sw.matchRoute(request)) return;

            evt.respondWith(
                sw.fromCache(request).catch(function () {
                    return sw.fromNetwork(request).then(function (response) {
                        sw.cache(request, response);
                        return Promise.resolve(response.clone());
                    });
                })
            );
        });

        this.on("activate", function (evt) {
            evt.waitUntil(self.clients.claim());
        });
    }
};

var CACHE_NAME = "mobilemode-cache";

sw.init(CACHE_NAME, {
    routes: [/\/mobilemode\/mobile\/.*\.(html|js[^p]|css)/g],
    nocaches: /.*nocache|demo\/.*?\.html|view\.html.*?$/g, // 针对含nocache参数和demo中的html不进行缓存
    precache: [""]
});

/** 用于接受消息 */
self.addEventListener('message', function (e) {
    var data = e.data;
    var cmd = data.command;

    switch (cmd) {
        case "report":
            reporting().then(function (data) {
                e.ports[0].postMessage({ data: data });
            });
            break;
        default:
    }
});

/** 生成缓存报表 */
function reporting() {
    return caches.open(CACHE_NAME).then(function (cache) {
        return cache.keys().then(function (reqs) {
            return Promise.all(
                reqs.map(function (req) {
                    return cache.match(req).then(function (res) {
                        var h = res.headers;
                        var url = new URL(res.url);

                        return {
                            url: res.url,
                            pathname: url.pathname,
                            size: h.get("Content-Length"),
                            lastModified: new Date(h.get("date"))
                        };
                    });
                })
            );
        });
    });
}