JS: Service Worker phục vụ cache

Các bạn sẽ tự tìm hiểu Service Worker là gì?
Nó như một dịch vụ chạy ngầm để phục cho riêng trang https của bạn, bạn có thể chạy ở tên miền http://localhost:*/ để tìm hiểu.

Bạn biết đấy, google luôn ưu tiên cho các nhà phát triển, nên các ý tưởng, sản phẩm, dịch vụ chính thức của họ, bạn có thể tìm hiểu nhanh và demo trên: http://localhost:*/, đến khi deploy, bạn mới cần đến các site: https. Các bạn hẳn còn nhớ mô hình 7 lớp OSI, ở tầng mô tả dữ liệu ngay bên dưới tầng ứng dụng, giao thức https sẽ mã hóa tín hiệu của bạn, nên các buffer tương tác với server sẽ đc mã hóa, và một phần thông tin sẽ đc bảo vệ.

Quay trở lại về SW, nó chạy ngầm khi mà bạn tương tác với server, nó quản lý các request của bạn đến server.

Nó đc thực thi bởi file *.js nào đó, do đó bạn cần đăng ký:

// Thực thi như js phía client thông thường
if (navigator.serviceWorker) {
    navigator.serviceWorker.register('/service-worker.js').then(function(registration) {
      console.log('ServiceWorker registration successful with scope:',  registration.scope);
    }).catch(function(error) {
      console.log('ServiceWorker registration failed:', error);
    });
}

Bài hướng dẫn này sẽ demo để bạn tạo cache cho trang web của bạn, bạn sẽ thấy thay vì lấy nội dung trên server, browser sẽ lấy nội dung trả lời (response) của request đó trong cache.

Nội dung js bạn đăng ký trong file: /service-worker.js sẽ không hoàn toàn giống các nội dung js mà bạn đã viết bên phía client thông thường. Sau đây là mã js mà bạn sẽ viết trên đó.

Mình sẽ chỉ nói về cache: (code trước, mô tả code sau)

var CACHE_NAME = 'cache-v-1.0.1';
var urlsToCache = [
  '/fonts/Roadway.ttf',
  '/launcher-icon-4x.png',
  '/launcher-icon-2x.png',
  '/launcher-icon-1x.png',
  '/index.css',
  '/searchicon.png',
  '/stylesheets/style.css',
  '/offline.html'
];
self.addEventListener('install', function(event) {
  event.waitUntil(self.skipWaiting().then(function(){
  caches.open(CACHE_NAME)
      .then(function(cache) {
        return cache.addAll(urlsToCache);
      })
  }));
});


self.skipWaiting() là để nếu như bạn thay đổi nội dung của file này, lần sau thay vì SW đang chạy nó sẽ chuyển sang trạng thái chờ kích hoạt lại (có một số cách để kích hoạt lại), nó sẽ luôn săn sàng cho dù bạn thay đổi nội dung của file SW này.

self.addEventListener('activate', function(event) {
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.filter(function(cacheName) {
          // Return true if you want to remove this cache,
          // but remember that caches are shared across
          // the whole origin
  return cacheName.indexOf(CACHE_NAME) !== 0;
        }).map(function(cacheName) {
          //console.log("Deleteing cache: " + cacheName);
          return caches.delete(cacheName);
        })
      );
    })
  );
});

Khi active, nó chỉ cho cache đúng tên CACHE_NAME: 'cache-v-1.0.1' ở lại, các cache có tên khác sẽ bị xóa. Do đó khi bạn muốn deploy bản mới, chỉ đơn giản là thay CACHE_NAME thành 'cache-v-1.0.2'.

self.addEventListener('fetch', function(event) {
  var request = event.request;
  if (request.method !== 'GET') { return; }
  event.respondWith(
    caches.match(request)
      .then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }
        var fetchRequest = request.clone();

        return fetch(fetchRequest).then(
          function(response) {
            // Check if we received a valid response
            if(!response || response.status !== 200 || response.type !== 'basic') {
              //console.log("return res:"+response.url + " status: "+response.status);
              return response;
            }
            // IMPORTANT: Clone the response. A response is a stream
            // and because we want the browser to consume the response
            // as well as the cache consuming the response, we need
            // to clone it so we have 2 stream.
            var responseToCache = response.clone();
   var a = request.url,  b = /^https?:\/\/test-sw.herokuapp.com\/api\/fixed/;
   if(a.match(b))
            caches.open(CACHE_NAME)
              .then(function(cache) {
                cache.put(request, responseToCache);
              });

            return response;
          }
        )
.catch(function fallback() {
     caches.match(request).then(function(response) {
        return response || caches.match("/offline.html");
     })
});
      })
    );
});

Code trên là để SW quản lý các request của bạn đến server (tất nhiên là nếu bạn muốn, nếu ko muốn bạn có thể thấy: if (request.method !== 'GET') { return; } -> SW không quản lý các request dạng PUT, POST, DELETE...).
Code trên kiểm tra request đó có trong Cache Storge, để lôi nội dung mà không cần truy cập server.
Bạn cũng thấy tôi còn put vào cache các request có url dạng: https://test-sw.herokuapp.com/api/fixed/* (thêm cache với các truy vấn có phản hồi ít thay đổi)

Trên là demo nhanh để bạn tham khảo: quản lý cache bằng Service Worker, thay cho công nghệ Application Cache

@HoangThanh

Nhận xét