kangyana / daily-question

When your heart is set on something, you get closer to your goal with each passing day.
https://www.webpack.top
MIT License
3 stars 0 forks source link

【Q034】web worker #34

Open kangyana opened 1 year ago

kangyana commented 1 year ago

1. web worker

Web Worker 为 Web 内容在后台线程中运行脚本提供了一种简单的方法。 worker 可以与 js 互相通信,线程可以执行任务而不干扰用户界面。

常用来做 耗时的计算,Service worker等后台任务

2. worker的使用

生成一个专用 worker

// main.js
var myWorker = new Worker('worker.js');

worker 中消息的接收和发送

你可以通过 postMessage() 方法和 onmessage 事件处理函数触发 workers 的方法。

主线程向 worker 发消息

// main.js
first.onchange = function() {
  myWorker.postMessage([first.value,second.value]);
  console.log('Message posted to worker');
}

second.onchange = function() {
  myWorker.postMessage([first.value,second.value]);
  console.log('Message posted to worker');
}

worker接收消息的回调

// worker.js
onmessage = function(e) {
  console.log('Message received from main script');
  var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
  console.log('Posting message back to main script');
  postMessage(workerResult);
}

主线程响应 worker 回传的消息

// main.js
myWorker.onmessage = function(e) {
  result.textContent = e.data;
  console.log('Message received from worker');
}

终止 worker

主线程调用 terminate 方法关闭

// main.js
myWorker.terminate();

worker线程调用 close 方法关闭

// worker.js
close();

处理错误

当 worker 出现运行中错误时,它的 onerror 事件处理函数会被调用。 该事件不会冒泡并且可以被取消;为了防止触发默认动作,worker 可以调用错误事件的 preventDefault() 方法。

// worker.js
onerror = function(err) {
  console.log('错误消息:', err.message);
  console.log('错误文件:', err.filename);
  console.log('错误行数:', err.lineno);
}

生成 subworker

worker 能够生成更多的 worker。

引入脚本与库

Worker 线程能够访问一个全局函数 importScripts() 来引入脚本,该函数接受 0 个或者多个 URI 作为参数来引入资源。

importScripts();                        /* 什么都不引入 */
importScripts('foo.js');                /* 只引入 "foo.js" */
importScripts('foo.js', 'bar.js');      /* 引入两个脚本 */

浏览器加载并运行每个的脚本,脚本中的全局对象都能够被 worker 使用。 如果脚本无法加载,将抛出 NETWORK_ERROR 异常,接下来的代码也无法执行,而之前执行的代码 异步执行的代码 依然能够运行。 importScripts() 之后的函数声明有效,因为函数提升。

备注: 脚本的下载顺序不固定,但执行时会按照传入 importScripts() 中的文件名顺序进行。 这个过程是同步完成的;直到所有脚本都下载并运行完毕,importScripts() 才会返回。

kangyana commented 1 year ago

3. pwa 渐进式 Web 应用

PWA(Progressive Web Apps)运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序。 PWA 使 web应用 具有与 原生应用 相同的用户体验。

4. 通过 Service workers 让 PWA 离线工作

Service Worker 是浏览器和网络之间的虚拟代理。 它解决决了如何正确 缓存网站资源 并使其在离线时可用的问题。

Service Worker 的生命周期

注册sw后,sw.js 文件会自动下载、安装,然后激活。

注册 Service Worker

if('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/pwa-examples/js13kpwa/sw.js');
};

安装

安装sw,并缓存文件

var cacheName = 'js13kPWA-v1'; // 缓存版本号
var appShellFiles = [
  '/pwa-examples/js13kpwa/index.html',
  '/pwa-examples/js13kpwa/app.js',
  '/pwa-examples/js13kpwa/style.css',
]; // 需要缓存的资源路径

self.addEventListener('install', function(e) {
  console.log('SW 安装');
  // 等待缓存后再安装
  e.waitUntil(
    caches.open(cacheName).then(function(cache) {
          console.log('SW 缓存资源');
      return cache.addAll(appShellFiles);
    })
  );
});

拦截请求

缓存中查找资源是否被缓存:如果存在,将会返回缓存的资源;如果不存在,会转而从网络中请求数据,然后将它缓存起来。

self.addEventListener('fetch', function(e) {
  e.respondWith(
    caches.match(e.request).then(function(r) {
      console.log('SW 拦截到请求'+e.request.url);
      return r || fetch(e.request).then(function(response) {
                return caches.open(cacheName).then(function(cache) {
          console.log('[Service Worker] Caching new resource: '+e.request.url);
          cache.put(e.request, response.clone());
          return response;
        });
      });
    })
  );
});

更新

当有资源变动时,更新版本号,会创新一个新sw缓存文件。

appShellFiles.push('/pwa-examples/js13kpwa/icons/icon-32.png');

cacheName = 'js13kPWA-v2';

旧的 Service Worker 仍然会正常运行,直到没有任何页面使用到它为止。

激活

用来清理不需要的缓存。

self.addEventListener('activate', function(e) {
  console.log('SW 激活');
  e.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if(cacheName.indexOf(key) === -1) {
          return caches.delete(key);
        }
      }));
    })
  );
});