使用electron時間不是很久,隨著使用的深入慢慢的也遇到一些問題,下面總結一下遇到的問題與大家分享,避免趟坑。
1、webview與渲染程式renderer間通訊
與渲染程式之間的通訊不同,渲染程式與webview之間的通訊,在webview層通過呼叫sendToHost
方法來向渲染程式通訊;而在渲染程式測通過webview提供的ipc-message
事件來向webview通訊。具體如下面程式碼所示:
// renderer環境,獲取webview,然後註冊事件
webview.addEventListener('ipc-message', (event) => {
// 通過event.channel的值來判斷webview傳送的事件名
if (event.channel === 'webview_event_name') {
console.log(event.args[0]) // 123
}
})
webview.send('renderer_event_name', '456')
// webview環境
const {ipcRenderer} = require('electron')
ipcRenderer.on('renderer_event_name', (e, message) => {
console.log(message); // 456
ipcRenderer.sendToHost('webview_event_name', '123')
})
2、BrowserWindow載入第三方網站,整合node模組時導致第三方模組不可用
具體來說,就是在使用new BrowserWindow
時,配置其webPreferences選項的nodeIntegration
值為true,即:
new BrowserWindow({
webPreferences: {
nodeIntegration: true // 注入node模組
}
})
這樣,第三方網站按照CMD格式載入前端模組時如下所示,
!function(a, b) {
"object" == typeof module && "object" == typeof module.exports ? module.exports = a.document ? b(a, !0) : function(a) {
if (!a.document)
throw new Error("jQuery requires a window with a document");
return b(a)
}
: b(a)
}(this, fn);
可以看出,若electron視窗配置整合node模組的話,前端模組佔用了node關鍵字module
,導致前端頁面的模組成了node的模組,以jQuery為例,依賴jQuery的模組會出現如下錯誤資訊:
Uncaught ReferenceError: $ is not defined
知道問題所在,解決問題有兩種思路:
不啟用node功能,即設定
nodeIntegration: false
。這種方式比較粗暴,不能更好的擴充electron應用在頁面載入模組依賴之前改變
module
,之後恢復module指向node模組。
針對第二種方法,github上有人提出解決方案。我們在不可控的載入第三方網站時,利用BrowserWindow的前置注入指令碼preload
來提供修改module指向,可參考程式碼如下:
// renderer
var win = new BrowserWindow({
...
webPreferences: {
nodeIntegration: true,
preload: process.cwd() + '/app/resource/preload.js'
}
});
// preload.js
// electron的BrowserWindow設定nodeIntegration為true時,導致頁面可以訪問node的module影響頁面正常模組引入功能,如jQuery
document.addEventListener('DOMNodeInserted', function(event){
// 頁面內容載入之前需要引入的一些程式碼
if (document.head && !document.getElementById('module')) {
var script = document.createElement('script');
script.setAttribute('id', 'module');
script.innerHTML = "if (typeof module === 'object') {window.module = module; module = undefined;}"
document.head.appendChild(script);
}
});
document.addEventListener('DOMContentLoaded', function(event) {
// 頁面內容載入之後需要引入的一些操作
var script = document.createElement('script');
script.innerHTML = `if (window.module) module = window.module;`
document.body.appendChild(script);
})
3、預載入指令碼preload的問題
BrowserWindow
提供的preload
的配置是為了在頁面第一次載入文件之前預先載入js指令碼檔案,其需要注意3個問題:
- preload配置值不能直接為指令碼字串,否則不會執行
- preload配置的指令碼檔案路徑,只能為本地檔案,其協議必須是file:、asar:二者之一
- preload指令碼仍然有能力去訪問所有的 Node APIs, 即使配置nodeIntegration: false。但是當這個指令碼執行執行完成之後,通過Node 注入的全域性物件(global objects)將會被刪除。
preload是在指令碼載入之前執行,其有三個階段如下,具體可以參考#217 Electron 深度實踐總結:
// ---------------------------------------------------
// 第一階段:在頁面載入之前需要執行的相關程式碼
// ...
// -------------------------------------------------------
document.addEventListener('DOMNodeInserted', (event) => {
// 第二階段:頁面內容載入之前需要引入的一些程式碼
// ...
})
// -------------------------------------------------------
document.addEventListener('DOMContentLoaded', (event) => {
// 第三階段:頁面內容載入之後需要引入的一些操作
// ...
})
// -------------------------------------------------------
可以看出:
preload環境
可以使用Node API,又能訪問DOM、BOM的特殊環境,即使dom文件還未形成之前。