xor

二兎を得るか、一兎をも得ざるか

Nuxt.tsでWeb Workersを使う【Nuxt.js + TypeScript】

絶妙に引っかかったのでメモ。

パッケージ導入&設定

worker-loader導入

$ npm install worker-loader

WebPack環境でWeb Workers APIを扱いやすくしてくれるloaderパッケージを導入します。

github.com

~/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エラーみたいなのが出てめっちゃ気になりますが普通に動きます。(気にしすぎてここではまりまくった)
    f:id:ukkz:20201010142758p:plain
    ビルド後に問題なくバインドされるんだけどVSCodeではそこまで捕捉できないから、だと思っている(たぶん)

  • SSR時にどっち側なのか判定をつけます。
    ワーカーはクライアント側でしか動かないので process の中身を見て判定します。
    process.browser の真偽値を見ればよいのですがTSでは存在しない(実際には存在するがdeprecated扱いなんだと思う)とのことなので process.title の文字列値で判定しています。

動かしてみる

f:id:ukkz:20201010143448p:plain

しっかりイベント駆動でメッセージの送受信ができていることが確認できました!