React + Electron开发记录(截图识字)

1,506 阅读9分钟

前言

     Electron是由Github开发的, 是一个跨平台的桌面应用开发框架,用HTML css js 的技术开发桌面上面可以安装的软件,可以让前端人员用HTML css js 的技术开发跨平台可以安装的桌面软件。

    像我们前端常使用的开发工具VSCode Atom都是由Electron开发的,因为好奇的我尝试了下开发入门,记录下。

环境搭建

     由于是用react所以我直接使用了官方的脚手架create-react-app,以及相关环境node就不再赘述(百度一下都有)。 主要记录下集成Electron配置开发环境。

  1. 安装Electron依赖

    npm i electron --dev
    
  2. 添加Electron必要的启动文件

    public文件夹下创建electron.js文件

    const { app, BrowserWindow } = require('electron')
    
    let mainWindow
    function createWindow () {
     //创建浏览器窗口
     mainWindow = new BrowserWindow({
         width: 800, 
         height: 600, 
         webPreferences: {
             nodeIntegration: true, // 是否集成 Nodejs
         }})
    
     // 判断是开发环境还是生产环境(生产环境是打包后所以直接引用html文件官方的案例就是这样的)
     process.env.NODE_ENV === 'development' ? 
         mainWindow.loadURL('http://localhost:3000/')
         :
         mainWindow.loadURL(`file://${__dirname}/index.html`)
       
       // 打开开发者工具,默认不打开
        mainWindow.webContents.openDevTools()
     
       // 关闭window时触发下列事件.
       mainWindow.on('closed', function () {
         mainWindow = null
       })
     }
     
     // 当 Electron 完成初始化并准备创建浏览器窗口时调用此方法
     app.on('ready', createWindow)
     
     // 所有窗口关闭时退出应用.
     app.on('window-all-closed', function () {
       // macOS中除非用户按下 `Cmd + Q` 显式退出,否则应用与菜单栏始终处于活动状态.
       if (process.platform !== 'darwin') {
         app.quit()
       }
     })
     
     app.on('activate', function () {
        // macOS中点击Dock图标时没有已打开的其余应用窗口时,则通常在应用中重建一个窗口
       if (mainWindow === null) {
         createWindow()
       }
     })
    
    
  3. 配置Electron入口 以及运行开发环境

    package.json文件中添加, 以及添加script配置

    "main": "public/electron.js",
    "scripts": {
      "start": "react-scripts start",
      "build": "react-scripts build",
      "test": "react-scripts test",
      "eject": "react-scripts eject",
      "electron:dev": "cross-env NODE_ENV=development electron ."
      }
    

    然后在先启动npm start后在运行npm run electron:dev就能在界面中显示

  4. 优化开发启动命令

    安装同时开启多个监听服务concurrently及wait-on等待的包

    npm i concurrently wait-on -D
    

    然后修改script文件下的electron:dev命令

    "electron:dev": "concurrently \"cross-env BROWSER=none yarn start\" \"wait-on http://localhost:3000 && cross-env NODE_ENV=development electron . \""
    

    这样我们直接运行这个命令就直接打开的就是Electron的开发环境直接打开应用,省去了直接需要运行两个命令。

主进程与渲染进程

  1. 介绍

        Electron 运行 package.jsonmain 脚本的进程被称为主进程。 在主进程中运行的脚本通过创建web页面来展示用户界面。 一个Electron应用总是有且只有一个主进程。

        由于Electron使用了Chromium来展示web页面,所以Chromium的多进程架构也被使用到。 每个 Electron中的 web 页面运行在它的叫渲染进程的进程中。

        在普通的浏览器中,web页面通常在沙盒环境中运行,并且无法访问操作系统的原生资源。 然而 Electron的用户在Node.jsAPI支持下可以在页面中和操作系统进行一些底层交互。

-------- 摘自官网

  1. 不同进程的特点:

    • Main Process:

      • 可以使用和系统对接的Electron API
      • 创建渲染进程 - Renderer Process
      • 全面支持Node.js
      • 有且只有一个
    • Renderer Process:

      • 可以有多个,每个对应一个窗口
      • 每个都是一个单独的进程
      • 全面支持Node.js & DOM API
      • 可以使用一部分ElectronAPI

        主进程使用BroswerWindow实例创建网页。每个BroswerWindow 实例都在自己的渲染进程里运行着一个网页。当一个BroswerWindow 实例被销毁后,相应的渲染进程也会被终止。主进程管理所有页面和与之对应的渲染进程。每个渲染进程都是相互独立的,并且只关心他们自己的网页。由于在网页里管理原生 GUI 资源是非常危险而且容易造成资源泄露,所以在网页面调用 GUI 相关的 APIs 是不被允许的。如果你想在网页里使用 GUI 操作,其对应的渲染进程必须与主进程进行通讯,请求主进程进行相关的GUI 操作。 在 Electron,我们提供用于在主进程与渲染进程之间通讯的 ipc 模块。并且也有一个远程进程调用风格的通讯模块 remote

进程之间的通讯

在渲染中引入Electron(就是所指的子页面)

const { ipcRenderer } = window.require('electron')

ipcRenderer.send('emit', { name: 'electron' }) // 第一个参数是标识,第二个是携带的参数


在主线程也是相关原理引入ipcMain模块我这项目指的是我的electron.js

const { ipcMain  } = require('electron')
 ipcMain.on('capture-screen', (e, data) => {
     console.log(data) // 这里的data就是渲染进程传过来的 
 })

这里的主线程与渲染进程的通信更像是一种监听的模式, 一个send另个on

BrowserWindow Api使用记录

因为这个API都会有所用到所以记录下些主要的参数

const { BrowserWindow } = require('electron')
let mainWin

mainWin = BrowserWindow({
        width: 1080, // 所指窗体宽度
        height: 800, // 所指窗体高度
        backgroundColor: "#292B35", //窗体背景颜色
        fullscreenable: false, // 是否全屏
        transparent: false, //是否透明
        resizable: false,  // 是否可以放大缩小
        alwaysOnTop:true, // 是否显示在所有窗体中最上层
        hasShadow: false, //是否有阴影
        frame: false,// 其实就是否显示边框菜单栏标题之类的
        x: 0, // x轴坐标(是以显示器左上角为起始点)
        y: 0, // y轴坐标
        //titleBarStyle: "hidden",
        darkTheme:true, // 是否为深色主题窗口
        show:false, // 是否在创建的时候显示
        title: '', // 窗体的标题
        icon: path.join(__dirname, '../win_favicon.ico'), // 窗体的标题ico (这个只在在windows下有效)
        webPreferences: {
            nodeIntegration: true, // 是否集成 Nodejs
    }})

这里想说下titleBarStyle这个参数

这个参数是提供窗体标题栏自定义的

暂时只是发现了mac下有效果。

更多详尽的API可上官网查询。官网BrowserWindow相关Api

Tray Api使用记录

因为项目中使用到了托盘特此记录下,碰到windowsmac下区别。

在主线程中引入Tray模块,并创建使用(注意是在appready后创建)当Electron完成初始化完成

const { app, Menu, Tray, ipcMain  } = require('electron')

let mainTray = null

 //  托盘
  if(require('os').platform() === 'win32') { // 判断系统类型(因为这里ico文件只支持window下)
    mainTray = new Tray(path.join(__dirname, 'win_favicon.ico'));  // windows下图标
  } else {
    mainTray = new Tray(path.join(__dirname, 'logo_normal.png')); // mac设置托盘的图标
    mainTray.setPressedImage(path.join(__dirname, 'logo_pressed.png'))// mac设置托盘点击下的图标
  }
  
  // 这里是我项目主要的托盘菜单直接贴了这里使用到了Menu Api创建菜单
   const contextMenu = Menu.buildFromTemplate([
     {label: '截图识字', click: () => {captureSceen()}},
     {label: '选择图片', click: () => {uploadImgDialog()}},
     {label: '选择PDF文件', click: () => {uploadPdfDialog()}},
     {label: '批量识别', click: () => { mutileWin.show()}},
     {label: '设置', click: () => {settingWin.show()}},
     {label: '识别历史', click: () => {redcordWin.show()}},
     {label: '退出', click: () => {
        //这里说退出说明下,因为mac下退出会自动执行Tray的destroy所以不需要手动销毁,不然的话mac下点击退出都会崩溃 
        require('os').platform() === 'win32' ?
            mainTray.destroy()
            :
            MacShowResultWin.destroy()
          
          mutileWin.destroy()
          settingWin.destroy()
          redcordWin.destroy() 
          app.quit()
     }},//真正的退出(这里直接强制退出)
   ])
   
mainTray.setContextMenu(contextMenu) // 将菜单设置到托盘中

// 这个我好像只是在`windows`下看到了效果, 就是点击托盘左键看开主窗体
mainTray.on('click', ()=>{ //模拟桌面程序点击通知区图标实现打开关闭应用的功能
  mutileWin.isVisible() ? mutileWin.hide() : mutileWin.show()
  mutileWin.isVisible() ? mutileWin.setSkipTaskbar(false):mutileWin.setSkipTaskbar(true); //设置窗口是否在任务栏中
})

在使用TrayAPI中碰到的问题

  • 图标的文件格式macwindows下的区别,最后我也是通过官方的文档查询才发现。所以仔细的阅读官方文档还是很重要的。
  • 还有就是Tray的销毁问题,最终也是网上寻得的答案

在这部分文末贴个官方的Tray文档 官网Tray相关Api

项目打包

  1. 使用electron-packager打包

    1.1 安装相关包

      npm install -D electron-packager
    

    1.2 直接使用命令打包,electron-packager的打包基本命令是:

    electron-packager <location of project> <name of project> <platform> <architecture> <electron version> <optional options>
    

    命令说明:

    • location of project:项目所在路径
    • name of project:打包的项目名字
    • platform:确定了你要构建哪个平台的应用(Windows、Mac 还是 Linux)
    • architecture:决定了使用 x86 还是 x64 还是两个架构都用
    • electron version:electron 的版本
    • optional options:可选选项

    1.3 在pacage.json文件得script下添加

     "electron:build": "electron-packager ./ --all --asar --out=./release-builds --overwrite",
    

    最终就可以使用npm运行该命令进行打包了

  2. 使用electron-builder打包(我使用了这种)

    1.1 安装相关包

      npm install -D electron-builder
    

    2.1 在pacage.json文件下添加

    "build": {
        "productName":"xxxx",//项目名 这也是生成的exe文件的前缀名
        "appId": "com.xxx.xxxxx",//包名  
        "copyright":"xxxx",//版权  信息
        "files": [ // 打包的文件
          "package.json",
          "node_modules/**/*",
          "build/**/*",
          "config",
          "assets"
        ],
        "mac": {// mac下
          "target": [
            "dmg"
          ],
          "icon": "public/logo.png"
        },
        "win": {  // windows下
            "icon": "public/icons/win_favicon.ico",
            "target": [
              {
                "target": "nsis" // 我们要的目标安装包
              }
            ]
      }
     }
    
    

    然后在script栏添加

     "pack": "electron-builder --dir"
    

    运行npm run pack就能进行打包了

    打包后文件很大,自己寻求网上解决方案是删除package.jsondependencies里的不需要的依赖,然后打后感觉还是很大好像打包后100MB多,不知道有没有大佬有方案能还能缩小吗?不胜感激啊。😉

总结

     Electron一个跨端的桌面应用程序开发框架,它发布的那会我就有关注,然后自己也没有实际的尝试过,最近公司有需求,然后自己又有极大的兴趣,所以就三下五除二的开干了,通过此项目自己巩固了下react因为我使用react进行开发, 没有选择vue。因为是平时自己公司的项目都是vue的所以选择了reactvue有专门为Electron写的脚手架electron-vue)。其实个人感觉用Electron开发就是框架搭建好了,然后剩下的都是些网页,还有一些就是网页中(也就是所谓的渲染进程跟主线程的通信)所以不是特别的对系统的一些操作,感觉难度并不是很大的。

    然而通过Electron对桌面应用程序的开发,自己对桌面应用也有了相应的了解。然后想想了想前端真的是无所不能啊。😂😂从网页到移动web,然后跨平台APP、跨平台桌面应用程序。

    然后感谢 掘金昵称 徐健本尊juejin.cn/post/684490…发布的截图方案因为我自己本项目中使用到了所以写出出处感谢下,还有唯一一点不足的就是双屏截图这个方案也是没有实现,然后我自己也是寻找很多方法也是没有,有大佬有思路的不胜感激寻求。网上好像有说调用原生模块得各种的编译,这种方案我暂时还没有试。第一版本先发了,然后之后优化截图方案。🤞

     放上几个Electron的官方文档地址:

    Electron(官方文档): www.electronjs.org/

    Electron的官方快速开始脚手架: www.electronjs.org/docs/tutori…

    Electron-Vue脚手架: simulatedgreg.gitbooks.io/electron-vu…

    Electron React Boilerplate: electron-react-boilerplate.js.org/

    Electron的 electron-builder打包(官方文档): www.electron.build/

最后放上几张项目图:

Windows:

Mac: