問題產(chǎn)生原因
Electron 14.x.y 開始窟扑,Electron 廢棄了一個接口:app.allowRendererProcessReuse
志笼。
根據(jù)其官方文檔描述,當設置了 app.allowRendererProcessReuse
為 true
時,render 進程在加載 native module 的時候被辑,如果發(fā)生了重新加載代碼的情況會自動復用某些底層的初始化代碼。
因此敬惦,當 Electron 加載 node_sqlite3
的時候盼理,由于加載了舊的結構代碼(稍后會解釋為什么此處是舊的結構),在 reload 的時候就崩潰了俄删。
為什么 node_sqlite3 加載的是舊的結構
分析 node_sqlite3
源代碼宏怔。在 src\database.cc
的代碼中可以發(fā)現(xiàn)如下的代碼:
Napi::Object Database::Init(Napi::Env env, Napi::Object exports) {
Napi::HandleScope scope(env);
Napi::Function t = DefineClass(env, "Database", {
InstanceMethod("close", &Database::Close),
InstanceMethod("exec", &Database::Exec),
InstanceMethod("wait", &Database::Wait),
InstanceMethod("loadExtension", &Database::LoadExtension),
InstanceMethod("serialize", &Database::Serialize),
InstanceMethod("parallelize", &Database::Parallelize),
InstanceMethod("configure", &Database::Configure),
InstanceMethod("interrupt", &Database::Interrupt),
InstanceAccessor("open", &Database::OpenGetter, nullptr)
});
#if NAPI_VERSION < 6
constructor = Napi::Persistent(t);
constructor.SuppressDestruct();
#else
Napi::FunctionReference* constructor = new Napi::FunctionReference();
*constructor = Napi::Persistent(t);
env.SetInstanceData<Napi::FunctionReference>(constructor);
#endif
exports.Set("Database", t);
return exports;
}
參照 "Class property and descriptor" example crashes in Electron render process on reload 的處置結果可知,node_sqlite3
代碼在底層實際上已經(jīng)修復了該問題畴椰。但是在 reload 的時候仍然出現(xiàn)了錯誤的情況臊诊,說明修改的代碼在預編譯的時候并沒有生效。即斜脂,默認編譯的時候抓艳,NAPI_VERSION
的值小于 6。
繼續(xù)分析 node_sqlite3
源代碼帚戳。發(fā)現(xiàn)它使用的是 node-pre-gyp
的組件來進行本地編譯工作的玷或。在 node-pre-gyp
項目的 readme 中我們發(fā)現(xiàn),在項目中片任,可以設置項目支持的 napi_versions
偏友。而在 node_sqlite3
的 package.json
中,napi_versions
為:
"napi_versions": [
3
]
即蚂踊,限定了當前版本最高支持到 napi 版本 3约谈。修改 package.json 中 napi_versions 如下:
"napi_versions": [
3,
4,
5,
6,
7,
8
]
再次 build,這時候就會發(fā)現(xiàn)編譯的 native module 跟隨本地的 node 版本的 napi 版本相同了(由于 electron 中使用的 electron-builder 或者 electron-rebuild,因此此處在后面繼續(xù)解釋)棱诱。至此泼橘,代碼修改部分就基本結束了。
關于 node_sqlite3 在 electron 上 electron-builder 與 electron-rebuild 的使用問題
electron-builder
electron-builder
在 package.json
中需要配置一段 script 腳本:"postinstall": "electron-builder install-app-deps"
迈勋,該腳本就是用來安裝 native module 的炬灭。
在安裝的過程中,會根據(jù) electron-builder
的配置靡菇,將 native module 鏈接到指定 electron 的 node 版本重归。
通過跟蹤代碼后發(fā)現(xiàn),其實現(xiàn)原理是調用了 nodejs 的 spawn
函數(shù)厦凤,執(zhí)行了 npm install
命令鼻吮。
追蹤 electron-builder
的代碼,最后確定該命令的代碼位于 app-builder-lib\out\util\yarn.js
中的 installDependencies
方法中较鼓。
由于其在 env
中配置了 electron 相對應的 node 的 lib 路徑椎木,因此會在 native module build 過程中,直接連接使用 electron 相關版本的 lib博烂。因此香椎,此處的本質還是使用 node-sqlite3
自身的配置對 node-sqlite
進行的編譯操作。最多對 node 連接的 lib 做了替換操作禽篱。
electron-rebuild
目前項目中并未有 electron-rebuild
的使用畜伐。因此,并未細究 electron-rebuild
的技術細節(jié)躺率。
根據(jù) electron-rebuild
的文檔簡介玛界,rebuild 過程中,electron-rebuild
會使用 node-gyp
對需要 rebuild 的 native module 進行 rebuild 操作肥照。從結果來看脚仔,rebuild 過程中 napi 版本,連接的 node 版本都是使用的 electron 相應的 node 版本舆绎。
但是鲤脏,編譯產(chǎn)生的文件卻在 napi-v{napi_build_version}-win32-x64
文件夾中,而不是 napi-v8-win32-x64
文件夾中吕朵。這就導致了猎醇,electron 加載 node 模塊的時候,仍舊加載了舊版本的 node 模塊(因為 npm install 的時候就 build 好了該模塊)努溃,仍舊出現(xiàn)問題硫嘶。
當然,如果此時將 napi-v{napi_build_version}-win32-x64
的 node 文件替換掉 napi-v8-win32-x64
文件夾中 node 文件梧税,該問題仍然可以得以解決沦疾。
參考
"Class property and descriptor" example crashes in Electron render process on reload