LoginSignup
14
11

More than 3 years have passed since last update.

nextjs+TypeScript環境でWebWorkerを使う

Last updated at Posted at 2019-09-05

nextjs+TypeScript環境でマルチスレッド処理したくなったので、web workerを使うための環境構築メモ

いろいろ試したけど、結局以下のURLのやり方でいけた
https://github.com/zeit/next.js/issues/4768#issuecomment-501082855

今回はすでにTypeScript環境は構築済みのものとする

サンプルプロジェクトは以下
https://github.com/kuwabataK/next-template/tree/service-worker-test

環境

  • nextjs ver 9.0.5
  • typescript ver 3.6.2

install

worker-loader@zeit/next-workers をインストールする

npm install worker-loader @zeit/next-workers

tsconfig の設定

libwebWorkerを追加する

tsconfig.json
{
  "compilerOptions": {
   ... 
    "lib": [
      "dom",
      "es2017",
      "webworker"  // 追加
    ],
   ... 
}

next.config.jsの設定

以下のようなnext.config.jsをプロジェクト直下に作成する

next.config.js
/* eslint-disable @typescript-eslint/explicit-function-return-type */
module.exports = {
  webpack(config) {
    config.module.rules.push({
      test: /\.worker\.js$/,
      loader: 'worker-loader',
      options: {
        // inline: true,
        name: 'static/[hash].worker.js',
        publicPath: '/_next/'
      }
    })
    // Overcome webpack referencing `window` in chunks
    config.output.globalObject = `(typeof self !== 'undefined' ? self : this)`
    return config
  }
}

typesの設定

TypeScriptのmodule importを解決するため以下のようなdeclareファイルを作成する。
本当はworkerごとに作ったほうが良いけれど、とりあえず動かすだけなら以下でOK

/src/typings/custom.d.ts
declare module 'worker-loader?name=static/[hash].worker.js!*' {
  class WebpackWorker extends Worker {
    constructor()
  }

  export default WebpackWorker
}

とりあえずここまでで準備は完了

workerの作成

以下のようにexample.worker.tsを作る。workerの名前は、hogehoge.worker.tsにしないと動かないので注意。今回はpostMessageで受け取った値を2乗して返すだけのWorkerを作る

/src/workers/example.worker.ts
import { square } from '../utils/calc' // 他のtsファイルをimportして使うこともできるよ

/* eslint-disable @typescript-eslint/no-explicit-any */
const ctx: Worker = self as any

ctx.addEventListener('message', async event => {
  console.log('worker側だよ!! 受け取った値は', event.data)
  const res = square(event.data)
  ctx.postMessage({ input: event.data, output: res }) // 呼び出し元にEventを発火して結果を返す
})

export default ctx
/src/utils/calc.ts
/**
 * 引数を2乗して返すだけの関数
 * このメソッドはWebWorker関係なくプロジェクトのどこからでも呼び出せる
 * @param a
 */
export function square(a: number): number {
  return a * a
}

pagesでの呼び出し方

以下のような感じでworkerを呼び出せる

/pages/web-worker.tsx
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import * as React from 'react'
import Layout from '../src/components/template/Layout'
import Link from 'next/link'
// worker import時はfileパスの頭に 'worker-loader?name=static/[hash].worker.js!' をつける
import MyWorker from 'worker-loader?name=static/[hash].worker.js!../src/workers/example.worker'

let worker: MyWorker

const WebWorker: React.FC = () => {
  // mount時にworkerを作成する
  React.useEffect(() => {
    worker = new MyWorker()
    // unmount時にworkerをterminateする
    return () => worker.terminate()
  }, [])

  /**
   * workerから帰ってくるイベント発火時に実行される関数
   * @param event
   */
  const onWorkerMessage = (event: { data: number }) => {
    console.log('Workerから結果が帰ってきたよ!!!', event.data)
  }
  /**
   * workerの処理を実行する
   */
  const exec = () => {
    worker.onmessage = onWorkerMessage // workerから帰ってくる処理結果をひろうリスナーを登録
    worker.postMessage(33) // イベント経由でworkerに処理を依頼
  }

  return (
    <Layout title="WebWorkerテスト | Next.js + TypeScript Example">
      <h1>WebWorkerテスト</h1>
      <button onClick={exec}>WebWorker呼び出し</button>
      <p>
        <Link href="/">
          <a>Go home</a>
        </Link>
      </p>
    </Layout>
  )
}

export default WebWorker

型定義ファイルを真面目に作ってないせいで型推論がうまく効いてくれないんだけれども、とりあえず動くので良しとする

14
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
11