12

I'm trying to use Web Worker within the nuxt.js framework but keep getting a reference error. ReferenceError: Worker is not defined.

I have installed worker-loader 1.1.1 via npm and added the following rule to my nuxt.config.js:

module.exports = {
  build: {
    extend (config, { isDev, isClient }) {
      if (isDev && isClient) {
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
      }
      // Web Worker support
      config.module.rules.push({
        test: /\.worker\.js$/,
        use: { loader: 'worker-loader' },
        exclude: /(node_modules)/
      })
    }
  }
}

If I create a build via nuxt build it looks like the web worker file is created.

Asset                           Size                      
2a202b9d805e69831a05.worker.js  632 bytes          [emitted]

I import it inside a vuex module, like so:

import Worker from '~/assets/js/shared/Loader.worker.js'

console.log(Worker)
const worker = new Worker // <- this line fails!

In the console I get what looks like a function to create the worker:

ƒ () {
  return new Worker(__webpack_require__.p + "345c16d02e75e9312f73.worker.js");
}

Inside the worker, I just have some dummy code to see if it actually works:

const msg = 'world!'

self.addEventListener('message', event => {
  console.log(event.data)
  self.postMessage({ hello: msg })
})

self.postMessage({ hello: 'from web worker' })
dotnetCarpenter
  • 10,019
  • 6
  • 32
  • 54
  • I've also tried to use workerize-loader but are getting a different error. I'm sure it has to do with my configuration but I still don't know what to do. https://github.com/developit/workerize-loader/issues/27 – dotnetCarpenter Mar 09 '18 at 14:12
  • 1
    shouldn't it be `new Worker()`? – lukas-reineke Mar 09 '18 at 17:57
  • 3
    @lukas-reineke if you don't send arguments to a constructor in JS then the parentheses are optional. https://stackoverflow.com/a/3034952/205696 Anyway, I tried both (and many other things) and it all failed. – dotnetCarpenter Mar 09 '18 at 18:53
  • try to import this way **import * as Worker from "worker-loader!~/assets/js/shared/Loader.worker.js'";** this type of import and and then create a instance. – Neha Tawar Mar 16 '18 at 11:43

2 Answers2

9

Let's first get some things settled:

  • workers are only available at client side -> no ssr

So either you need to use the no ssr component, or need to set the the application to no ssr

With this knowledge, we will modify our nuxt.config.js as follows:

mode: 'spa',
build: {
  extend(config, { isDev, isClient }) {
    ...
    // Web Worker support
    if (isClient) {
      config.module.rules.push({
        test: /\.worker\.js$/,
        use: { loader: 'worker-loader' },
        exclude: /(node_modules)/
      })
    }
  }
}

after npm run build and npm run start it should work like a charm.

I created a repo for you, it is a standard nuxt template with worker-loader utilized: Github Repo

Greaka
  • 735
  • 7
  • 16
  • Finally - Thanks @Greaka! Only now, that I understand why, I got another issue. How do I build a normal SSR capable version of nuxt with a client-side only web worker? My main issue for not using `` is that the web worker is used inside a vuex module, to do pre-fetching. – dotnetCarpenter Mar 17 '18 at 02:18
  • Seems that I can implement it as a plugin - but not sure how to use the plugin in vuex code... https://github.com/nuxt/nuxt.js/issues/1607#issuecomment-328489139 – dotnetCarpenter Mar 17 '18 at 02:36
  • OK. It works in a plugin with ssr set to false. `{ src: '~/plugins/load-ww', ssr: false }` but the vuex files a run before the plugins so I can not make the web worker accessible from to a store file via `Vue.use()`. – dotnetCarpenter Mar 17 '18 at 03:45
  • With the latest source for nuxt, dev modes works fine with worker-loader. I just did a git clone and it works – dotnetCarpenter Mar 17 '18 at 15:14
  • I just rushed here because it struck my mind. Webworkers do get registered in Dev mode, i will update my answer accordingly. – Greaka Mar 17 '18 at 21:21
  • You cannot use workers server side. if you just want to prefatch data, use [asyncdata](https://nuxtjs.org/faq/async-data-components#async-data-in-components-). Another option is to write [your own loader](https://webpack.js.org/contribute/writing-a-loader/) which you call if `!isClient`. The loader could for example change the path to e.g. instead of `*.worker.js` -> `*.js`. Downside: you have to write the same code multiple times. workaround: import the non worker script in the worker and just call the functions. the webworker will be just a wrapper for offloading it in the client. – Greaka Mar 17 '18 at 21:28
  • I figured out to put the web worker instantiation in a `ssr: false` plugin and inject it into nuxt. Then I can use it in a `if (process.browser) ` guard in vuex actions. I'm preloading thousands of SVG and images for a viewer. IMHO it's better to use browser caching than anything else for that. I just need vuex to keep track of what I preload. It largely depends on the user navigation in the app, what prefetch strategy I use. – dotnetCarpenter Mar 18 '18 at 00:10
  • 1
    I played around with it and ended with an example PR: https://github.com/nuxt/nuxt.js/pull/3044 – dotnetCarpenter Mar 18 '18 at 01:18
  • @dotnetCarpenter thanks for demo, but it seems it doesn't work in hot reloading mode in nuxt-edge – husayt Jul 10 '18 at 16:20
  • 2
    @husayt Looks like you answered the question yourself in https://github.com/nuxt/nuxt.js/pull/3480#issuecomment-404150387 – dotnetCarpenter Jul 13 '18 at 11:28
  • Indeed @dotnetCarpenter. Thanks for all the changes – husayt Jul 13 '18 at 15:58
2

With worker-loader, there's the fallback option but personally (and this is what I'm doing) I would keep only the communication code inside the immediate worker file, importing a second file with the actual workload. This way the second file can also be imported serverside. Most likely in a forked thread/threadpool, unless you are in a FaaS context and your main thread has literally nothing else to do.

Also, did you have to use the following in nuxt.config.js? For me, without it, it was "window is undefined". (in the browser, when trying to instantiate worker)

extend(config, ctx) {
    /*
    ** Required for HotModuleReloading to work with worker-loader
    */
    config.output.globalObject = 'this'
}
Mihail Malostanidis
  • 2,686
  • 3
  • 22
  • 38
  • 2
    This was due to a comment by @husayt https://github.com/nuxt/nuxt.js/pull/3480#issuecomment-404150387. However in 1.4.0, it doesn't seem to work anyway. So I removed it and in nuxt page I get the reference through `async fetch({app}) { app.$worker` and in a store I can still use `this.$worker`. Sorry for the bad formatting, but SO peeps think it's great(!) - https://meta.stackexchange.com/questions/216927/code-in-comments-does-not-work – dotnetCarpenter Aug 26 '18 at 13:36