絶妙に引っかかったのでメモ。
パッケージ導入&設定
worker-loader導入
$ npm install worker-loader
WebPack環境でWeb Workers APIを扱いやすくしてくれるloaderパッケージを導入します。
~/nuxt.config.js
build: { extend: (config, ctx) => { if (ctx.isClient) { config.module.rules.push({ test: /\.worker\.js$/, loader: 'worker-loader', }) } } }
クライアント側のビルドでのみworker-loaderを使うようにします。
~/tsconfig.json
{ "compilerOptions": { "typeRoots": [ "./types", "./node_modules/@types", "./node_modules/@nuxt/vue-app/types" ] } }
ここは型定義ファイルがちゃんと読み込まれてるかどうか不安だったのでいろいろ追記してみただけで、基本的には触らなくてよいです。
~/types/worker-loader.d.ts
declare module 'worker-loader!*' { class WebpackWorker extends Worker { constructor(); } export default WebpackWorker; }
型定義ファイルを作ります。
.d.ts
サフィックスのファイルは必ず declare module
で書く決まりがあるようです。
extends Worker
の部分は、別のWorkerを継承するとほかの色々なワーカーとしても使えるみたいです。
(Dedicated・Shared・Serviceなど)
ワーカーとメインを書く
~/assets/test.worker.ts
const ctx: Worker = self as any; ctx.addEventListener('message', async event => { console.log('Workerだよ!受信したよ →', event.data); ctx.postMessage('Workerからのデータです'); });
実際のワーカーをTypeScriptで書いていきます。
ここではassetsディレクトリにいれてますがどこでも良さそうです。
イベントリスナを使い、メインスレッドから何かしらのデータが来たら返事するような簡単なやつです。
~/pages/index.vue
<script lang="ts"> import { log } from 'util'; import { Component, Vue } from 'nuxt-property-decorator'; import Worker from 'worker-loader!~/assets/test.worker'; @Component export default class Index extends Vue { created() { if (process.title === 'browser') { const worker = new Worker(); worker.addEventListener('message', (event: MessageEvent) => { console.log('Workerから何かきたよ →', event.data) }); worker.postMessage('Workerにメッセージ送信しました'); } } } <script>
ワーカーのimportは
worker-loader!~/assets/test.worker
のように、worker-loader!
を先頭につけ、ワーカーのファイル位置を後に続けて指定します。拡張子.ts
は書かなくてよいです。
VSCode補完だとLintエラーみたいなのが出てめっちゃ気になりますが普通に動きます。(気にしすぎてここではまりまくった)
ビルド後に問題なくバインドされるんだけどVSCodeではそこまで捕捉できないから、だと思っている(たぶん)SSR時にどっち側なのか判定をつけます。
ワーカーはクライアント側でしか動かないのでprocess
の中身を見て判定します。
process.browser
の真偽値を見ればよいのですがTSでは存在しない(実際には存在するがdeprecated扱いなんだと思う)とのことなのでprocess.title
の文字列値で判定しています。
動かしてみる
しっかりイベント駆動でメッセージの送受信ができていることが確認できました!