Press "Enter" to skip to content

Electron-Vue Worker Process

For heavy work, Electron and Electron-Vue need to use a separate worker process. Here’s what I have done for it.

Overall flow is depicted below. The main process becomes a bridge between a main renderer and a worker renderer.


Router

So, how to create a worker renderer? Electron-Vue uses Webpack and it is hard to create an additional entry point which will be used in workerWindow.loadURL(workerEntryPoint). Instead, it can be easily done by using a Vue router directing to a worker page.

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'home',
      component: require('@/components/Home').default
    },
    {
      path: '/worker',
      name: 'worker',
      component: require('@/components/Worker').default
    },
    {
      path: '*',
      redirect: '/'
    }
  ]
})

Worker Vue Template

And worker Vue template is simple. Just create an empty template and write a worker code in a <script> tag.

<template>
  <h1>This is a Worker Process</h1>
</template>

<script>
  // All scripts for worker can be defined here
  export default {
    methods: {
    },
    mounted () {
      ipcRenderer.on('request', (event, input) => {
        event.sender.send('response', output)
      })
      ipcRenderer.send('worker-ready')
    }
  }
</script>

Note ipcRenderer.send('worker-ready'). It notifies the main process that the worker process is successfully loaded. Then the main process will take a further action (see below).

Main Process

And the main process looks like below.

function createWindow () {
  mainWindow = new BrowserWindow()

  workerWindow = new BrowserWindow({show: process.env.NODE_ENV === 'development'})
  workerWindow.loadURL(`${winURL}#/worker`)

  ipcMain.on('worker-ready', (event, data) => {
    mainWindow.loadURL(winURL)
  })

  ipcMain.on('request', (event, input) => {
    workerWindow.webContents.send('request', input)
  })
  ipcMain.on('response', (event, output) => {
    mainWindow.webContents.send('response', output)
  })

  workerWindow.on('closed', () => {
    app.quit()
  })
  mainWindow.on('closed', () => {
    app.quit()
  })
}

Deep Dive into Main Process

Looking into line-by-line,

workerWindow = new BrowserWindow({show: process.env.NODE_ENV === 'development'})
workerWindow.loadURL(`${winURL}#/worker`)

A worker renderer is visible only when development mode for debugging purpose. And the worker will render a worker page.

ipcMain.on('worker-ready', (event, data) => {
  mainWindow.loadURL(winURL)
})

This indicates that a main window loads and renders URL only after the worker finishes its rendering. This is due to Vuex. If two renderers (windows) are created and renders page at the same time, there might be a race condition and vuex-electron fails to validate a Vuex store.

ipcMain.on('request', (event, input) => {
  workerWindow.webContents.send('request', input)
})
ipcMain.on('response', (event, output) => {
  mainWindow.webContents.send('response', output)
})

As described above, these two statements bridges the main renderer and the worker.

workerWindow.on('closed', () => {
  app.quit()
})
mainWindow.on('closed', () => {
  app.quit()
})

This is specific to my application. I assume that either one of two renderers are terminated due to some reason, there’s no reason not to quit the application, i.e., the main renderer without the worker or the worker without the main renderer? A trivial case that app cannot operate as expected. One can relaunch the closed renderer, it’s up to you.