Electron 使用初体验

804 阅读5分钟

需求

因为需要批量识别网页中的二维码,仅使用前端浏览器又存在跨域的问题。

首先考虑使用的是使用node,但是由于没有找到一款适合加载网页的库而放弃,找到的是只能加载第一次返回的页面,当页面中存在js跳转就不能正确的返回页面内容。

至于为什么不选择python,那是因为我不会。

最后采用的Electron,Electron可以使用javascript开放跨平台的应用,并且天生能加载网页,可以与浏览器进行交互,比如通信、截图、和三方网页交互,Electron中也集成了node,可以使用第三方库做额外的操作。

本次识别网页中的二维码没有开启禁用跨域功能,开启后的通信应该会少点,这个以后再试试,这里现在讲讲开发中遇到的问题

Electron 安装

参考我这里写的 electron 安装失败,Electron failed to install correctly - 掘金 (juejin.cn)

开发中的问题

完成这次开发主要加强了 BrowserWindow ipcMain ipcRenderer 的基本使用,包括主进程、渲染进程、加载内部页面、加载外部页面、页面开启node和electron环境、内部页面使用ipcRenderer与主进程通信、外部页面使用预加载与主进程通信、主进程要主动解绑事件或者方法绑定

  • 主进程是Electron的主程序,控制着一切
  • 渲染进程是加载的页面,包括外部和内部,也可以加载文件
    • 加载网页有时需要设置webPreferences完成一些特定功能
    • 想要和渲染进程完成通信,要开启webPreferences中的两个属性
      • nodeIntegration: true 启用Node integration
      • contextIsolation: true 在独立 JavaScript 环境中运行 Electron API和指定的preload 脚本. 默认为 true
  • 主进程到渲染进程的异步通信,通过渲染进程的实例发送异步通知
    • 这里首先要加载好渲染进程的页面,否则获取不到渲染进程
    • 渲染进程通过ipcRenderer监听事件
    //####### 主进程
    // 向渲染进程发送事件,需要确定选择进程是否加载完成,win是通过 BrowserWindow 创建的窗口
    win.webContents.send('parse-code', params)
    
    //####### 渲染进程
    const { ipcRenderer } = require('electron')
    // 监听从主进程发送过来的事件
    ipcRenderer.on('parse-code', (event, arg) => {
        ......
    })
    
  • 从渲染器进程到主进程的异步通信,通过ipcRenderer发送事件,主进程通过ipcMain接收异步通知
    //####### 渲染进程
    const { ipcRenderer } = require('electron')
    // 发送事件并同步等待事件返回值
    console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // prints "pong"
    // 发送事件不等待事件返回值
    ipcRenderer.send('asynchronous-message', 'ping')
    
    //####### 主进程
    const { ipcMain } = require('electron')
    // 监听事件并同步返回信息
    ipcMain.on('synchronous-message', (event, arg) => {
        console.log(arg) // prints "ping"
        // 返回事件信息
        event.returnValue = 'pong'
    })
    // 监听事件并异步返回信息
    ipcMain.on('asynchronous-message', (event, arg) => {
        console.log(arg) // prints "ping"
        // 方式一
        event.reply('asynchronous-reply', 'pong')
        // 方式二
        win.webContents.send('asynchronous-reply', 'pong')
    })
    
  • 当想和三方页面通信,需要通过preload预加载一个js文件,并监听和发送事件
    • js文件必须是全路径preload: path.join(__dirname, 'viewPreload.js')
    • 此时属于渲染进程,不是主进程
    • 监听和发送事件通正常的加载内部页面,如果不成功这时就重启程序
    // 预加载文件中
    const { ipcRenderer } = require('electron')
    ipcRenderer.on('parse-code-pre', (event, params) => {
        const htmlEl = document.querySelector(params.expression)
        ipcRenderer.send('parse-code-end', {
            ...params,
            expressionVal: htmlEl && htmlEl.innerHTML || ''
        })
    })
    
  • 解绑主进程事件监听
    • 当渲染进程被销毁时,如果主进程存在相应的事件监听,这时最好将其解绑,防止不必要的监听和错误引用

开发的功能

将功能分开到不同的页面,达到功能区分,使功能更容易区分

主页面

主要是设定页面加载多少次才显示二维码、延时识别时间。

首先需要去测试页面测试页面功能,然后再批量页面去批量处理数据。

测试页面

需要在主页面添加好需要测试的url,然后去测试

  • 监听窗口的ready-to-show事件可以指定当前页面重新加载了多少次
    • 页面加载好后不需要别的操作,只需要点击关闭
  • 当点击关闭事件的时候首先阻止页面关闭event.preventDefault()
    • 为了更好的识别券码,防止页面提前关掉
  • 使用jsqr处理win.webContents.capturePage()获得的图片数据
    • 这时图片不需要保存,只是需要转换一下image.toBitmap()
    • 同时要传入文件的尺寸image.getSize()
    • jsqr要在dependencies中,打包的时候带上,否则运行起来找不到依赖包
  • 向主页面渲染进程发送事件
    • 带着数据加载次数、二维码解析值
  • 销毁测试页面win.destroy()

批量页面

测试页面成功以后就需要进行批量处理了,这里会进行连续的识别,当识别页面意外关闭也可以继续之前的位置进行识别,完成后可以进行批量的复制,复制的内容对不通的券码会进行换行。

后期需要加入的内容包括:缓存上次操作信息(确保意外关闭导致的数据丢失,加快解析速度)、excel导出缓存结果(做到有理可依)

显示页面

基本功能和测试页面类似,主要加入了自动识别和自动循环相关的逻辑,测试页面创建的时候加载次数、延时时间已经保存好了

  • 主进程接收加载页面的通知
  • 判断达到加载次数和延时时间自动去识别券码
  • 将识别的数据通知给批量页面渲染线程,并等待下一次加载通知
  • 当批量页面发现没有需要识别的页面后,通知主线程隐藏显示页面

因为需要等待页面资源加载并且要加载达到一定次数,还要等待页面加载完成,所以每次加载都需要很长时间

总结

这一次开发的工具,把electron的创建浏览器窗口和通信搞清楚了,其他的东西还没有过多的查看,最主要的是多看文档,特别是官方文档,看不明白的反复看,看别人是怎么做的。

无论是预加载文件,还是内部的网页使用Vue、React,都可以使用构建工具处理,这里因为文件结构比较简单,就不做处理了。

参考