🚄

n番煎じの create-react-app で最速 Electron

6 min read

準備

Node.js はインストール済みとします。

https://nodejs.org/en/

手順

インストール

zsh
# TS の場合は '--template typescript' を最後尾に追加
% npx create-react-app myapp

% cd myapp

# electron 本体
% npm i -D electron
# 各種ユーティリティ
% npm i -D cross-env npm-run-all wait-on

npx および npm コマンドは Node.js に同梱されています。

  • cross-env: 環境変数をマルチプラットフォームで利用できるようにするライブラリ
  • npm-run-all:
    • run-s で NPM スクリプトを順番に実行します
    • run-p で NPM スクリプトを並行して実行します
  • wait-on: ファイルや http のポートなどの準備完了までスクリプトを待たせることができます

https://www.electronjs.org/

編集

package.json
 {
   "name": "myapp",
   "version": "0.1.0",
   "private": true,
+  "main": "src/main.js",
   "scripts": {
+    "dev": "run-p start:dev wait",
     "start": "react-scripts start",
+    "start:dev": "cross-env BROWSER=\"none\" react-scripts start",
+    "wait": "wait-on http://localhost:3000 && electron .",
     "build": "react-scripts build",
     "test": "react-scripts test",
     "eject": "react-scripts eject"
   }
 }
tsconfig.json(--template typescript)の場合のみ
 {
   "compilerOptions": {
     "target": "es5",
     "lib": ["dom", "dom.iterable", "esnext"],
+    "allowJs": false,
     "skipLibCheck": true,
     "esModuleInterop": true,
     "allowSyntheticDefaultImports": true,
     "strict": true,
     "forceConsistentCasingInFileNames": true,
     "noFallthroughCasesInSwitch": true,
     "module": "esnext",
     "moduleResolution": "node",
     "resolveJsonModule": true,
     "isolatedModules": true,
     "noEmit": true,
     "jsx": "react-jsx"
   },
   "include": ["src"],
 }

メインプロセス

zsh
% touch src/main.js
src/main.js
const { BrowserWindow, app } = require('electron');

const createWindow = () => {
  const mainWindow = new BrowserWindow();
  mainWindow.loadURL('http://localhost:3000');
};

app.whenReady().then(createWindow);
app.once('window-all-closed', () => app.quit());

実行

zsh
% npm run dev

スクリーンショット 2021-07-01 9.39.39.png

さらにもう一歩(一部宣伝あり)

React Developer Tools をロードする

NPM ライブラリを作ってみたので、ぜひご利用ください!
わずか87行のスクリプトですけども……

https://www.npmjs.com/package/electron-search-devtools
zsh
% npm i -D electron-search-devtools
main.js
+ const { BrowserWindow, app, session } = require('electron');
+ const { searchDevtools } = require('electron-search-devtools');

  const createWindow = () => {
    const mainWindow = new BrowserWindow();
+   mainWindow.webContents.openDevTools({ mode: 'detach' });
    mainWindow.loadURL('http://localhost:3000');
  };
  
+ app.whenReady().then(async () => {
+   await session.defaultSession.loadExtension(await searchDevtools('REACT'));
+   createWindow();
+ });
  app.once('window-all-closed', () => app.quit());

メインプロセスでも TypeScript を使う

インストール

zsh
# --template typescript したプロジェクトを想定
% npm i -D @types/node

tsconfig.main.json を作成

tsconfig.main.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "CommonJS",
    "outDir": "build"
  },
  "include": ["src/main.ts"]
}

tsconfig.json を編集

tsconfig.json
 {
   "compilerOptions": {
     "target": "es5",
     "lib": ["dom", "dom.iterable", "esnext"],
     "allowJs": false,
     "skipLibCheck": true,
     "esModuleInterop": true,
     "allowSyntheticDefaultImports": true,
     "strict": true,
     "forceConsistentCasingInFileNames": true,
     "noFallthroughCasesInSwitch": true,
     "module": "esnext",
     "moduleResolution": "node",
     "resolveJsonModule": true,
     "isolatedModules": true,
     "noEmit": true,
     "jsx": "react-jsx"
   },
   "include": ["src"],
+  "exclude": ["src/main.ts", "build", "node_modules"]
 }

https://qiita.com/ryokkkke/items/390647a7c26933940470#exclude

package.json を編集

package.json
 {
-  "main": "src/main.js",
+  "main": "build/main.js",
   "scripts": {
-    "dev": "run-p start:dev wait",
+    "dev": "run-p build:ts start:dev wait",
     "start": "react-scripts start",
     "start:dev": "cross-env BROWSER=\"none\" react-scripts start",
     "wait": "wait-on http://localhost:3000 && electron .",
     "build": "react-scripts build",
+    "build:ts": "tsc -p tsconfig.main.json",
     "test": "react-scripts test",
     "eject": "react-scripts eject"
   }
 }

src/main.jssrc/main.ts へ書き換え

src/main.ts
import { BrowserWindow, app, session } from 'electron';
import { searchDevtools } from 'electron-search-devtools';

const createWindow = () => {
  const mainWindow = new BrowserWindow();
  mainWindow.webContents.openDevTools({ mode: 'detach' });
  mainWindow.loadURL('http://localhost:3000');
};

app.whenReady().then(async () => {
  const devtools = await searchDevtools('REACT');
  if (devtools) await session.defaultSession.loadExtension(devtools);

  createWindow();
});
app.once('window-all-closed', () => app.quit());

実行

zsh
% npm run dev

ただし、この手法ではメインプロセスのホットリロードはできません。

こちらもどうぞ

https://zenn.dev/sprout2000/books/6f6a0bf2fd301c

https://zenn.dev/sprout2000/books/3691a679478de2