Electron+Reactアプリにプラグインシステムを付ける 創刊
あんまり書く元気がないのでほとんどのところは実際の実装を見てもらうことにして
配布用型定義
MirakTest/plugin.ts at 8a4b4b66d4a523683eec008a27cc35f866a353c4 · ci7lus/MirakTest · GitHub
プラグイン読み込み部分
MirakTest/plugin.ts at 8a4b4b66d4a523683eec008a27cc35f866a353c4 · ci7lus/MirakTest · GitHub
勘所を書きます
React/Recoilインスタンスの共通化
重要なのはこれです
同じブラウザ内で用いるReactは全て統一する必要があります
用いる場合はもRecoilも…今回の例では用います
これはバージョンが同一というだけではなく、本当に同じソースから使用する必要があります
メインで読み込んでいるものがwebpackでバンドルされている場合、node_modules内のものをrequireして用いてもエラーとなります
なので、本体側からプラグインに対してReactを渡すという手筈が必須になってきます
がしかし、引数で受け取らせるというのはなかなか現実的ではありません
ので、Reactをトップレベルに露出します
code:global.d.ts
import _React from "react"
import _Recoil from "recoil"
declare global {
// eslint-disable-next-line no-var
declare var React: typeof _React
// eslint-disable-next-line no-var
declare var Recoil: typeof _Recoil
}
code:index.web.tsx
import React from "react"
import Recoil from "recoil"
global.React = React
global.Recoil = Recoil
なお、react-domは露出の必要はありません
次にプラグインから読ませる方法です
プラグインはwebpackでバンドルされているものとします
webpackには依存をバンドルに含めず他を参照させるExternals | webpackという機能があるので、これを用います
code:webpack.config.ts
externals: {
react: "root React",
recoil: "root Recoil",
},
これでプラグインがグローバル変数のReact/Recoilを用いるようになります
externalsは割と柔軟に置換されるので、メインプロセスでも読み込ませたいというニッチな需要も満たせます
code:webpack.config.ts
react: "React" in globalThis?React:{},
webpackとesm
レンダラー(browser)とメインプロセス(node)で両方読めるようにするには、プラグインはesmである必要があります
現時点では* webpack v4を使う以外に道はありません
大丈夫になった、webpack5でもいけます
webpack5 by ci7lus · Pull Request #14 · ci7lus/miraktest-plugins · GitHub
メインプロセスがcommonjsでもesm - npmがあればesm読み込めてGoodです
ただプラグインファイルを統一すると、メインプロセスでレンダラーにしかないグローバル変数触りに行ったり、先程Externalsで設定した外部依存をメインプロセスのグローバル変数に求めに行ったりするので、これはそれぞれゴリ押しします
windowを見に行くタイプのヤバい依存がある場合
code:loader.js
if (typeof window !== "undefined") {
exports"NicoCommentPlayer" =
// eslint-disable-next-line @typescript-eslint/no-var-requires
require("../../zenzawatch/src/CommentPlayer").NicoCommentPlayer
} else {
exports"NicoCommentPlayer" = null
}
miraktest-plugins/zenzaLoader.js at cd325f4debe62f591a59df7a6cdb874d3b4524af · ci7lus/miraktest-plugins · GitHub
webpackはこういうことをしてもちゃんとそういう動きをしてくれるので本当にすごい
Reactをメインプロセスに求められる場合
上見てください
Electron+Reactアプリにプラグインシステムを付ける 創刊#61c2aad1ae0f1400004c8b35
Recoilのマルチウィンドウステート共有
追記:recoil-syncというのが出て、かなり素直な方法で共有できるようになりました
recoil-syncを導入し、Atomの同期を改善 by ci7lus · Pull Request #58 · ci7lus/MirakTest · GitHub
ただ現状alphaのレベルのを突っ込んじゃったので今後全部変わるかも
これが一番やばいです
現状、全てのウィンドウで発生したsetをすべて拾って、共有するべきkeyだった場合にメインプロセスを通じて全てのウィンドウに伝搬、受け取った側でsetしています
自分で書いてて泣いちゃうな
なので高速で変更すると
ウィンドウ1がステートAを変更する(hoge)
ウィンドウ1がステートAを共有する(hoge)
ウィンドウ2がステートAを受け取って反映する(hoge)
ウィンドウ1がステートAを変更する(piyo)
ウィンドウ2がステートAを共有する(hoge)
ウィンドウ1がステートAを共有する(piyo)
ウィンドウ1がステートAを受け取って反映してしまう(hoge)
と言った感じで無限ループに陥る場合がある
もっといい方法はあるのかな?
少しばかりマシな方法はあると思うが、現状のRecoilのAPIだとこういうユースに合う使い方出来なさそう
素直にredux+saga使ったほうが良いと思いますが、私はどうしてもrecoilが使いたかった
人は初めて見た状態管理を親だとおもうから
/tosuke/tosuke.icon 何かしらの方法で発信元のウィンドウ情報を埋め込めればもしくは…
#MirakTest #Electron