Vue-cli-plugin-electron-builder: Web workers don't work

Created on 23 Sep 2019  ·  10Comments  ·  Source: nklayman/vue-cli-plugin-electron-builder

Describe the bug
Web workers don't work, I get the following error:

testWorker.js:1       
Uncaught SyntaxError: Unexpected token <

When I click on the error, I see the code from the index.html for some reason:

image

structure

App.vue
workers
|_ testWorker.js

App.vue

mounted() {
  let testWorker = new Worker('workers/testWorker.js')
  testWorker.postMessage('message from App.vue')
  testWorker.addEventListener("message", (event) => {
    console.log("Received data from worker:", event)
  })
}

testWorker.js

onmessage = function (event) {
  postMessage({ result: 'Reply from worker' })
}

Environment:

  • OS and version: win10
  • vue-cli-plugin-electron-builder version : 1.4.0
  • electron version: 6.0.7

Most helpful comment

Never mind. I figured out how to make it work. For anyone else having troubles with this, here's what I did:

  1. Install worker-loader:
yarn add -D worker-loader
  1. Add the following in your vue.config.js:
module.exports = {
  configureWebpack: {
    module: {
      rules: [
        {
          test: /\.worker\.js$/,
          use: { loader: 'worker-loader' }
        }
      ]
    }
    ...
  1. In your background.js (main Electron file), inside webPreferences: {} of the window, add nodeIntegrationInWorker: true if you want to be able to use Node's modules inside the worker. Create a worker file (e.g. workers/worker1.js) and create a listener in there, here's some example:
const path = require('path')

// Listen to messages from parent thread
self.addEventListener('message', (event) => {
  console.log('Worker received message:', event.data)
  // Do some calculations and send the result back to parent thread
  let parsedPath = path.parse('C:/test')
  self.postMessage({result: parsedPath})
})
  1. Import and init the worker where you need it:
<script>
import Worker1 from 'worker-loader!../workers/worker1'
let worker = new Worker1()
...
mounted() {
  worker.addEventListener("message", function (event) {
    console.log('Received data from worker:', event.data)
  })
}
...
methods: {
  someMethod() {
    // Send some message to the worker
    worker.postMessage({parse: 'C:/test'})
  }
}

Notes:
Don't try to use node's native worker_threads, they don't work in Electron

Tags for people who's trying to find it:

  • Web worker Module not found: Error: Can't resolve
  • Webpack electron-builder web worker doesn't work
  • How to use modules inside web worker in electron webpack

All 10 comments

Never mind. I figured out how to make it work. For anyone else having troubles with this, here's what I did:

  1. Install worker-loader:
yarn add -D worker-loader
  1. Add the following in your vue.config.js:
module.exports = {
  configureWebpack: {
    module: {
      rules: [
        {
          test: /\.worker\.js$/,
          use: { loader: 'worker-loader' }
        }
      ]
    }
    ...
  1. In your background.js (main Electron file), inside webPreferences: {} of the window, add nodeIntegrationInWorker: true if you want to be able to use Node's modules inside the worker. Create a worker file (e.g. workers/worker1.js) and create a listener in there, here's some example:
const path = require('path')

// Listen to messages from parent thread
self.addEventListener('message', (event) => {
  console.log('Worker received message:', event.data)
  // Do some calculations and send the result back to parent thread
  let parsedPath = path.parse('C:/test')
  self.postMessage({result: parsedPath})
})
  1. Import and init the worker where you need it:
<script>
import Worker1 from 'worker-loader!../workers/worker1'
let worker = new Worker1()
...
mounted() {
  worker.addEventListener("message", function (event) {
    console.log('Received data from worker:', event.data)
  })
}
...
methods: {
  someMethod() {
    // Send some message to the worker
    worker.postMessage({parse: 'C:/test'})
  }
}

Notes:
Don't try to use node's native worker_threads, they don't work in Electron

Tags for people who's trying to find it:

  • Web worker Module not found: Error: Can't resolve
  • Webpack electron-builder web worker doesn't work
  • How to use modules inside web worker in electron webpack

Thanks for providing such a detailed guide! I will link to this in the docs when I get the chance.

@nklayman sure thing!
As a junior developer I had no idea that I have to use webpack loaders. At least others won't need to spend several hours trying to figure out how to make it work. There's almost no information about this issue on the internet.

@AlexSHoffman Nice guide ! But after build I get the following error:

Uncaught Error: ENOENT, worker\api\exports\electron.js not found in C:\path-to-code-folder\dist_electron\win-unpacked\resources\electron.asar

@dooodgers not sure, but it seems the app doesn't have that worker folder bundled. Try messing about with vue.config.js, maybe add and specify files: []:

module.exports = {
  configureWebpack: {
    ...
  },
  pluginOptions: {
    electronBuilder: {
      builderOptions: {
        files: [
          "**/*",
          "./build/**/*",
          "./worker/api/exports/**"
        ],
      }
    }
  }
}

Perhaps @nklayman knows how to fix it?

Required node module should be bundled along with my worker file?
I builed with asar: false and required node module and worker are bundled in one file.

Update: I checked with simple worker (without importing module) and it works.

Please understand that English is not my native language
I am try use vim-wasm in my project
it has some problem
Firstly i have to explain vim-wasm use the Web Worker's structure
Major script is vimwasm.js and vim.js in his offical example

vimwasm.js: ES Module to manage lifetime of Web Worker
vim.js: Web Worker script to drive vim.wasm

in my code
import {VimWasm}from 'vim-wasm' to source code in vimwasm.js VimWasm is a eport class and for use fllowing three argument

  • canvas: document.getElementById('vimcanvas'),
  • input: document.getElementById('viminput'),
  • workerScriptPath: '../../node_modules/vim-wasm/vim.js

use the above in his script vimwasm.js internal to do a new Worker
so

  • webpack can't work it ( i don't how to dispose in any way it make be agiated 😡 )

    • i try to install and use worker-loader (don't worker good)

    • image

  • when launch app it become Uncaught SyntaxError: Unexpected token '<'

    vimwasm.js soucre code

export class VimWasm {
    constructor(opts) {
        const script = opts.workerScriptPath;
        if (!script) {
            throw new Error("'workerScriptPath' option is required")
        }
        this.handleError = this.handleError.bind(this);
        this.worker = new VimWorker(script, this.onMessage.bind(this), this.handleError);
        if ("canvas" in opts && "input" in opts) {
            this.screen = new ScreenCanvas(this.worker, opts.canvas, opts.input)
        } else if ("screen" in opts) {
            this.screen = opts.screen
        } else {
            throw new Error("Invalid options for VimWasm construction: " + JSON.stringify(opts))
        }
        this.perf = false;
        this.debug = false;
        this.perfMessages = {};
        this.running = false;
        this.end = false
    }

```node
export class VimWorker {
constructor(scriptPath, onMessage, onError) {
this.worker = new Worker(scriptPath);
this.worker.onmessage = this.recvMessage.bind(this);
this.worker.onerror = this.recvError.bind(this);
this.sharedBuffer = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 128)); //mark
this.onMessage = onMessage;
this.onError = onError;
this.onOneshotMessage = new Map;
this.debug = false;
this.pendingEvents = []
}

#### offical thumbnail
<details>
<summary></summary>
<img src="https://user-images.githubusercontent.com/45911286/73616366-5888ae00-464d-11ea-88b4-3faf3729664d.png">
</details>


#### code in component

```vue
<template lang="html">
  <div>
      <canvas id="vimcanvas" ref="vimcanvas"></canvas>
      <input autocomplete="off"  ref="viminput" id="viminput" autofocus>
  </div>
</template>


<script>
// import {
//     VimWasm
// } from '../../node_modules/vim-wasm/vimwasm.js'
import {VimWasm}from 'vim-wasm'

export default {
    name: 'vim',
        mounted:function(){
        const vim = new VimWasm({
            canvas: document.getElementById('vimcanvas'),
            input: document.getElementById('viminput'),
            workerScriptPath: '../../node_modules/vim-wasm/vim.js'
        })
        vim.start()
    }
    }
</script>

<style lang="css" scoped>
canvas{
    width: 500px;
    height: 500px;
    background: #add8e6;
}
</style>

launch Error

image

I will add some tips from my own experience.

  1. Do not insert the configuration into the vue.config.js:
module.exports = {
  configureWebpack: {
    module: {
      rules: [
        {
          test: /\.worker\.(js|ts)$/,
          use: { loader: 'worker-loader' }
        }
      ]
    }
    ...

Use only inline syntax

import MyWorker from 'worker-loader!../workers/my-worker'
  1. For using with TypeScript add the following in your typings:
declare module "worker-loader!*" {
  class WebpackWorker extends Worker {
    constructor();
  }

  export default WebpackWorker;
}
  1. If you encounter a TypeError: Cannot read property 'app' of undefined, try to pass all the associated code to a separate chunk and load it asynchronously.

I think the above guide is no longer relevant. I had a lot of problems with this approach. (https://github.com/vuejs/vue-cli/issues/5283 one of the most unpleasant)

Never mind. I figured out how to make it work. For anyone else having troubles with this, here's what I did:

1. Install `worker-loader`:
yarn add -D worker-loader
1. Add the following in your `vue.config.js`:
module.exports = {
  configureWebpack: {
    module: {
      rules: [
        {
          test: /\.worker\.js$/,
          use: { loader: 'worker-loader' }
        }
      ]
    }
    ...
1. In your `background.js` (main Electron file), inside `webPreferences: {}` of the window, add `nodeIntegrationInWorker: true` if you want to be able to use Node's modules inside the worker. Create a worker file (e.g. `workers/worker1.js`) and create a listener in there, here's some example:
const path = require('path')

// Listen to messages from parent thread
self.addEventListener('message', (event) => {
  console.log('Worker received message:', event.data)
  // Do some calculations and send the result back to parent thread
  let parsedPath = path.parse('C:/test')
  self.postMessage({result: parsedPath})
})
1. Import and init the worker where you need it:
<script>
import Worker1 from 'worker-loader!../workers/worker1'
let worker = new Worker1()
...
mounted() {
  worker.addEventListener("message", function (event) {
    console.log('Received data from worker:', event.data)
  })
}
...
methods: {
  someMethod() {
    // Send some message to the worker
    worker.postMessage({parse: 'C:/test'})
  }
}

Notes:
Don't try to use node's native worker_threads, they don't work in Electron

Tags for people who's trying to find it:

* Web worker Module not found: Error: Can't resolve

* Webpack electron-builder web worker doesn't work

* How to use modules inside web worker in electron webpack

I found an easier way to use workers. Just use https://github.com/GoogleChromeLabs/worker-plugin. @nklayman, maybe you would like to include this in vue-cli-plugin-electron-builder by default?

@cawa-93 I added worker-plugin instructions to the docs, under guide. Thanks for the tip!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

grantdfoster picture grantdfoster  ·  3Comments

TrustNguyen picture TrustNguyen  ·  5Comments

webees picture webees  ·  5Comments

nklayman picture nklayman  ·  3Comments

AlexSHoffman picture AlexSHoffman  ·  3Comments