nklayman / vue-cli-plugin-electron-builder

Easily Build Your Vue.js App For Desktop With Electron
https://nklayman.github.io/vue-cli-plugin-electron-builder/
MIT License
4.12k stars 278 forks source link

Web workers don't work #486

Closed alexhx5 closed 5 years ago

alexhx5 commented 5 years ago

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:

alexhx5 commented 5 years ago

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
  2. 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:

nklayman commented 5 years ago

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

alexhx5 commented 5 years ago

@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.

codebk1 commented 5 years ago

@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

alexhx5 commented 5 years ago

@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?

codebk1 commented 5 years ago

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.

WingDust commented 4 years ago

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

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

```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

code in component

<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

cawa-93 commented 4 years ago

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'
  2. For using with TypeScript add the following in your typings:

    declare module "worker-loader!*" {
    class WebpackWorker extends Worker {
    constructor();
    }
    
    export default WebpackWorker;
    }
  3. 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.

cawa-93 commented 4 years ago

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?

nklayman commented 4 years ago

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

MountainAndMorning commented 3 years ago

It works. Thanks. @alexhx5