Vuex のアクションの処理を Web Worker にやらせる
はじめに
Worker を未だに使う機会が無い(むしろ私が実用性を分かってない)ので、今後のために勉強がてら Vuex のアクションで行ってる処理を Worker へ移行する実装をしてみます
なんとなく理解できたら、次はworker-domを使ってみたいなと思っております
ちなみに Vuex のアクションはデフォルトで非同期処理をサポートしており、非同期処理で捌けるケースがほとんどだと思うので、Worker を使って何かを行うケースはあまり無いかもしれません
ただし、重い計算処理があり、非同期による並行処理では解決できない場合(処理を分割して処理速度を改善したいケース)やそもそも非同期処理を使いたくないケースなどがあれば Worker スレッドによる並列処理は良いと思います
私が携わったプロジェクトでは Vuex のアクションは主に API サーバにリクエストを投げて、DB から取得したデータを受け取って、Mutation にコミットする処理をしています
今回なぜ Vuex のアクションを題材にしたかというと、普段仕事で Vue+Vuex を使っているのもありますが、アクションを呼ぶ度に Worker で処理させるような実装であればイメージしやすく、なんとなく作りやすそうだなって思ったからです
実装の方針に関して
「vue web worker」とかで検索するとまずvue-workerというライブラリがヒットすると思います
vue-worker の実装はほぼ無くてsimple-web-workerというライブラリを Vue.prototype に$worker
という名前で追加しているだけです
simple-web-worker の方は Web Worker を操作するユーティリティ関数をいくつか提供してくれてて、とりあえずrunの実装を見てみると Worker に postMessage して Promise でラップして返してくれてるようです
便利そうですが、ライブラリの更新が止まってる感じがするので、今回は素の Web Worker だけで実装しようと思います
worker-loader を使う
Worker をworker-loaderやworker-pluginを使ってモジュール化して使う方法が便利そうです
worker-loader の処理でモジュール化された Worker を import して使えるようになります
手軽さと webpack の alias 設定が効くので worker-loader を採用します
module: {
rules: [
{
test: /\.worker\.ts$/,
use: ['worker-loader']
}
]
}
一応補足ですが TS や ES で書いた Worker スクリプトを worker-loader だけでトランスパイルなどはできないので、別途以下のような loader 設定も必要です
module: {
rules: [
{
test: /\.worker\.ts$/,
use: ['worker-loader']
},
// TSのトランスパイルやLintなどのloaderは別途追加しておく
{
test: /\.ts$/,
use: ['ts-loader', 'tslint-loader'],
exclude: /node_modules/
}
]
}
簡単な Worker の実装
とりあえず Vue から Worker への送受信の簡単な実装を試してみます
<script lang="ts">
import Worker from '@/worker/sample.worker'
import Vue from 'vue'
export default Vue.extend({
name: 'App',
created() {
const w = new Worker()
w.postMessage({ test: 'Send from main thread' })
w.addEventListener('message', e => console.log(e.data))
}
})
</script>
const w: Worker = self as any
w.postMessage({ test: 'Send from worker thread' })
w.addEventListener('message', e => console.log(e.data))
それぞれのスレッド上で実行された console.log が出力されると思います
また、ブラウザの開発ツールなどでネットワークを見ると Worker が動いていることが確認できます
Vuex のアクションの実装
API 経由で DB からレポートデータを取得するアクションを想定して、実装しています
- 日付パラメータを変えながらアクションを 4 回リクエスト
- アクションで Worker インスタンスを作成
- Worker スレッドで API リクエストを投げ、色々データ加工処理とかして、メインスレッドへ返す
- メインスレッドで Mutation.commit する
アクションをパラメータを変えながら 4 回呼ぶ
<script lang="ts">
import Vue from 'vue'
import { mapActions } from 'vuex'
export default Vue.extend({
name: 'App',
methods: {
...mapActions({
getReport: 'GET_REPORT'
})
},
created() {
;[
{ date: { from: '2019-01-01', to: '2019-01-02' } },
{ date: { from: '2019-01-03', to: '2019-01-04' } },
{ date: { from: '2019-01-05', to: '2019-01-06' } },
{ date: { from: '2019-01-07', to: '2019-01-08' } }
].forEach(this.getReport)
}
})
</script>
アクションではアクションが呼ばれる度に Worker スレッドを起動し、パラメータを渡しています
import SampleWorker from '@/worker/sample.worker'
import { ActionContext, ActionTree } from 'vuex'
const actions: ActionTree<any, any> = {
['GET_REPORT'](
context: ActionContext<any, any>,
date: { from: string, to: string }
) {
const params = {
from_date: date.from,
to_date: date.to
}
const w: Worker = new SampleWorker()
w.postMessage({ params })
w.addEventListener('message', e => {
context.commit('GET_REPORT', e.data.reports)
w.terminate()
})
}
}
export default actions
Worker では受け取ったパラメータを使って API リクエストを投げて、受け取ったデータをメインスレッドへ返す
import axios from 'axios'
const w: Worker = self as any
w.addEventListener('message', e => {
axios
.get('https://example.co.jp/reports', {
params: e.data.params
})
.then(res => {
const reports = res.data.reports // ここで色々加工処理的なことをやる
w.postMessage({ reports })
})
.catch(err => console.error(err.response))
})
Author And Source
この問題について(Vuex のアクションの処理を Web Worker にやらせる), 我々は、より多くの情報をここで見つけました https://qiita.com/kurosame/items/7a03b2a45e9cbe6ac9c6著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .