vite(+vue)+electronでアプリをビルドする

# はじめに
普段は、vue-cli+electron環境でアプリを開発しています。
高速にビルドできるviteで、electronをビルドする方法を調べたので、メモします。



# 環境構築の流れ

## vite(+vue)とElectronの導入

viteとwebテンプレートの展開

```
npm init @vitejs/app
```

実行すると「プロジェクト名」が聞かれ、プロジェクト名でフォルダーが作られます。
また、ライブラリ(vanilla,vue,react...)が聞かれます。
viteは、vueの開発者が作っているがreactなどのビルドにも使えるように独立して作られています。
今回は、vueを選択して進めます。

インストールが完了するとvueのテンプレート一式が展開されます。

```
cd [プロジェクト名] 
npm i
```

続いてelectron開発に必要なパッケージを追加します。

```
npm i -D electron electron-devtools-installer electron-builder
npm i -D npm-run-all
```

## Electronのプログラムの追加

### viteの基準パス変更

`vite.config.js` の基準パスを 相対パスにする設定を追加します。
ファイルの中身は以下の形です。

```js:vite.config.prd.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  base: './',// 追加
  plugins: [vue()]
})

```

### Electronのメインプロセス作成

Electronのメインプロセスとなる `background.js` をルートフォルダーに作成します。
vue-cliが作成する内容を元に記載しました。
ファイルの中身は以下の形です。


注1)デバッグツールをインストールのインストールは、今回はvue3を使っているので `VUEJS3_DEVTOOLS` としています。
reactなら `REACT_DEVELOPER_TOOLS` など適切に変えてください

```js:background.js
import { app, BrowserWindow} from 'electron'
import path from 'path'

const isDevelopment = ( ( "" + process.env.NODE_ENV).trim() === 'development')


async function createWindow () {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  if ( isDevelopment ) {
    // Load the url of the dev server if in development mode
    await win.loadURL('http://localhost:3000/')
    if (!process.env.IS_TEST) win.webContents.openDevTools()

  } else {
    // Load the index.html when not in development
    win.loadURL('file://' + __dirname + '/index.html')
  }
}

app.whenReady().then(() => {
  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow()
    }
  })
})
app.on('ready', async () => {
    if (isDevelopment && !process.env.IS_TEST) {
      const installExtension = (await import('electron-devtools-installer')).default;
      const VUEJS3_DEVTOOLS = (await import('electron-devtools-installer')).VUEJS3_DEVTOOLS;

      // Install Vue Devtools
      try {
        await installExtension(VUEJS3_DEVTOOLS)
      } catch (e) {
        console.error('Vue Devtools failed to install:', e.toString())
      }
    }
    createWindow()
})
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
   }
})

// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
    if (process.platform === 'win32') {
        process.on('message', (data) => {
            if (data === 'graceful-exit') {
                app.quit()
            }
        })
    } else {
        process.on('SIGTERM', () => {
        app.quit()
        })
    }
}
```

### Electronのメインプロセスのビルド設定ファイル作成

 `rollup.config.js` をルートフォルダーに作成します。
ファイルの中身は以下の形です。

```js:rollup.config.js
export default {
  input: 'background.js',
  output: {
    file: 'dist/background.js',
    format: 'cjs',
    compact: true,
    sourcemap: true
  }
};
```


### レンダラープロセスでElectronを使用するためのpreloadを作成

レンダラープロセスでElectronの機能を使うため `preload.js` をルートフォルダーに作成します。
(詳しくは書きませんが、ElectronではセキュリティのためにレンダラープロセスでElectronの機能を呼び出せません。
必要な機能は、preload.jsを経由して渡します)
ここではサンプルとしてクイックリファレンスの内容を入れました。要件に合わせて変更してご使用ください。

```js:preload.js
window.addEventListener('DOMContentLoaded', () => {

  //
  // ここに、レンダラープロセスに渡したい機能を記載していく
  const replaceText = (selector, text) => {
    const element = document.getElementById(selector)
    if (element) element.innerText = text
  }
  for (const type of ['chrome', 'node', 'electron']) {
    replaceText(`${type}-version`, process.versions[type])
  }
})
```

## Electron用の設定を追加

`package.json` ファイルにいくつかの変更を行います。

主な変更点

* nameなどを追加してpackage.jsonの体裁を整える
* mainプロパティとビルドコマンドを設定
* `scripts` の中身をビルド用に書き換え(元のviteコマンドは `vite:` をつけて残してあります)
* Electronのビルド設定 `build` を追加

(package.json内にあるコメントは削除してください)

```json:package.jsonサンプル
{ 
  "name": "vite-electron", //適当な名前を追加する
  "scripts": {//中身を変更
    "start": "npm-run-all makedir:dist filecopy:* background:build --parallel vite:dev --race electron:start",
    "build": "run-s clean vite:build filecopy:preload background:build electron:build",
    "build:mac": "run-s clean vite:build electron:macbuild",
    "background:build": "rollup --config rollup.config.js",
    "vite:dev": "vite",
    "vite:build": "vite build",
    "vite:serve": "vite preview",
    "vite:prdbuild": "vite build --config vite.config.prd.js",
    "electron:start": "set NODE_ENV=development && electron .",
    "electron:build": "set NODE_ENV=production && electron-builder --win --x64 --dir",
    "electron:macbuild": "set NODE_ENV=production && electron-builder --mac --dir",
    "makedir:dist": "node -e \"require('fs').mkdirSync('./dist', { recursive: true })\"",
    "filecopy:preload": "node -e \"require('fs').copyFileSync('./preload.js', './dist/preload.js')\"",
    "clean": "rimraf dist"
  },
  "main": "dist/background.js", // 追加
  "build": {//追加(Electronのビルド設定)
    "productName": "MyApp", //適当な名前を追加する。ビルド後のファイル名になる
    "files": [
      "dist/**/*"
    ]
  },
  ... 
}
```

これで、設定は完了です。


# Electron開発・ビルドコマンド

Electronを使いながらの開発・コーディングは
`npm start`
コマンドで行えます。 `src`のレンダラープロセスの変更は(viteの機能で)リアルタイムに反映されます。
`background.js` や、`preload.js` の変更は追跡していないので注意が必要です。

ブラウザで開発を進める場合のコマンドは、
`npm run vite:dev`

配布用のビルドを行うコマンドは、
`npm run build`
`dist` フォルダーを削除してからビルドしていますが、その辺はお好みで。

ビルドオプションの`electron-builder --win --x64 --dir`で、windowsの64ビット、ディレクト形式を指定しています。
ビルドファイルは、 `dist/win-unpacked` に出力されます。

ビルドオプションは、公式を参照ください
https://www.electron.build/configuration/win



以上で開発環境は整いました。

開発フォルダーは、
`/src`:レンダラープロセス
`background.js`:メインプロセス
になります。

![スクリーンショット 2021-06-24 161304.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90529/8ead626d-4b6e-83cc-4edb-ff856eed7802.jpeg)

## おまけ・検討事項

1.開発(npm start)で、メインプロセス(background.js)やpreload.jsの変更を監視するようにするか。
今は、監視していないのでソースを変更が自動で反映されない。

2.ソースに問題があってクラッシュした際に、コマンドは終了しているのにElectronのプロセスがバックグラウンドで起動しているときがある。急にビルドできなくなり、その度にタスクマネージャーからプロセスを落とすのがめんどう。


以上です。