Как использовать Web Worker под ES6+Webpack

внешний интерфейс GitHub JavaScript Webpack
Как использовать Web Worker под ES6+Webpack

Всем известно, что в HTML 5 добавлено множество API, включая Web Worker. Использование ES5 для написания связанного кода с обычными js-файлами не должно вызвать проблем. Его нужно запускать только в браузерах, поддерживающих H5.

Что, если нам нужно использовать Web Worker в комбинированной среде ES6+Webpack? На самом деле это тоже очень удобно, просто нужно обращать внимание на отдельные точки, а потом записывать ямы, на которые я наступал.

Что касается базовых знаний и базового API Web Worker, то я поставлю его в конец для читателей, которые еще не знакомы с ним или не пользовались системой.

1. Быстро создать инженерную среду

Предположим, у вас уже есть среда разработки кода ES6+Webpack, и она может работать без сбоев; если нет, вы можете клонировать мой репозиторий github:GitHub.com/IR M-GitHub/…

2. Установите и используйте рабочий загрузчик

2.1 Зависимости установки:

$ npm install -D worker-loader
# 或
$ yarn add worker-loader --dev

2.2 Использовать worker-loader прямо в коде

// main.js
var MyWorker = require("worker-loader!./file.js");
// var MyWorker = require("worker-loader?inline=true&fallback=false!./file.js");

var worker = new MyWorker();
worker.postMessage({a: 1});
worker.onmessage = function(event) { /* 操作 */ };
worker.addEventListener("message", function(event) { /* 操作 */ });

Преимущества: файл сценария для написания рабочей логики может быть назван произвольно, главное, чтобы он был передан вworker-loaderможно обрабатывать в середине; Недостаток: каждый раз, когда вводится скрипт-файл рабочей логики, показанный выше код нужно написать один раз, а «воркер-загрузчик!» нужно написать еще N(N>=1) раз.

2.3 Представлено в файле конфигурации веб-пакетаworker-loader

{
  module: {
    rules: [
      {
        // 匹配 *.worker.js
        test: /\.worker\.js$/,
        use: {
          loader: 'worker-loader',
          options: {
            name: '[name]:[hash:8].js',
            // inline: true,
            // fallback: false
            // publicPath: '/scripts/workers/'
          }
        }
      }
    ]
  }
}

При этом может быть предусмотрена конфигурацияinlineсобственностьtrueВстроить работника как большой двоичный объект; Обратите внимание, что для браузера будет дополнительно создан встроенный режим.chunk, даже для браузеров, не поддерживающих встроенных исполнителей; если такие браузеры хотят отключить это поведение, просто установитеfallbackпараметр установлен наfalseВот и все.

3. Та же политика происхождения

Web Worker строго придерживается политики одного и того же происхождения.Если статические ресурсы веб-пакета и код приложения имеют разное происхождение, они, скорее всего, будут заблокированы браузером, и этот сценарий происходит часто. Есть два решения для Web Workers, чтобы столкнуться с этой ситуацией.

3.1 Первый

установивworker-loaderопционный параметрinlineВставьте работника в формат данных BLOB-объектов вместо загрузки файла скрипта, чтобы использовать работника:

App.js

import Worker from './file.worker.js';

webpack.config.js

{
  loader: 'worker-loader'
  options: { inline: true }
}

3.2 Второй

установивworker-loaderопционный параметрpublicPathЧтобы переписать URL-адрес загрузки рабочего сценария, конечно, сценарий также должен храниться в том же месте:

App.js

// This will cause the worker to be downloaded from `/workers/file.worker.js`
import Worker from './file.worker.js';

webpack.config.js

{
  loader: 'worker-loader'
  options: { publicPath: '/workers/' }
}

4. В режиме devServer сообщается об ошибке «окно не определено».

Если используетсяwebpack-dev-serverЕсли запущен локальный сервер отладки, в консоли может появиться сообщение об ошибке: «Uncaught ReferenceError: окно не определено»

Так или иначе, я столкнулся с этим, и я долго искал безрезультатно.В это время я умылся и успокоился, чтобы исследовать проблему.worker-loader,webpack-dev-serverа такжеwebpackЯ нашел это в выпусках репозитория github и, наконец, нашелwebpackВопрос друга по коду был найден в репозитории github, и был дан официальный ответ:

Просто добавьте пару свойств под вывод в файле конфигурации webpack:globalObject: 'this'

output: {
  path: DIST_PATH,
  publicPath: '/dist/',
  filename: '[name].bundle.[hash:8].js',
  chunkFilename: "[name].chunk.[chunkhash:8].js",
  globalObject: 'this',
},

5. Фон, на котором появляется веб-воркер

Движок JavaScript работает в одном потоке, а трудоемкие операции ввода-вывода в JavaScript обрабатываются как асинхронные операции, включая ввод-вывод с помощью клавиатуры, мыши, события ввода-вывода, размер окнаresizeсобытия, таймеры (setTimeout,setInterval), события, обратные вызовы сетевого ввода-вывода запроса Ajax и т. д. Когда возникают эти асинхронные задачи, они помещаются в очередь событийных задач браузера и выполняются одна за другой по принципу «первым пришел — первым вышел», когда поток выполнения среды выполнения JavaScript простаивает, но в конце концов , это все еще один поток.

Обычно достаточно асинхронного программирования (promise,async/await), при столкновении с очень сложными операциями, такими как оптимизация или преобразование распознавания изображений, реализация игрового движка H5, операции алгоритма шифрования и дешифрования и т. д., постепенно будут отражаться их недостатки. Длительно работающий js-процесс может привести к тому, что браузер заморозит пользовательский интерфейс и ухудшит работу пользователя. Есть ли способ отделить сложный расчет от кода бизнес-логики, чтобы расчет можно было запустить, не блокируя пользовательский интерфейс для получения обратной связи?

Стандарт HTML5 передал спецификацию Web Worker, которая определяет набор API-интерфейсов, позволяющих запускать js-программу в другом потоке, отличном от основного. Рабочие потоки позволяют разработчикам писать фоновые программы, которые могут работать в течение длительного времени, не прерываясь пользователями, выполнять транзакции или логику и в то же время обеспечивать своевременный отклик страницы на запросы пользователей.Заморозить пользовательский интерфейс.

5. Типы веб-воркеров

Я всегда думал, что есть только один тип, а типов рабочих может быть несколько. Ответ положительный, его можно разделить на два типа:

  1. Преданный работник, преданный веб-работник
  2. Общий рабочий, общий веб-воркер

Доступ к «частному работнику» может получить только страница, которая его создала, в то время как «общий работник» может использоваться на одной и той же странице, открытой на нескольких вкладках браузера.

В js-кодеWokerПредставитель классаDedicated Worker;Shared WorkerПредставитель классаShared Web Worker.

Я написал следующий пример кода непосредственно в ES5. Вышеизложенное научило вас использовать его в среде ES6 + Webpack. Каждый должен практиковать эту миграцию и делать больше.

6. Как создать воркера

очень простой

// 应用文件 app.js
var worker = new Worker('./my.worker.js'); // 传入 worker 脚本文件的路径即可

7. Как общаться с работниками

Это можно сделать двумя способами:

Файл приложения app.js

// 创建 worker 实例
var worker = new Worker('./my.worker.js'); // 传入 worker 脚本文件的路径即可
// 监听消息
worker.onmessage = function(evt){
  // 主线程收到工作线程的消息
};
// 主线程向工作线程发送消息
worker.postMessage({
  value: '主线程向工作线程发送消息'
});

рабочий файл my.worker.js

// 监听消息
this.onmessage = function(evt){
  // 工作线程收到主线程的消息
};
this.postMessage({
  value: '工作线程向主线程发送消息'
});

8. Глобальная область действия работника

Самое важное, что нужно знать об использовании Web Worker, это то, что исполняемый им код js находится полностью в другой области видимости и не разделяет область действия с кодом текущего основного потока. В Web Worker также есть глобальный объект и другие объекты и методы, но его код не может получить доступ к DOM и не может повлиять на внешний вид страницы.

Глобальным объектом в Web Worker является сам объект worker, т.е.thisа такжеselfВсе ссылки являются рабочими объектами, грубо говоря, как и предыдущий абзац вmy.worker.jsкод,thisполностью заменяемыйself, можно даже опустить.

Для облегчения обработки данных сам Web Worker представляет собой минимальную среду выполнения, которая может получать доступ к следующим данным или использовать их:

  • Минимизироватьnavigatorобъект включатьonLine, appName, appVersion, userAgentа такжеplatformАтрибуты
  • только чтениеlocationобъект
  • setTimeout(), setInterval(), clearTimeout(), clearInterval()метод
  • XMLHttpRequestКонструктор

9. Как завершить рабочий поток

Если вы не хотите, чтобы Worker продолжал работать в определенное время, то нам нужно завершить поток, вы можете вызвать Worker в основном потокеterminateметод или вызов в соответствующем потокеcloseметод:

Файл приложения app.js

var worker = new Worker('./worker.js');
// ...一些操作
worker.terminate();

Рабочий файл my.worker.js

self.close();

10. Механизм обработки ошибок воркера

В частности, пока js внутри Worker сталкивается с ошибкой во время выполнения, он вызываетerrorмероприятие. происходитьerrorКогда происходит событие, объект события содержит три свойства:filename, linenoа такжеmessage, которые представляют имя файла, в котором произошла ошибка, номер строки и полное сообщение об ошибке соответственно.

worker.addEventListener('error', function (e) {
  console.log('MAIN: ', 'ERROR', e);
  console.log('filename:' + e.filename + '-message:' + e.message + '-lineno:' + e.lineno);
});

11. Импорт скриптов и библиотек

Рабочие потоки могут получить доступ к глобальной функцииimportScripts()Для импорта скриптов эта функция принимает ноль или более URI в качестве параметров для импорта ресурсов; допустимы все следующие примеры:

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

Браузер загружает и запускает каждый из перечисленных сценариев. Глобальный объект в каждом скрипте может использоваться рабочими. Выдает, если скрипт не может быть загруженNETWORK_ERRORИсключение, следующий код не может быть выполнен. в то время как ранее выполненный код (в том числе с использованиемwindow.setTimeout()Асинхронно исполняемый код) по-прежнему сможет выполняться.importScripts()Последующие объявления функций по-прежнему сохраняются, поскольку они всегда выполняются перед другим кодом.

Примечание: Порядок загрузки скрипта не фиксирован, но он будет выполняться в соответствии с поступающимimportScripts()в порядке имен файлов. Этот процесс выполняется синхронно, пока все скрипты не будут загружены и запущены,importScripts()вернусь.

Приложение: Ссылки по теме

URL-адрес рабочего-загрузчика на github:GitHub.com/Webpack-con…

китайская документация webpack (сообщество):doc.Web pack-China.org/loaders/ Я делаю…

китайская документация webpack (сторонняя сторона):Woohoo. CSS88.com/doc/Web pack…  

Область проблемы в HMR в режиме devServer: «Webpack 4.0.1 | WebWorkerwindow is not definedGitHub.com/Webpack/Веб…

Тематическая область, которая полностью решает вышеперечисленные проблемы: «Добавитьtarget: "universal"GitHub.com/Webpack/Веб…


微信公众号