当前位置: 首页 > article >正文

JavaScript系列(37)-- Service Workers详解

JavaScript Service Workers详解 🔄

今天,让我们深入了解Service Workers,这是一种强大的Web技术,能够实现离线缓存、推送通知和后台同步等功能。

Service Workers基础概念 🌟

💡 小知识:Service Worker是一种运行在浏览器后台的脚本,它可以拦截和处理网络请求,实现资源缓存、推送通知等功能,是实现Progressive Web Apps (PWA)的核心技术之一。

基本实现 📊

// 1. Service Worker注册
class ServiceWorkerManager {
    constructor(options = {}) {
        this.options = {
            scope: '/',
            ...options
        };
    }
    
    async register(scriptUrl) {
        try {
            if (!('serviceWorker' in navigator)) {
                throw new Error('Service Worker not supported');
            }
            
            const registration = await navigator.serviceWorker.register(
                scriptUrl,
                { scope: this.options.scope }
            );
            
            console.log('Service Worker registered:', registration.scope);
            return registration;
        } catch (error) {
            console.error('Service Worker registration failed:', error);
            throw error;
        }
    }
    
    async unregister() {
        const registration = await navigator.serviceWorker.getRegistration();
        if (registration) {
            await registration.unregister();
            console.log('Service Worker unregistered');
        }
    }
}

// 2. 缓存管理器
class CacheManager {
    constructor(cacheName) {
        this.cacheName = cacheName;
    }
    
    async addToCache(request, response) {
        const cache = await caches.open(this.cacheName);
        await cache.put(request, response);
    }
    
    async getFromCache(request) {
        const cache = await caches.open(this.cacheName);
        return cache.match(request);
    }
    
    async deleteFromCache(request) {
        const cache = await caches.open(this.cacheName);
        await cache.delete(request);
    }
    
    async clearCache() {
        await caches.delete(this.cacheName);
    }
}

// 3. 网络请求拦截器
class RequestInterceptor {
    constructor(strategies = {}) {
        this.strategies = {
            'cache-first': this.cacheFirst.bind(this),
            'network-first': this.networkFirst.bind(this),
            'cache-only': this.cacheOnly.bind(this),
            'network-only': this.networkOnly.bind(this),
            'stale-while-revalidate': this.staleWhileRevalidate.bind(this),
            ...strategies
        };
    }
    
    async cacheFirst(request, cacheName) {
        const cache = await caches.open(cacheName);
        const cached = await cache.match(request);
        
        if (cached) {
            return cached;
        }
        
        const response = await fetch(request);
        await cache.put(request, response.clone());
        return response;
    }
    
    async networkFirst(request, cacheName) {
        try {
            const response = await fetch(request);
            const cache = await caches.open(cacheName);
            await cache.put(request, response.clone());
            return response;
        } catch (error) {
            const cached = await caches.match(request);
            if (cached) {
                return cached;
            }
            throw error;
        }
    }
    
    async cacheOnly(request) {
        const cached = await caches.match(request);
        if (!cached) {
            throw new Error('No cached response found');
        }
        return cached;
    }
    
    async networkOnly(request) {
        return fetch(request);
    }
    
    async staleWhileRevalidate(request, cacheName) {
        const cache = await caches.open(cacheName);
        const cached = await cache.match(request);
        
        const fetchPromise = fetch(request).then(response => {
            cache.put(request, response.clone());
            return response;
        });
        
        return cached || fetchPromise;
    }
}

高级功能实现 🚀

// 1. 推送通知管理器
class NotificationManager {
    constructor() {
        this.permission = Notification.permission;
    }
    
    async requestPermission() {
        try {
            const permission = await Notification.requestPermission();
            this.permission = permission;
            return permission;
        } catch (error) {
            console.error('Failed to request notification permission:', error);
            throw error;
        }
    }
    
    async subscribe(registration) {
        try {
            const subscription = await registration.pushManager.subscribe({
                userVisibleOnly: true,
                applicationServerKey: this.urlBase64ToUint8Array(
                    'YOUR_PUBLIC_VAPID_KEY'
                )
            });
            
            return subscription;
        } catch (error) {
            console.error('Failed to subscribe to push:', error);
            throw error;
        }
    }
    
    async showNotification(title, options = {}) {
        if (this.permission !== 'granted') {
            throw new Error('Notification permission not granted');
        }
        
        const registration = await navigator.serviceWorker.getRegistration();
        await registration.showNotification(title, {
            icon: '/icon.png',
            badge: '/badge.png',
            ...options
        });
    }
    
    urlBase64ToUint8Array(base64String) {
        const padding = '='.repeat((4 - base64String.length % 4) % 4);
        const base64 = (base64String + padding)
            .replace(/\\-/g, '+')
            .replace(/_/g, '/');
            
        const rawData = window.atob(base64);
        const outputArray = new Uint8Array(rawData.length);
        
        for (let i = 0; i < rawData.length; ++i) {
            outputArray[i] = rawData.charCodeAt(i);
        }
        
        return outputArray;
    }
}

// 2. 后台同步管理器
class BackgroundSyncManager {
    constructor(registration) {
        this.registration = registration;
    }
    
    async register(tag) {
        try {
            await this.registration.sync.register(tag);
            console.log(`Background sync registered: ${tag}`);
        } catch (error) {
            console.error('Background sync registration failed:', error);
            throw error;
        }
    }
    
    async getTags() {
        const tags = await this.registration.sync.getTags();
        return tags;
    }
}

// 3. 离线数据管理器
class OfflineDataManager {
    constructor(dbName = 'offlineDB') {
        this.dbName = dbName;
        this.db = null;
    }
    
    async initDB() {
        return new Promise((resolve, reject) => {
            const request = indexedDB.open(this.dbName, 1);
            
            request.onerror = () => reject(request.error);
            request.onsuccess = () => {
                this.db = request.result;
                resolve(this.db);
            };
            
            request.onupgradeneeded = (event) => {
                const db = event.target.result;
                if (!db.objectStoreNames.contains('offlineData')) {
                    db.createObjectStore('offlineData', { keyPath: 'id' });
                }
            };
        });
    }
    
    async saveData(data) {
        if (!this.db) await this.initDB();
        
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction(
                ['offlineData'],
                'readwrite'
            );
            const store = transaction.objectStore('offlineData');
            const request = store.put(data);
            
            request.onsuccess = () => resolve(request.result);
            request.onerror = () => reject(request.error);
        });
    }
    
    async getData(id) {
        if (!this.db) await this.initDB();
        
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction(['offlineData'], 'readonly');
            const store = transaction.objectStore('offlineData');
            const request = store.get(id);
            
            request.onsuccess = () => resolve(request.result);
            request.onerror = () => reject(request.error);
        });
    }
}

性能优化技巧 ⚡

// 1. 预缓存策略
class PrecacheManager {
    constructor(cacheVersion) {
        this.cacheVersion = cacheVersion;
        this.precacheList = new Set();
    }
    
    addResources(resources) {
        resources.forEach(resource => this.precacheList.add(resource));
    }
    
    async precache() {
        const cache = await caches.open(this.cacheVersion);
        const existingKeys = await cache.keys();
        const existingUrls = new Set(
            existingKeys.map(request => request.url)
        );
        
        const newResources = Array.from(this.precacheList)
            .filter(url => !existingUrls.has(url));
            
        await Promise.all(
            newResources.map(async url => {
                try {
                    const response = await fetch(url);
                    await cache.put(url, response);
                } catch (error) {
                    console.error(`Failed to precache ${url}:`, error);
                }
            })
        );
    }
    
    async cleanup() {
        const cache = await caches.open(this.cacheVersion);
        const keys = await cache.keys();
        
        await Promise.all(
            keys.map(async request => {
                if (!this.precacheList.has(request.url)) {
                    await cache.delete(request);
                }
            })
        );
    }
}

// 2. 请求优化器
class RequestOptimizer {
    constructor() {
        this.requestQueue = new Map();
        this.batchSize = 5;
        this.batchDelay = 100;
    }
    
    async optimizeRequest(request) {
        const key = this.getRequestKey(request);
        
        if (this.requestQueue.has(key)) {
            return this.requestQueue.get(key);
        }
        
        const promise = this.processRequest(request);
        this.requestQueue.set(key, promise);
        
        try {
            const response = await promise;
            return response;
        } finally {
            this.requestQueue.delete(key);
        }
    }
    
    getRequestKey(request) {
        return `${request.method}:${request.url}`;
    }
    
    async processRequest(request) {
        // 实现请求处理逻辑
        return fetch(request);
    }
}

// 3. 缓存优化器
class CacheOptimizer {
    constructor(maxSize = 100) {
        this.maxSize = maxSize;
        this.cacheUsage = new Map();
    }
    
    async optimizeCache(cacheName) {
        const cache = await caches.open(cacheName);
        const keys = await cache.keys();
        
        if (keys.length <= this.maxSize) return;
        
        // 更新使用频率
        keys.forEach(key => {
            const count = this.cacheUsage.get(key.url) || 0;
            this.cacheUsage.set(key.url, count + 1);
        });
        
        // 按使用频率排序
        const sortedUrls = Array.from(this.cacheUsage.entries())
            .sort((a, b) => b[1] - a[1])
            .map(([url]) => url);
            
        // 删除最少使用的缓存
        const urlsToDelete = sortedUrls.slice(this.maxSize);
        await Promise.all(
            urlsToDelete.map(url => cache.delete(url))
        );
    }
}

最佳实践建议 💡

  1. Service Worker生命周期管理
// 1. 生命周期处理器
class LifecycleHandler {
    constructor() {
        this.version = '1.0.0';
        this.handlers = new Map();
    }
    
    onInstall(callback) {
        this.handlers.set('install', callback);
    }
    
    onActivate(callback) {
        this.handlers.set('activate', callback);
    }
    
    onFetch(callback) {
        this.handlers.set('fetch', callback);
    }
    
    async handleInstall(event) {
        const handler = this.handlers.get('install');
        if (handler) {
            event.waitUntil(handler(event));
        }
    }
    
    async handleActivate(event) {
        const handler = this.handlers.get('activate');
        if (handler) {
            event.waitUntil(handler(event));
        }
    }
    
    handleFetch(event) {
        const handler = this.handlers.get('fetch');
        if (handler) {
            event.respondWith(handler(event));
        }
    }
}

// 2. 错误处理
class ErrorHandler {
    static async handle(error, context) {
        console.error(`Error in ${context}:`, error);
        
        // 发送错误报告
        try {
            await fetch('/error-report', {
                method: 'POST',
                body: JSON.stringify({
                    error: error.message,
                    context,
                    timestamp: Date.now()
                })
            });
        } catch (e) {
            console.error('Failed to send error report:', e);
        }
        
        // 返回降级响应
        return new Response('Service Worker Error', {
            status: 500,
            headers: { 'Content-Type': 'text/plain' }
        });
    }
}

// 3. 安全策略
class SecurityPolicy {
    constructor() {
        this.allowedOrigins = new Set();
        this.allowedPaths = new Set();
    }
    
    addAllowedOrigin(origin) {
        this.allowedOrigins.add(origin);
    }
    
    addAllowedPath(path) {
        this.allowedPaths.add(path);
    }
    
    isRequestAllowed(request) {
        const url = new URL(request.url);
        return this.allowedOrigins.has(url.origin) ||
               this.allowedPaths.has(url.pathname);
    }
}

结语 📝

Service Workers为Web应用提供了强大的离线能力和后台处理能力。通过本文,我们学习了:

  1. Service Worker的基本概念和实现
  2. 高级功能如推送通知和后台同步
  3. 性能优化技巧
  4. 最佳实践和注意事项
  5. 安全性考虑

💡 学习建议:在使用Service Worker时,要特别注意生命周期管理和缓存策略的选择。同时,要考虑兼容性和降级处理,确保在不支持Service Worker的环境下也能正常工作。


如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇

终身学习,共同成长。

咱们下一期见

💻


http://www.kler.cn/a/515311.html

相关文章:

  • cookie 与 session -- 会话管理
  • SQL-leetcode—1174. 即时食物配送 II
  • Web安全攻防入门教程——hvv行动详解
  • 计算机网络 (52)秘钥分配
  • C语言——文件操作
  • 靠右行驶数学建模分析(2014MCM美赛A题)
  • 有限元分析学习——Anasys Workbanch第一阶段笔记(15)接触间隙处理与赫兹接触
  • DELL EDI:需求分析及注意事项
  • vue3+webOffice合集
  • HarmonyOS Next 应用UI生成工具介绍
  • IP属地与视频定位位置不一致:现象解析与影响探讨
  • orbbec 奥比中光相机单目及多目调用方式python代码
  • 「全网最细 + 实战源码案例」设计模式——工厂方法模式
  • 如何确保爬虫不违反苏宁的使用条款?
  • 机器学习之决策树(DecisionTree——C4.5)
  • StarRocks强大的实时数据分析
  • 网络安全解决方案分享:推荐十款网络准入控制系统,保护企业网络安全
  • 青少年编程与数学 02-007 PostgreSQL数据库应用 15课题、备份与还原
  • 新年好(Dijkstra+dfs/全排列)
  • excel导入数据处理前端
  • 安卓程序作为web服务端的技术实现(二):Room 实现数据存储
  • Spring AOP 中,常用来定义切入点的表达式
  • 算法随笔_16: 找出第k小的数对距离
  • ubuntu扩建swap 解决8295编译卡死的问题(提高系统性能)
  • K8S中Service详解(二)
  • 详解深度学习中的Dropout