げっとシステムログ

WEB開発メモ

webpack で web-worker してみる話

CONTENTS
  1. なぜそんなことをするのか
  2. まず単純に worker してみる
  3. webpack で build してみる
  4. うまくいかなかったこと
  5. まとめ
  6. 参考資料

なぜそんなことをするのか

この記事を読んで web worker のことを知った。 DOM の描画処理とは別スレッドでメインの処理を行う、というのは Electron でもそんなことを言っていた気がする。

preact で DOM の処理を行い、メインの処理は worker に投げる、という夢を見たので worker してみることにした。

TOP

まず単純に worker してみる

Web Worker のドキュメントを見ながら、まずは単純な worker を動かしてみる。

まずは worker のコードを用意する。

const ctx = self;

setTimeout(() => {
  ctx.postMessage({ message: "hello, world" });
}, 1000);

ctx.addEventListener("message", (event) => {
  console.log(event.data);
});

これを /worker.js で参照できるパスに置いておく。

次に worker を起動するコードを書いていく。

const worker = new Worker("./worker.js");

worker.addEventListener("message", (event) => {
  console.log(event.data);
});

worker.postMessage({ message: "from app" });

これを /app.js で参照できるパスに置く。

作成したファイルは以下の通り。

  • /worker.js
  • /app.js : ./worker.js で worker を参照できる

app.js をロードする html を用意する。

<html>
  <body>
    <script src="/app.js"></script>
  </body>
</html>

これでコンソールに以下の内容が出るはず。

{ message: "from app" }
{ message: "hello, world " }

これを webpack を使って typescript で書けるようにしていく。

TOP

webpack で build してみる

まず、webpack.config.js の output.globalObject を設定する。

output: {
  globalObject: "self",
}

worker のコードではグローバルオブジェクトは self で、window ではない。 そのため、これを設定しておかないと HMR のコードが動かないため、worker のコードまで到達する前にこける。

entry の設定はこんな感じ。

entry: {
  "worker": path.join(__dirname, "lib/worker.ts"),
  "app": path.join(__dirname, "lib/app.ts"),
}

これで先ほどと同じ構成になる。

まずは worker のコード。

// lib/worker.ts
const ctx: Worker = self as any // eslint-disable-line @typescript-eslint/no-explicit-any

setTimeout(() => {
  ctx.postMessage({ message: "hello, world" })
}, 1000)

ctx.addEventListener("message", (event) => {
  console.log(event.data)
})

javascript とほとんど同じだが、worker の api にアクセスするため ctx の型情報を追加している。 そのために any を使っているので eslint-disable を書いてある。

次に worker を起動するコード。

// lib/app.ts
const worker = new Worker("./worker.js")

worker.addEventListener("message", (event) => {
  console.log(event.data)
})

worker.postMessage({ message: "from app" })

これは先ほどの javascript と全く同じだが、typescript で書けるようになっている。

ただし、やり取りするイベントのデータ型は any なので、worker との接続部分はうまいことつなげてあげないと簡単にエラーになる。 worker に postMessage したり、worker からのメッセージをハンドリングするところは注意深く組む必要がある。

とりあえずこれで webpack で worker できるようになった。 やっていることは単純で、worker のコードを build して、それを new Worker() しているだけ。

TOP

うまくいかなかったこと

worker-loader も使ってみたのだけど、README に書いてあるようにやってもうまくいかなかった。 よくわからないまま試行錯誤したので、何かの設定が足りなかったのだろう。

とりあえず単純な形でやり直してみたらうまくいった。

TOP

まとめ

worker が使えるようになったので、これでコンポーネントのメイン部分を worker にできるといいな。

TOP

参考資料

TOP