久々のJavaScriptネタは、Electron。
ダウンロードしたアプリケーションがコイツを使ったWebアプリだったりするとなかなかに残念な気持ちになる、あのElectron。
一部のセクターで根強いニーズがあるとかないとか。
そんなElectronをVue.jsと合わせて使うメモ。開発環境はUbuntuベースのディストリ。
目次
プロジェクトの作成
vue-cliをインストール。
VUE CLI 3以前のものをインストールしている場合はyarn global remove vue-cli
で削除しておく。
$ yarn global add @vue/cli
$ vue --version
@vue/cli 4.1.2
vue create
コマンドでプロジェクトを作成。
Manually select features
を選ぶと色々と聞かれるので、
デスクトップアプリもJavaScriptで作成するような変態さんは、ここで自分の宗派をゴリ押しするとよい。
CSSプリプロセッサはStylus
、コーディングスタイルはStandard
以外ありえない訳だが...
$ vue create hello-vue-electron-builder
Vue CLI v4.1.2
? Please pick a preset:
default (babel, eslint)
❯ Manually select features
...
Vue CLI v4.1.2
? Please pick a preset: Manually select features
? Check the features needed for your project:
◉ Babel
◯ TypeScript
◯ Progressive Web App (PWA) Support
◉ Router
◉ Vuex
◉ CSS Pre-processors
❯◉ Linter / Formatter
◯ Unit Testing
◯ E2E Testing
...
Vue CLI v4.1.2
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, CSS Pre-processors, Linter
? Use history mode for router? (Requires proper server setup for index fallback in production) No
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Stylus
? Pick a linter / formatter config: Standard
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? (y/N)
...
📄 Generating README.md...
🎉 Successfully created project hello-vue-electron-builder.
👉 Get started with the following commands:
$ cd hello-vue-electron-builder
$ yarn serve
生成されたファイルの一覧。
$ cd hello-vue-electron-builder
$ tree . -I node_modules
.
├── README.md
├── babel.config.js
├── package.json
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── HelloWorld.vue
│ ├── main.js
│ ├── router
│ │ └── index.js
│ ├── store
│ │ └── index.js
│ └── views
│ ├── About.vue
│ └── Home.vue
└── yarn.lock
7 directories, 14 files
続いてvue-cli-plugin-electron-builderを追加。
$ vue add electron-builder
📦 Installing vue-cli-plugin-electron-builder...
...
? Choose Electron Version (Use arrow keys)
^4.0.0
^5.0.0
❯ ^6.0.0
? Choose Electron Version ^6.0.0
🚀 Invoking generator for vue-cli-plugin-electron-builder...
WARN Devtools extensions are broken in Electron 6.0.0 and greater
WARN Vue Devtools have been disabled, see the comments in your background file for more info
📦 Installing additional dependencies...
Version6を選んだところ、不吉なワーニングが表示されるが...
yarn install v1.21.1
[1/4] Resolving packages...
[2/4] Fetching packages...
info fsevents@1.2.11: The platform "linux" is incompatible with this module.
info "fsevents@1.2.11" is an optional dependency and failed compatibility check. Excluding it from installation.
info fsevents@2.1.2: The platform "linux" is incompatible with this module.
info "fsevents@2.1.2" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
$ electron-builder install-app-deps
• electron-builder version=21.2.0
Done in 5.69s.
⠋ Running completion hooks...error: 'installVueDevtools' is defined but never used (no-unused-vars) at src/background.js:6:3:
4 | import {
5 | createProtocol,
> 6 | installVueDevtools
| ^
7 | } from 'vue-cli-plugin-electron-builder/lib'
8 | const isDevelopment = process.env.NODE_ENV !== 'production'
9 |
1 error found.
やっぱり出た。
'installVueDevtools' is defined but never used (no-unused-vars)
とのことなので、Linterが未使用の変数をエラーにしているようだ。
上のほうで壊れていると警告されているDevtools拡張関連なので、該当の記述を削除で問題ないだろう。
// src/background.js
'use strict'
import { app, protocol, BrowserWindow } from 'electron'
import {
createProtocol,
// installVueDevtools
} from 'vue-cli-plugin-electron-builder/lib'
const isDevelopment = process.env.NODE_ENV !== 'production'
...
installVueDevtools
使用箇所はコメントアウトされていたので、単純に消し忘れではなかろうか。
コンパイルエラーは修正されたはずなので、開発サーバーを起動。
$ yarn electron:serve
特に問題なさそうなので、リリースビルドを実行。Electronアプリケーションを生成する。
$ yarn electron:build
$ tree . -I node_modules
.
├── README.md
├── babel.config.js
├── dist_electron
...
│ ├── hello-vue-electron-builder-0.1.0.AppImage
│ ├── hello-vue-electron-builder_0.1.0_amd64.snap
│ ├── index.js
│ ├── linux-unpacked
│ │ ├── LICENSE.electron.txt
│ │ ├── LICENSES.chromium.html
│ │ ├── chrome-sandbox
...
dist_electron
以下にアプリケーションが生成された。
AppImageファイルは直接実行できる。
$ ./dist_electron/hello-vue-electron-builder-0.1.0.AppImage &
ESLintを一時的に無効にする
上記でもエラーで止まっていたが、保守や機能追加程度ならまだしも、新規開発中に未使用変数如きでコンパイルエラーにされるのはさすがに効率が悪い。
ルールを変えずに手っ取り早くLinterチェックをスキップするには、.eslintignore
ファイルを使用するのが良さそう。
中身は.gitignore
等と同じようにglobパターンで指定できる。
/node_modules/*
とbower_components/*
は特に記載しなくても無視してくれるようだ。
// .eslitignore
**/* // 全ファイル
**/*.js // jsファイル
**/*.vue // vueファイル
.eslintignore
ファイルまでバージョン管理されていることは稀だろうから、比較的行いやすい方法だとおもう。
コミット前にはチェックを忘れずに。
アイコンを追加
いろんなサイズやフォーマットを準備しなければいけないので結構な手間。アイコン生成ツールを使うとまとめて生成できる。
$ yarn add electron-icon-builder --dev
package.json
へアイコン生成スクリプトを追加。
// package.json
{
"name": "hello-vue-cli-v3",
"version": "0.1.0",
"private": true,
"scripts": {
...
"electron:generate-icons": "electron-icon-builder --input=./public/icon.png --output=build --flatten"
},
...
public/icon.png
へ画像ファイルを配置(1024x1024以上の大きさの正方形画像が推奨されている)してスクリプトを実行。
$ yarn electron:generate-icons
$ tree build/icons
build/icons
├── 1024x1024.png
├── 128x128.png
├── 16x16.png
├── 24x24.png
├── 256x256.png
├── 32x32.png
├── 48x48.png
├── 512x512.png
├── 64x64.png
├── icon.icns
└── icon.ico
0 directories, 11 files
vue.config.js
ファイルを作成して、Electron Builderへのオプションを指定。
// vue.config.js
module.exports = {
pluginOptions: {
electronBuilder: {
builderOptions: {
// options placed here will be merged with default configuration and passed to electron-builder
linux: {
target: ["AppImage"],
icon: "build/icons/"
},
win: {
target: ["zip"],
icon: "build/icons/icon.ico"
}
}
}
}
}
タスクトレイアイコンの指定はbackground.js
へ記述。new BrowserWindow
へicon
オプションを渡す。
// src/background.js
...
import path from 'path'
...
function createWindow () {
// Create the browser window.
win = new BrowserWindow({ width: 800,
height: 600,
icon: path.join(__static, 'icon.png'),
webPreferences: {
nodeIntegration: true
} })
...
}
ウィンドウサイズと位置の保存と復元
どうもそういったAPIはないようなので自前で実装する。ストレージ用のライブラリを導入するとある程度、楽できるかもしれない。
自前で実装する場合は、以下を参考に。
- ウィンドウのサイズと位置は
win.getBounds()
で取得できる。ウィンドウを閉じたタイミングでこの値をファイルに書き出しておく。 - ウィンドウサイズと位置の指定は、
BrowserWindow
のコンストラクタかBrowserWindow#setBounds
メソッドで行える。 - 設定ファイルは、
app.getPath('userData')
で取得できる場所へ保存するのが無難。環境に応じたパスを返してくれる。
getBounds()
ではwidth
、height
、x
、y
が取れる。
> win.getBounds()
{ width: 800, height: 600, x: 10, y: 10 }
大雑把な実装は以下の通り。
実製品の場合は、クラス化したうえで、外部入力となるので値チェックもしっかり行うべき。
...
import path from 'path'
import {readFileSync, writeFileSync} from 'fs'
const config = path.join(app.getPath('userData'), 'config.json')
function createWindow () {
let bounds
try {
bounds = JSON.parse(readFileSync(config))
} catch (e) {
bounds = {width:800, height:600}
}
win = new BrowserWindow({
...bounds,
webPreferences: {
nodeIntegration: true,
webSecurity: true
}
})
...
win.on('close', () => {
writeFileSync(config, JSON.parse(win.getBounds()))
}
win.on('closed', () => {
...
Same-origin-policyを無効にする
CORSヘッダーでリクエストが許可されずにエラーとなる場合、ウィンドウのwebSecurity
オプションで無効にできる。
win = new BrowserWindow({
webPreferences: {webSecurity: true}
})
あえてセキュリティホールを作る行為。ご利用は慎重に。
クロスプラットフォームビルド
Linux上でWindows用のアプリケーションをビルドする。
Windows用のアプリ生成にはwine
とmono
が必要。
公式ではdockerを勧めているが、コンテナ起動の引数にゲンナリしたのでローカルインストール。
$ sudo apt install wine64 mono-devel
--win
引数を追加してWindowアプリのビルド。
$ yarn electron:build --win
$ tree dist_electron
dist_electron
...
├── hello-vue-electron-builder-0.1.0-win.zip
├── hello-vue-electron-builder-0.1.0.AppImage
...
└── win-unpacked
├── LICENSE.electron.txt
├── LICENSES.chromium.html
├── chrome_100_percent.pak
├── chrome_200_percent.pak
├── d3dcompiler_47.dll
├── ffmpeg.dll
├── hello-vue-electron-builder.exe
├── icudtl.dat
├── libEGL.dll
├── libGLESv2.dll
├── locales
...
├── natives_blob.bin
├── resources
│ ├── app.asar
│ └── electron.asar
├── resources.pak
├── snapshot_blob.bin
├── swiftshader
│ ├── libEGL.dll
│ └── libGLESv2.dll
└── v8_context_snapshot.bin
wine
でexe
ファイルを起動してみる。
$ wine dist_electron/win-unpacked/hello-vue-electron-builder.exe &
非効率で馬鹿げてる?
部屋の電気をつけるのに西海岸クラウドサービスと通信する時代だよ。そもそも、Electron自体...(ry
ポンコツ感はあるが一応起動。
本物のWindowsならちゃんと表示されるはず...
以上、私用に一本アプリを作成した時のメモでした。