electron初探問題總結


使用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文檔還未形成之前。

4、渲染線程renderer中引入Electron報錯

在使用webpack打包編譯的renderer進程中,使用如下語句引入electron:

const { shell, BrowserWindow } =  require('electron').remote;

打包完成后報fs.existsSync is not a function的錯誤,詳細信息如下圖所示:

即,electron/index.js文件中引入fs.existsSync語句造成的。

 
  6 | function getElectronPath () {
  7 |  if (fs.existsSync(pathFile)) {
  8 |   var executablePath = fs.readFileSync(pathFile, 'utf-8')

主要原因還是因為webpack默認產出目標是web平台的js,其混淆了nodejs的標准模塊系統,導致引入nodejs的模塊時出現問題。對此github有對應的解決辦法:

即通過使用window.require代替require來引入electron,因為前者不會被webpack編譯,在渲染進程require關鍵字就是表示node模塊的系統;

其實,更佳的解決方案是通過webpack自身來幫我們解決,即修改webpack提供的target產出目標為electron,即:

  • 修改electron主線程webpack的target配置項為electron-main
  • 修改electron渲染線程的webpack的target配置項為electron-renderer

這樣就可以更好的解決上面的問題。

5、渲染進程使用require報Uncaught ReferenceError: require is not defined錯

在將webpack打包輸出目標為electron對應環境后,在主線程中使用BrowserWindow加載renderer線程頁面:

var win = new BrowserWindow({
    width: 600,
    height: 800
})
win.loadURL(url)

因為渲染進程使用require引入node的模塊,這時會報錯Uncaught ReferenceError: require is not defined。原因應該是加載渲染進程的窗口沒有集成node環境

奇怪的是在electron3.x中並沒有任何配置就集成了node環境,而升級到electron5.x就不行了,查詢資料發現:

electron5.x的node集成環境默認是關閉的,這之前的版本是默認開啟的。

所以,為了保持前后兼容,建議顯示配置集成node環境,即nodeIntegration為true。

var win = new BrowserWindow({
    width: 600,
    height: 800,
    webPreferences: {
       nodeIntegration: true
    }
})

參考文獻


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM