ドキュメントに騙されて苦労した。
ブツ
DEMOでは以下のようなエラーになった。どうやらHTTPS上ではエラーになるらしい。ローカルならちゃんと下の画像のように表示されたのだが。おそらくIPC通信の部分はローカルのプロセス間通信のしくみだからネットワーク上では動作しないということが原因なのだろう。
Uncaught (in promise) TypeError: Cannot read property 'setup' of undefined
at renderer.js:4
インストール&実行
NAME='Electron.sql.js.Load.20220806150924'
git clone https://github.com/ytyaru/$NAME
cd $NAME
npm install
npm start
コード抜粋
package.json
{
"main": "src/js/main.js",
"scripts": {
"start": "electron .",
},
"devDependencies": {
"electron": "^20.0.1"
},
"dependencies": {
"sql.js": "^1.7.0"
}
}
前回はElectronのバージョンが19
だったが、今回は20
になっていた。そのせいかトップレベルでconst fs = require('fs')
するとエラーになった。
プロジェクト作成するとき次のように作成した。するとその時点での最新版がインストールされる。どうやら前回と今回とではちょうどメジャーバージョンが更新されるタイミングだったらしい。メジャー版の更新はインタフェースに変更があるくらいの大きな変更があるときに行われる。今回fs
のロードが失敗するようになったのも、Electronのメジャーバージョンアップのせいだろう。
NAME=electron-sqljs-not-load
mkdir $NAME
cd $NAME
npm init -y
npm i electron -D
npm i sql.js
sql.js
もNode.jsのパッケージとしてインストールしている。これはブラウザのときと違う。ブラウザのときはCDNにあったファイルを配置してロードしていた。
おそらくブラウザ版とNode.js版では異なる実装と思われる。
main.js
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
const path = require('path')
const initSqlJs = require('sql.js');
...
// ここではdb.execを参照できるが、return後では参照できない謎
ipcMain.handle('loadDb', async (event, filePath) => {
console.log('----- loadDb ----- ', filePath)
const fs = require('fs')
const SQL = await initSqlJs().catch(e=>console.error(e))
const db = new SQL.Database(new Uint8Array(fs.readFileSync(filePath)))
console.log(db)
console.log(db.exec)
const res = db.exec(`select * from comments;`)
console.log(res)
return db
})
// db.execの実行結果を返すならOK
ipcMain.handle('getComments', async (event, filePath) => {
console.log('----- getComments ----- ', filePath)
const fs = require('fs')
const SQL = await initSqlJs().catch(e=>console.error(e))
const db = new SQL.Database(new Uint8Array(fs.readFileSync(filePath)))
console.log(db)
const res = db.exec(`select * from comments;`)
console.log(res)
return res[0].values
})
initSqlJs()
がブラウザ版ライブラリと異なる。ブラウザ版はこの引数のところで.wasm
ファイルパスを指定する必要があった。けれどnpmでインストールした今回のライブラリではそれが必要ないらしい。
本当はnew SQL.Database
の戻り値であるdb
オブジェクトを返したかった。そうすれば受け取る側でdb.exec(任意SQL文)
とすることで任意SQL文を発行できた。だが、db
を返した先でexec
関数がundefined
になってしまう。原因不明。
なので仕方なく結果だけを返すメソッドgetComments
を作った。この方法だとユースケースの数だけライブラリのロードが必要になる。あまりに馬鹿げているので何とかしたい。fs
さえもこのipcMain.handle
の中でなければエラーになってしまうため、ライブラリのロードが頻発してしまう。実際にアプリを作るときはどうにかしてロードを工夫せねば実用的でない。
preload.js
contextBridge.exposeInMainWorld('myApi', {
loadDb:async(filePath)=>await ipcRenderer.invoke('loadDb', filePath),
getComments:async(filePath)=>await ipcRenderer.invoke('getComments', filePath),
})
単にIPCメッセージや引数を渡すだけ。
ちなみにこのファイルのトップレベルでもconst fs = require('fs')
がエラーになった。おそらくElectron verson 20になったせいだと思われるが、原因不明。もはやただインタフェースを作るだけの面倒な作業でしかない。
renderer.js
window.addEventListener('DOMContentLoaded', async(event) => {
console.log('DOMContentLoaded!!');
console.log(window.myApi)
window.myApi.setup();
const db = await window.myApi.loadDb(`src/db/mylog.db`);
console.log(db)
console.log(db.exec) // main.jsでは関数なのにこちらではundefinedの謎
//console.log(db.exec(`select * from comments;`)) // Uncaught (in promise) TypeError: db.exec is not a function
const rows = await window.myApi.getComments(`src/db/mylog.db`)
console.log(rows)
const ps = []
document.getElementById('show').innerHTML = rows.map(row=>`<p>${row[1]}</p>`).join('')
});
ここでloadDb
を呼び出して取得できた戻り値にはexec
メソッドがなくundefined
だった。原因不明。
getComments
の戻り値はちゃんとSQL文の結果だったのでOK。
index.html
<!doctype html>
<meta charset="utf-8">
<div id="show"></div>
<script src="./src/js/renderer.js"></script>
所感
Electron学習は大変
一応使えたけど気になる点も多い。Node.jsやElectronに慣れていないせいもある。情報も生のJavaScriptより少ないため調べにくい。難易度があがっている。
sql.jsでは同じコードを使い、同じ結果を、ローカルとサーバで得られないようだ。それを期待してElectronを使っているのに残念。どうしたものか。とりあえずローカルで動けばいいと妥協しておこう。
サーバ上で動かすときは別途ブラウザ版ライブラリで実装することになるのかな。面倒そう。せっかく実行速度を犠牲にしてクロスプラットフォームにしたのにメリットが薄まる。今は勉強のためと割り切ろう。
きっとOSのAPIで実装するよりは遙かに簡単なはず。だからElectronを学習する意義は大きいはず。
もはや宗教
新しいことを学ぶとき、こんな感じでたくさんの不満が出てくる。エラー連発でかなりのフラストレーションが溜まり、心が折れて投げ出したくなる。答えはあるのか。対策はあるのか。わからないまま調査しつづけるときのあの苦痛や不安。ああ逃げたい。
なんとか言い訳をつけて「できるまでやる」ところまでもっていけば勝ち。痛い目をみて失敗して間違いまくってどうにか自己流自己欺瞞術をみにつけるしかない。
小さな失敗や成功それ自体をひとつの成果として記事にする。「ここまではできた」という証拠を作る。「ここから先はわからない」という資料を作る。再現できることが大事。それが次の成果にもつながると信じて。
こうした小細工で自分のモチベを保つ。結果的に小さなことからコツコツやって成すという一般論にいきつく。言うは易く行うは難し。その小さな成功に喜びを感じて楽しむのが一番大事。楽しくないと続かない。楽しくなれば勝手に手が動く。そこまでいけば勝ち。
プログラミングって精神の修行なんじゃないかと思うことすらある。どんなことでもきっとそうした面がある。やっているのが人間である以上、人間のことを理解してうまいこと操作してやるのが問題解決に必要なのだろう。人に向き合わねば何も成せない。
こんな小さなことでさえ考えざるを得ない。さっさと動くものを書ければそれでいいのに、心が言い訳という救いを求めてしまう。もどかしいが、現実を受け入れてやっていくしかない。