skysan's programming notebook

コーディングして思ったことなどを気ままに

Nuxt.js + Electronの環境構築(Monorepo風)

はじめに

  • Web(Nuxt.js)側のコードを独立して動かせる環境構築
  • mainプロセスとrendererプロセス間の通信方法を検証

成果物

https://github.com/skysan87/nuxt-electron-sample

目次

プロジェクトの作成

  • ElectronとNuxtを別のパッケージとして作成
    • Monorepoだが、npmのworkspaceは利用していない
      • npmではnode_moduleが1つにまとめられてしまうため
      • yarnのnohoistオプションはnpmになさそう
  • こちらの記事を参考に作成

ディレクトリ構成

.
├── electron
│   ├── build         ・・・electron-builderの出力フォルダ
│   ├── dist          ・・・nuxt generateの出力フォルダ
│   ├── node_modules  ・・・electronプロジェクトのモジュール
│   ├── package.json
│   └── src
│       └── main                  ・・・mainプロセス群
│
└── web
    ├── (...)         ・・・Nuxt.jsプロジェクト
    ├── node_modules  ・・・Nuxt.jsプロジェクトのモジュール
    ├── nuxt.config.js
    └── package.json

作成手順

1. Nuxt.jsプロジェクト作成

  • 既存プロジェクトの場合はwebフォルダ配下に移動
$ mkdir web && cd web
# ここで設定は最小構成
$ npx create-nuxt-app .
create-nuxt-app v3.6.0
✨  Generating Nuxt.js project in .
? Project name: web
? Programming language: JavaScript
? Package manager: Npm
? UI framework: None
? Nuxt.js modules: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Linting tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Testing framework: None
? Rendering mode: Single Page App
? Deployment target: Static (Static/Jamstack hosting)
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? What is your GitHub username? skysan
? Version control system: None

2. Electronプロジェクト作成

$ mkdir electron && cd electron
$ npm init
$ npm install -D electron electron-builder electron-devtools-installer
# srcディレクトリ配下
$ mkdir src & cd src
# mainプロセス
$ mkdir main

3. 出力先フォルダの設定

nuxt.config.js

  • 環境ごとに出力先を変更
  • electronでローカルファイル(HTML)を読み込む場合、nuxt generateでフォルダ(dist)をelectronフォルダ配下に出力するように指定
    • 開発中はローカルサーバから画面を読み込む想定
generate: {
  dir: '../electron/dist'
}

ElectronとNuxt.js間の通信

概要

  • Mainプロセス(Electron)とRendererプロセス(Nuxt.js)間の通信はIPC通信
  • contextBridgeを利用することで、Nuxt.js側はグローバルプロパティとしてメソッドをコールできる
    • electronモジュールへの依存が低くなる

preload.js

  • Renderer(Nuxt.js)との通信処理を記載
const { contextBridge, ipcRenderer} = require("electron")

contextBridge.exposeInMainWorld(
  "api", {
    // renderer -> main -> renderer
    // rendererでのコール方法: "window.api.sendToMainWithResponse(args)"
    sendToMainWithResponse: async (args) => await ipcRenderer.invoke("ipc-send-to-main1", args),

    // renderer -> main
    // rendererでのコール方法: "window.api.sendToMain(args)"
    sendToMain: async (args) => ipcRenderer.send("ipc-send-to-main2", args),

    // main -> renderer
    // rendererでのコール方法: "window.api.recieveFromMain((arg) => { 処理 })"
    recieveFromMain: (listener) => {
      ipcRenderer.on("ipc-recieve-from-main", (event, args) => listener(args))
    }
  }
)

Rendererプロセス

// vueファイル
export default {
  mounted () {
    // IPC通信(main -> renderer)
    window.api.recieveFromMain((args) => {
      console.log(args)
    })
  },
  methods: {
    async send () {
      // IPC通信(renderer -> main -> renderer)
      const result = await window.api.sendToMainWithResponse('1')
      console.log('response from main', result)
    },

    send2 () {
      // IPC通信(renderer -> main)
      window.api.sendToMain('Hello World!')
    }
  }
}

Mainプロセス

// IPC通信(renderer -> main -> renderer)
// rendererプロセスからの通知を受信し、応答する
ipcMain.handle('ipc-send-to-main1', async (ev, args) => {
  const result = await 何か処理(args)
  return result
})

// IPC通信(renderer -> main)
ipcMain.on('ipc-send-to-main2', (ev, args) => {
  console.log('renderer', args)
})

setTimeout(() => {
  // IPC通信(main -> renderer)
  win.webContents.send("ipc-recieve-from-main", 'hello from main');
}
, 10000)

実行ファイル作成(Macのみ記載)

  • electron-builderで実行ファイルの作成
  • electronのsrcフォルダとnuxt generateしたdistフォルダをビルド対象
// electronのpackage.json
"build": {
  "appId": "com.nuxt-electron-sample.app",
  "files": [
    "dist",
    "src"
  ],
  "directories": {
    "output": "build",
    "app": "."
  },
  "asar": true,
  "publish": null
}

ビルドコマンド

// electronのpackage.json
"build:mac": "electron-builder --mac --x64 --dir"

所感

以前Nuxt.jsとElectronを1つのパッケージにするとビルドエラーがしんどかった。 疎結合になったので、Web(Nuxt.js)とデスクトップアプリ(Electron)どちらも開発しやすくなった。

参考文献