zoukankan      html  css  js  c++  java
  • 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({
         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({
         600,
        height: 800,
        webPreferences: {
           nodeIntegration: true
        }
    })
    

    参考文献

  • 相关阅读:
    Windows Mobile下创建cmwap接入点
    我的云之旅hadoop单机设置(2)
    ssh登录
    我的云之旅前言(1)
    实时搜索将是下一个核心
    cassandra索引目录
    图书大甩卖(操作系统、C语言、Linux) 已成交
    ehcache实例
    google推出语音搜索
    百姓网看起来还行
  • 原文地址:https://www.cnblogs.com/wonyun/p/10991984.html
Copyright © 2011-2022 走看看