🤦2020年以前の資料は参考にしない
Electronは2か月に1度、年6回メジャーバージョンアップがあり、最新4メジャーバージョンまでサポートということになっています。 セマンティックバージョンニングを採用してるのに、こんなポンポンメジャーバージョン上げまくるFWは聞いたことがない。 つまりアプリをリリースしても約8か月で必ずElectron自体のサポートが切れるぐらい新陳代謝が早いです。よって(Webフロントエンド界隈全般そうですが)「枯れたバージョン」という概念は存在せず最新のバージョンを使うのが無難です。
またセマンティックバージョニングを採用してるのに、これだけメジャーバージョンアップが早い(=破壊的変更が多すぎる)となると、解説記事の陳腐化も凄まじく、一時期は 「2年以上前の資料は見るな」 という状態でした。しかし2020~2021年あたりから破壊的変更が入るにしてもマイナー機能だったりと、大分落ち着いてきた印象があるので、表記を変更しました。
ちなみにElectronの本とかも出てますが2017年発売なので……🙊
メインプロセスとレンダラープロセスの関係について
Electronとはnode.jsとChromiumを連携して扱えるようにした薄い(?)ラッパーであり、メインプロセス=node.js、レンダラープロセス=Chromiumとなっています。昔はレンダラープロセスから直接node.jsを叩けたのでそこらへんの境目も曖昧だったし、「初心者相手にそこまではいらんやろ」みたいなノリでnode.js云々の説明すら省いている記事もしばしば見かけました。Electron自体の知名度はそこそこあるんですが、メインプロセス側でnode.jsが走ってる点についてはマジで知られてないですからね……
今はContextBrigeの整備等もあってきっちり二分化されましたし、セキュリティや設計を考える上での大前提なので、はっきり意識しておきましょう。この辺りの経緯や具体的な設定については既に素晴らしい記事があるのでそっちをご覧ください。
🛡️セキュリティについて
メインとレンダラープロセスの関係について触れた所で、切っても切れないセキュリティの話もしていきます。じゃん!(効果音)
というわけで、Electronで特に注意が必要なところを赤線で囲っています。通常のWebフロントで気を付ける箇所に加えて、これらにも気を付ける必要があります。要するに何でもできちゃうメインプロセス=node.jsを守れ🛡️、入力値は全てチェックしろ🔍ってことに尽きます。
(フロントエンジニア向け)開発にあたっての心構えについて
フロントエンジニアはブラウザという比較的セキュリティの保証された環境を対象に開発を行っているため、どうしてもその延長線上の感覚で開発しがちだと思います。過去の自分もそうでした。
しかしメインプロセスでnode.jsが走っており、アクセスできることを考えると、 「メインプロセス=バックエンド/レンダラープロセス=フロントを同時に開発して連携させてる」 ぐらいの心構えの方が妥当だと思います。
それを考えると、サーバーサイドを開発する際にAPI設計を考えたり、ちゃんとリクエストに対してバリデーションをかけたりするように、ContextBridgeでレンダラー側に公開するAPIの設計をちゃんと考えたり、メイン-レンダラーの両端でバリデーションが必要とされるのもしっくりくるのではないでしょうか。この辺りはサーバーサイドの開発経験があればかなり生きてくると思います。
アプリ特有の注意点について
とはいえ、あくまでアプリなので、サーバーとも勝手が違う部分があります。
まず一つ目はサーバーのように外部から無差別に送られたリクエストを直接node.jsで受けるわけではなく、ブラウザ(=自前のフロント)経由がほとんどになることです。この点、通常のサーバーサイドプログラムよりかはリスクは下がります。とはいえフロントエンジニアが開発するとどうしてもセキュリティが行き届かず、リスクが増えてしまいがちなのですが……。
もう一つはブラウザ経由でのアクセスだけではなく、ローカルファイルの読み込みであるとか、あるいはUSB他、何かしら接続デバイス経由で入ってくる値等の入力も疑う必要があるということです。
「ローカルファイルまで疑うとかやりすぎでしょ?気にしすぎだって😁」と思われる方もいるかもしれませんが、このルートだとWebと通信しない、スタンドアローンなアプリにさえ攻撃が可能です。
例えばスタンドアローンで動作するゲームアプリに対し、ファンコミュニティで「改造済みセーブデータです😎」と悪意のあるプログラム入りのセーブデータを配り、読み込ませて攻撃するシナリオが考えられます。Electronではありませんが、この攻撃シナリオについてNscripterというゲームの作成、実行用のスクリプトエンジンで実際に問題になり、IPAから脆弱性として発表されたこともあるので、リスクとして頭にいれておきましょう。
(TS導入環境向け)webpack.config.jsもTSに移行した方がいいかも
webpack.config.jsはTypeScript化が可能です。
Electronはelectron-main, electron-renderer,electron-preload
と3種類の設定オブジェクトが必要で、webpack.config.jsの行数が嵩みがち。最初はあんまりビルド部分にがっつりロジックを書くのはどうかなと思い、あえてMODE切り替えの統一ぐらいしかしてなかったのですが、200行を超えたあたりで限界を悟りました。😅
WebStorageの扱いについて
WebStorageはアプリごとに別扱いになりますが、同じアプリを複数立ち上げた場合は共有されるので注意。
これに絡んで開発中は"electron 【起点となるJSファイルへのパス】"
コマンドを頻繁に使うことになりますが、これだと"Electron"という一つのアプリ扱いになります。これで厄介なのは、複数のWebアプリを別に作っててElectronでラップしてるケースで、開発環境だと同一アプリ扱いされて別アプリのWebStorageが混ざってしまいます。
キャッシュやWeb Storageのファイルの場所について
両OSとも途中に隠しフォルダを挟んでいるため、ExplorerやFinderで見る際は表示設定が必要です。
Windows
C:\Users\【ユーザー名】\AppData\Roaming\【パッケージ名】
macOS
実パス: /Users/【ユーザー名】/Library/Application Support/【パッケージ名】
Finder上: /ユーザ/【ユーザー名】/ライブラリ/Application Support/【パッケージ名】
ショートカットキーが効かない!右クリックメニューが出ない!
残念なことにElectronは初期状態でショートカットキーや右クリックメニュー(ContextMenu)が一切設定されておらず、自分で設定する必要があります。
しかも、これらはブラウザやOS自体の機能に紐づいているので、原理上メインプロセスに設定してレンダラープロセスでのイベントにフックする形となります。普段意識しない所ですが、確かに言われてみればブラウザの範囲外ですね……でも面倒くさすぎ
流石に不便ということでElectronサイドでも色々考慮されていてコピーとかペーストとかよくある機能だったら手軽にできるようになっています。ショートカットに関しては公式ドキュメントをどうぞ。
一方、右クリックメニューに関しても、一応公式ドキュメントにもさらっと記載されていますが、書き方が古いので(レンダラーで直接ipcRenderer触ってる)、適宜書き直して下さい。
通信はメインプロセスとレンダラープロセス、どっちでやった方がいい?
Electronではnode.js(メインプロセス)とブラウザ(レンダラープロセス)両方からHTTPリクエストを飛ばすことができます。自分が作った時はWebアプリがベースで+αをElectronで実装する形だったので選択の余地がなかったのですが、選べる時の為にメモを残しておきます。
項目 | node.js | ブラウザ |
---|---|---|
CORS対応 | 不要🙌 | 必要 |
認証方法 | APIキー | cookie |
まずCORS対応の有無はデカいですね。これを利用してるっぽい実例があって、Postmanという、Electron製で自由にHTTPリクエストを飛ばせるツールがあります。バックエンドでAPIを作る際のテストによく使われてます。このツールが、どうもリクエストをメインプロセスで飛ばしてるらしくて、CORSを気にせず気軽にテストできる反面、フロントエンジニア的にはCORS周りのチェックは出来ないというハマりポイントがあります😅
次に認証方法について。node.jsから通信するとなるとAPIキーを利用することになるんですが、Electronの場合だとアプリを解析されたら即流出なのでちょっとねぇ……。マイナーアプリで、想定利用者のリテラシーも低くて、難読化かけてて、漏れても何とかなるとかならワンチャン……いや、ないか。
あと観点が変わりますが「取得した情報の利用場所(出力場所)」も判断に関わってきますね。工数的にも心情的にもプロセス間通信あんまりしたくないですしね。
便利なモジュール一覧
Electronアプリ開発で使えそうなモジュールをご紹介。
役割 | 名前 |
---|---|
node.jsのデフォルトモジュールの型定義 | @types/node |
実行ファイル/インストーラー作成 | electron-builder |
ロギング | electron-log |
JSONファイル読み書き | electron-store |
ソースコード難読化 | javascript-obfuscator |
システム情報取得 | systeminformation |
electron-builder関係
⚠️情報収集と設定に時間取られるので注意!
Windows,macOSアプリのリリース周りをやったことない人向けの警告となります。
歴史的な経緯と昨今のセキュリティ的な要請もあり、アプリをインストールし動かすには、広範かつ複雑怪奇な仕様を理解する必要があります。そして対応するelectron-builderの設定が必要です。アプリの生成まではまだ何とかなると思いますが、特に両OSのインストーラー周り、macOSの公証回りまでやろうとすると、必要な知識量や扱う設定ファイルが一気に増え、かなり厄介です。
よって未経験者が一から環境構築するなら、仕様の情報収集とelectron-builderの設定を詰めるだけで最低1~2か月(注:業務で週40時間やれる計算)は見といた方がいいと思います。もし他言語でのアプリ開発環境があってInstallShieldとか自動化shファイル等の環境が整ってるのであれば、一旦electron-builderでアプリの生成だけして、そっちでやるのも全然アリです。
設定ファイル周りの注意事項
型をつけたい
設定オブジェクトの項目が多岐に渡るため、補完機能が欲しい=型が欲しくなると思います。
最初webpack.config.tsみたいなノリでTypeScript使って何とかならんかな~と検索して回ったんですが、ビルドプロセス自体のTS化が面倒なので、設定ファイルを「設定オブジェクトを返すJSファイル」にして、JSDocから型情報の読み込みを行う形がセオリーのようですね。
//electron-builder.js
/**
* @type {import('electron-builder').Configuration}
* @see https://www.electron.build/configuration/configuration
*/
const config = {
//補完が効く
"appId": "jp.co.hoge",
"productName": "私の凄いアプリ",
}
module.exports = config
JSDocから型情報を読み込みできることを知らなかったので最初は驚きましたが、お手軽だし十分かと思います。
asarについて
現在のWebフロントエンドって極小のJSファイルを数千ファイル以上扱う必要がありますが、これをそのままコピーしたり別ドライブに移動したりすると、極端に速度が遅いという問題があります。このため普通のWeb開発でも、Gitのリモートリポジトリ環境を整備したり、アナログですがzipに固めたりすると思います。
Electronも内部にファイルを抱え込む以上、この問題とは無縁ではいられず、圧縮率と利便性に考慮しながら独自に作り出したファイル圧縮形式がasarとなります。標準設定だとWebアプリ本体の全ファイルが1つの.asarファイルに固められます。
ただ生成の過程で圧縮対象となるファイル内のパス表記を書き換えるため、書き換えに失敗して、画像が表示されないとかのトラブルが発生する場合があります。 どうも"baseDir"プロパティを設定してるとそういうことがある(すいません調査しきれてません)みたいなので、お気をつけ下さい。
asarを使わなかった場合、macは.appファイル自体がパッケージなのでそう問題にはならないのですが、Windowsへのインストールに時間かかりすぎて問題になります。私はインストールに1時間かかるインストーラーを作ったことがあります😵 cabに固めてインストールする設定にすると、多少は緩和できます。
後書き
ElectronをやるってことはTypeScript+SPAフレームワーク系もほぼセットだし、そこらの設定周りも含めて、Webpack職人🔧、設定ファイル職人になることと同義ですね……😅 Next.jsにElectron向けサンプル設定があってかなり楽になるらしいんで、使える環境の方は試すのもいいんじゃないでしょうか。