読者です 読者をやめる 読者になる 読者になる

DRYな備忘録

Don't Repeat Yourself.

webpackのプロジェクトでWebWorker書いててつまずいたメモ

javascript webpack

まずnew Worker(“./my/worker”) というやつ

Workerのコンストラクタにファイルパスを渡すやつからしてwebpackだとどう解決されるのか問題。

以上を参考にして、まず

npm install --save-dev worker-loader

つぎに、webpack.config.jsで

var plugins = [
    new webpack.LoaderOptionsPlugin({
        options: {
            worker: {
                output: {
                    filename: "dest/js/worker.[id].js",
                }
            }
        }
    })
];

として、自作のWorkerを使いたいほうは、

var Worker = require("worker-loader?inline!../../../Workers/my/worker");

とかしとくと

webpack 2.2の標準出力では

Child worker:
                  Asset     Size  Chunks             Chunk Names
    dest/js/worker.0.js  4.59 kB       0  [emitted]  main
       [1] ./~/babel-loader/lib!./src/js/Components/Workers/my/worker.js 1.24 kB {0} [built]
        + 1 hidden modules

となって、生成物としては、以下の648がWorkerを引っ張る共通処理、649が、648を使って自作のWorkerを提供する処理になっているので、

/***/ 648:
/***/ (function(module, exports) {

// http://stackoverflow.com/questions/10343913/how-to-create-a-web-worker-from-a-string

var URL = window.URL || window.webkitURL;
module.exports = function(content, url) {
    try {
        try {
            var blob;
            try { // BlobBuilder = Deprecated, but widely implemented
                var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
                blob = new BlobBuilder();
                blob.append(content);
                blob = blob.getBlob();
            } catch(e) { // The proposed API
                blob = new Blob([content]);
            }
            return new Worker(URL.createObjectURL(blob));
        } catch(e) {
            return new Worker('data:application/javascript,' + encodeURIComponent(content));
        }
    } catch(e) {
        return new Worker(url);
    }
}

/***/ }),

/***/ 649:
/***/ (function(module, exports, __webpack_require__) {

module.exports = function() {
    return __webpack_require__(648)("/** 略 **/", __webpack_require__.p + "dest/js/worker.0.js");
};

このWorkerを使いたいほうでは、649を使ってWorkerインスタンスを作っていることが予想される。以下の通り。

var Worker = __webpack_require__(649);

const worker = new Worker();

となって、うまいことWorkerをwebpackでロードできている感じになった。

importScriptsがどこなの問題

このエントリ書いてるのが2017/02/19 (CET) で、このissueが立ったのが02/08、コントリビュータがbugのlabelをつけたのが02/17なので、なかなかホット。実際、webpackの成果物で、importScriptsをしている部分を見ると、

importScripts("../foo.js", "../bar.js");

importScripts("../foo.js", "../bar.js");

となっており、webpack@2.2.1の時点でimportScripts内のコンテキストは解決されず、生成されたworkerなjsファイル(今回のケースでいうとdest/js/worker.0.js)に対する相対pathを書く必要がある。

TODO: このissueに動きがあったらここに追記する

番外: Chrome拡張において

Chrome拡張においてうまいことdest/js/worker.0.jsというworkerから相対pathでimportScriptsしてくれなかったので、完全なURLを渡す必要があり、以下のようにした。

自作Worker

import imports from "../imports";

const _init = (data) => {
    imports(data.root)("dest/js/nice-lib-x.js", "dest/js/cool-lib-y.js");
};
onmessage = (ev) => {
    switch(ev.data.cmd) {
    case "init": return _init(ev.data);
    case "なんか他のこといろいろ": return _awesome_action(ev.data);
    default:
        return;
    };
};

自作べんりimports関数

/**
 * Workerのコンテキストではchromeは参照できないので、
 * Chrome拡張自体の持つルートディレクトリのURLを
 * アプリケーション側からもらって、それをimportScriptsする必要があるよ。
 */
export default function imports(root) {
    return function() {
        // ↑
        // ここをアローで書くと、argumentsの参照がwebpackによって
        // カリー化関数自体のargumentsに固定されてしまうので
        // functionキーワードで書く必要がある
        if (!root) throw "importScriptsする場合は必ずrootを指定してください";
        const _root = root.replace(/\/+$/, "/"); // ケツの/の重複をきれいにするだけ
        importScripts(...Array.prototype.map.call(arguments, path => _root + path));
    };
}

自作Worker呼ぶ方

var Worker = require("worker-loader?inline!../../../Workers/my/worker");

const worker = new Worker();
worker.postMessage({
    cmd: "init",
    root: chrome.extension.getURL("/")
});

これでうまい感じに

chrome-extension://{extensionId}/dest/js/nice-lib-x.jsに定義した色々が自作Workerの中でアレできるようになった。

雑感

  • webpackちょっとわかってきた
  • さいきんReact/ReactNativeばっかり書いてて頭おかしくなりそう
  • ベルリンのスタートアップはいろいろつらい。早く日本に帰りたい
  • ベルリンは好きだ
    • ビール安い
    • ソーセージをはじめ、肉が安い
    • 食材が安いので料理がたのしい
  • SAOの劇場版、ドイツでやってるっぽい

sao-movie.net

DRYな備忘録として

初めてのJavaScript 第3版 ―ES2015以降の最新ウェブ開発

初めてのJavaScript 第3版 ―ES2015以降の最新ウェブ開発