8

本文主要讲以下几点

  • 简单介绍electron
  • electron的进程与渲染层通讯
  • electron的打包配置
  • electron的自动更新
  • electron如何集成sqlite3
  • electron的资料分享
  • electron在内网开发的一点点建议
  • electron主进程的生命周期

快速开始一个electron与简介

electron是什么

打开官网第一眼就是使用 JavaScript, HTML 和 CSS 构建跨平台的桌面应用,更加通俗一点就是一个桌面浏览器,实际上这样理解也是对的。因为electron就是通过javaScript使chromium来展示web页面

我为啥用它

  • 前端可用制作跨平台应用
  • 生态圈好
  • 由github开发维护
  • electron(70+k)比nw.js (35+k)Star多两倍
  • electron产品我使用过表现优秀

    • Vscode
    • Atom
    • gitHub桌面端
    • ...

如何快速上手electron

我认为以下是快速开始的最好办法

  • 一定要多读官方文档,可以减少大部分时间
  • 官方提供的快速开始模板
  • 官方提供快速熟悉的API的模板

直接使用electron官方提供的例子

// 看这个的源码,很简单可以快速上手
# 克隆这仓库
$ git clone https://github.com/electron/electron-quick-start
# 进入仓库
$ cd electron-quick-start
# 安装依赖库
$ npm install
# 运行应用
$ npm start

如果想了解这个electron的API可以克隆这个库并运行

git clone https://github.com/electron/electron-api-demos

cd electron-api-demos

npm install

npm start

如何调试electron

渲染进程调试(web页面)
渲染器进程这chrome浏览器一样,BrowserWindow创建窗口后添加窗口实例.webContents.open

主进程Electron调试
使用vsCode中的debug,注意调试路径,如果需要增加变量环境依照配置增加即可

electron中主进程的生命周期以及常用事件和渲染层

主要聊electron中主进程的生命周期,渲染器的部分生命周期,一般只介绍通用都存在的,如果只有某个系统有的api就不写在这里了,大家可以自行查阅

主进程中的生命周期

生命周期图
生命周期图

正常流程会触发的生命周期

  • will-finish-launching:当应用程序完成基础的启动的时候被触发
  • web-contents-created:webContents被创建完成
  • browser-window-created:BrowserWindow被创建完成
  • ready:当 Electron 完成初始化时被触发
  • remote-require: 引入remote时被调用
  • before-quit: 在应用程序开始关闭窗口之前触发
  • will-quit:当所有窗口都已关闭并且应用程序将退出时发出
  • quit: 在应用程序退出时发出
  • window-all-closed:当所有的窗口都被关闭时触发
这里要注意如果是进程杀死退出的所有都不触发,如果是cmd+Q或者开发者使用app.quit()退出的window-all-closed是不会被触发的,基本操作一般在ready中处理

进程相关

  • gpu-process-crashed: 当 gpu 进程崩溃或被杀时触发。

其他

  • browser-window-focus: 在 browserWindow 获得焦点时发出
  • browser-window-blur:在 browserWindow 失去焦点时发出

渲染进程--(浏览器)-BrowserWindow

  • ready-to-show:当页面已经渲染完成(但是还没有显示) 并且窗口可以被显示时触发
  • move: 窗口移动
  • resize: 调整窗口大小后触发
  • close: 在窗口要关闭的时候触发。 它在DOM 的beforeunload 和 unload 事件之前触发.
  • blur: 失去焦点,同app
  • focus: 获得焦点,同app
  • maximize:窗口最大化时触发
  • unmaximize: 当窗口最大化退出状态触发
  • minimize: 窗口最小化时触发
  • restore: 当窗口从最小化还原触发
  • ...

渲染进程 BrowserWindow实例中的webContents

  • did-finish-load:导航完成时触发,即选项卡的旋转器将停止旋转,并指派onload事件后
  • did-finish-load: 这个事件类似于 did-finish-load, 不过是在加载失败或取消后触发
  • dom-ready: 一个框架中的文本加载完成后触发该事件
  • crashed: 当渲染进程崩溃或被结束时触发
  • unresponsive: 页面未响应触发
  • devtools-opened: 当开发者工具被打开时,触发该事件。
  • devtools-closed: 当开发者工具被关闭时,触发该事件。
  • ...

electron的进程与渲染层通讯

  • 主进程和渲染器进程
  • 主进程和渲染器进程的区别

electron主进程与渲染进程

chrome浏览器由于每个标签页都是一个进程,而electron所运行的进程称为主进程并且只有一个,主进程要操控浏览器的每个标签的网页称为渲染器进程,如何通讯呢?

主进程和渲染器进程

进程

进程是正在运行的程序的实例(狭义定义)

electron中的主进程

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

electron中的渲染器进程

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

渲染器进程与主进程之间的区别

  • 主进程是从开始运行一直存在,渲染器进程通过BrowserWindow来创建实例,实例销毁则渲染进程销毁
主进程使用 BrowserWindow 实例创建页面。 每个 BrowserWindow 实例都在自己的渲染进程里运行页面。 当一个 BrowserWindow 实例被销毁后,相应的渲染进程也会被终止。
  • 主进程管理所有渲染进程,渲染进程是独立且自我管理(web页面)
主进程管理所有的web页面和它们对应的渲染进程。 每个渲染进程都是独立的,它只关心它所运行的 web 页面。
  • 主进程中可以调用底层所有的GUI的API,渲染进程则因为安全问题不能随意调用。如果要调用则需要通讯让主进程来调用。
在页面中调用与 GUI 相关的原生 API 是不被允许的,因为在 web 页面里操作原生的 GUI 资源是非常危险的,而且容易造成资源泄露。 如果你想在 web 页面里使用 GUI 操作,其对应的渲染进程必须与主进程进行通讯,请求主进程进行相关的 GUI 操作。

主进程与渲染器进程如何通讯

主进程与渲染器进程通过ipcMain与ipcRenderer来通讯

ipcMain与ipcRenderer通讯

主进程向渲染器进程通讯

这个方式主要是主进程中使用ipcMain使用on监听,监听获取后通过event.sender(相当于webContent)send来发送一个事件,渲染进程中使用ipcRenderer通过on来接收,如果是同步可以通过evnet.returnValue来返回主进程的结果代码如下:

渲染器进程向主进程通讯

渲染器进程主要通过ipcRenderer这个模块中的send来发送,该方法中可以同步与异步发送消息,接收消息使用on来接收

通讯简化

electron的打包

常用方式:

  1. electron-builder(本人主要用这个)
  2. electron-pakager
  3. electron-forge

electron-builder

electron-builder 是一个完整的解决方案,并且自带自动更新策略

electron-builder打包在package的script配置好

打包常用参数:

  "build": {
    "appId": "your.id", // appid
    "productName": "程序名称", // 程序名称
    "files": [    // 打包需要的不过滤的文件
      "build/**/*",
      "main.js",
      "node_modules/**/*"
    ],
    "directories": { 
      "output": "./dist-out", // 打包输出的目录
      "app": "./",  // package所在路径
      "buildResources": "assets"   
    },
    "nsis": {
      "oneClick": false,  // 是否需要点击安装,自动更新需要关掉
      "allowToChangeInstallationDirectory": true, //是否能够选择安装路径
      "perMachine": true // 是否需要辅助安装页面
    },
    "win": {
      "target": [
        {
          "target": "nsis",  // 输出目录的方式
          "arch": [ // 输出的配置ia32或者x64/x86
            "x64"
          ]
        }
      ],
      "publish": [ // 自动更新的配置
        {
          "provider": "generic", // 自己配置更新的服务器要选generic
          "url": "http://127.0.0.1:8080/updata/" //更新配置的路径
        }
      ]
    }
  }

在package.json中增加的快速启动项

"scripts": {
  "pack": "electron-builder --dir",
  "dist": "electron-builder"
}

启动打包

通常需要注意的点

  • 注意路径,由于打包后的路径会有问题最好使用path.join()来处理一下。
  • 碰到 The process cannot access the file because it is being used by another process.这个问题多数是vscode占用了关掉重开就好了

如果配合create-react-app创建出来的应用

  • 可以先让react的程序进行打包成静态文件,在把静态文件打包到electron应用中
  • 如果electron-builder总是报类似electron.js找不到的警告,并且在上面提示让你去看网页中的方法,可以通过electron-builder提示的网址来修改,这实际是一个教程需要科学上网。

electron-builder的自动更新

如果在API中看到autoUpdater这个API,希望在看到这个API之前先参考官方的自动更新这样会让你少点坑,因为electron-builder的自动更新机制和electron提供有些不一样,electron-builder官网也有说明,否则就会一直报error了

这个更新实际上是对比例两个版本之间的版本号,如果当前版本小于,服务器版本的话就会进行下载更新

更新机制

在资料里有一篇专门讲更新机制的,很好。这里简单总结。
更新步骤.png

autoUpader生命周期

  • error: 如果任意一个环节有问题就会走到这步
  • checking-for-update :当开始检查更新的时候触发。
  • update-available: 发现更新
  • update-not-available:当没有可用更新的时候触发.
  • update-downloaded: 在更新下载完成的时候触发。
  • before-quit-for-update : 此事件是在用户调用quitAndInstall()之后发出的。

更新要注意的点:

  • electron-builder中的autoUplader需要使用 electron-updater这个模块,非原生模块
  • package.json中的publish中的url与更新的地址要一致(注意端口),否则会报net::ERR_CONNECTION_REFUSED
  • 如果是未打包也想跑一边autoUpdater的流程需要一个dev-app-update.yml的文件放在与main.js同一层级的地方,类似win-ia32-unpacked/resource/app-update.yml这样的文件,直接复制改名就能使用
  • 如果更新完成后在package中配置了自动安装的选项,在关闭应用就能直接安装,如果想自己控制马上安装需要加autoUpdater.quitAndInstall()
  • 如果初次启动应用在终端输出了你想要的结果,在渲染选只输出来了一部分,或者没有输出,如果不是报错的话,不妨监听一下该渲染进程的did-finish-load这个声明周期,在这里面做处理,根据场景可以使用once来监听
  • 需要一个静态网站服务器在你需要的文件夹使用http-server -o,如果没有就全局安装一个npm i http-server -g,在package中需要配置publish中的 "provider": "generic"
  • 碰到 spawn **/*.exe ENOENT这类的可能是你在开发环境运行了一个未安装的应用,并且使用了autoUpdater.quitAndInstall()就会报错,如果是安装好了就不会报这个错
//electron-quick-start 
//main.js 主进程中

// 可以没有这个
const { autoUpdater } = require('electron-updater')

// ================= start 非必须 ===============
const log = require('electron-log');
autoUpdater.logger = log;
autoUpdater.logger.transports.file.level = 'info';
// ================ end 非必须 ==================

// 封装
function sendStatusToWindow(text) {
  log.info(text);
  mainWindow.webContents.send('message', text);
}

/**
 * 自动更新
 */
function checkUpdata() {
  autoUpdater.setFeedURL('http://127.0.0.1:8080/updata/')
  // autoUpdater.on('error', (error) => {
  //   sendStatusToWindow( `[error]:${error}`)
  // })
  autoUpdater.on('checking-for-update', () => {
    sendStatusToWindow('Checking for update...');
    // mainWindow.webContents.send('downlaod')

  })
  autoUpdater.on('update-available', (info) => {
    sendStatusToWindow('Update available.');
  })
  autoUpdater.on('update-not-available', (info) => {
    sendStatusToWindow('Update not available.');
  })
  autoUpdater.on('error', (err) => {
    sendStatusToWindow('Error in auto-updater. ' + err);
  })
  autoUpdater.on('download-progress', (progressObj) => {
    let log_message = "Download speed: " + progressObj.bytesPerSecond;
    log_message = log_message + ' - Downloaded ' + progressObj.percent + '%';
    log_message = log_message + ' (' + progressObj.transferred + "/" + progressObj.total + ')';
    sendStatusToWindow(log_message);
    mainWindow.webContents.send('downloadExe', progressObj.percent)
  })
  autoUpdater.on('update-downloaded', (info) => {
    mainWindow.webContents.send('downloaded')


    sendStatusToWindow('Update downloaded');
    // autoUpdater.quitAndInstall();
  });

electron中添加sqlite3

需要的环境

如果是windows环境需要准备 vs2015 与python2.7的环境

  1. python2.7.x 网上下载安装即可
  2. vs2015 以下方式可选其一

    1. 工具包 npm install --vs2015 -g windows-build-tools(推荐)
    2. 安装visual studio 中安装vs2015工具包(超久)

程序中可添加的本地数据库,最多使用的两款

  • sqlite3(关系型)
  • neDB(非关系型)

为自己的数据加上本地数据库sqlite3

sqlite3【gitHub】
为程序添加sqlite3npm i sqlite3 或者yarn add sqlite3

如何使用?

// 官网例子
var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database(':memory:'); // 这里是把数据存入内存
db.serialize(function() {
var db = new sqlite3.Database()
  db.run("CREATE TABLE lorem (info TEXT)");
  var stmt = db.prepare("INSERT INTO lorem VALUES (?)");
  for (var i = 0; i < 10; i++) {
      stmt.run("Ipsum " + i);
  }
  stmt.finalize();

  db.each("SELECT rowid AS id, info FROM lorem", function(err, row) {
      console.log(row.id + ": " + row.info);
  });
});

db.close();

sqilte3在安装的时候自动生成sqlite所需要的文件包,这个二进制文件需要使用node-pre-gyp或者是node-gyp这也就是为什么需要安装环境的原因,在这一步鬼知道当时报了多少东西,尤其是还要把这玩意弄进内网。
比如说:

  • 开始运行的时候就报Error: Cannot find module 'E:\electronjs\electron-quick-start\node_modules\sqlite3\lib\binding\electron-v4.0-win32-x64\node_sqlite3.node'类似这样的东西,实际上确实没有这个东西,怎么解决呢?

    • 使用node-gyp这个模块直接重新编译一个版本正确的
node-gyp rebuild 
--target=4.0.4 --arch=x64
--target_platform=win32 --dist-url=https://atom.io/download/electron/
--module_name=node_sqlite3 --module_path=../lib/binding/electron-v4.0-win32-x64

// 使用到的模块解析
// target                => electron的版本号一定要一直
// target_platform       => 需要打包的平台ia32/x64等
// dist-url              => 这个是需要下载相关内容的地址
// module_name           => 需要打包的模块名称
// module_path           => 打包输出的地方

如果觉得麻烦可以试试npm i electron-rebuild -D然后在使用./node_modules/.bin/electron-rebuild重新编译一遍,有时候可能是缓存问题,把缓存清除npm cache clear -f清除一下,还有就是npm i重新安装一下,因为环境的问题会跳出各种问题,可以多多尝试这些方法

was compiled against a different Node.js version using
NODE_MODULE_VERSION 64. This version of Node.js requires
NODE_MODULE_VERSION 69. Please try re-compiling or re-installing

如让你选择node编译版本的github【issues】====>也有可能是编译问题,利用electron-rebuild 重新编译一遍或者把sqlite3降级

需要注意的

  • 打包的时候如果报error MSB4019: 未找到导入的项目Microsoft.WebApplication.targets重新安装一下vs2015工具包,或者以前安装的有问题重新安装(我就碰到了)
  • 用node-gyp编译sqlite3打包需要binding.gyp这个python文件以及其他配置所以直接进入node_modules\sqlite3这个文件运行 node-gyp的操作
  • 打包的时候要注意在渲染进程中使用sqlite3在打包的时候会出现打包问题,解决可以选在在主进程操作本地数据库
  • 打包之后使用DB会出现一个问题,在当前目录怎么都生成不了DB,在网上查了要么就通过手动授权为管理员打开,要么手动管理员打开,这根本就不能行,后来参考electron-vue发现可以使用app.getPath('userData')这个方法进行获取app可缓存地址,然后使用path.join合并一下路径就解决了无法生成DB文件的问题,事实上indexDB,locaStore这个两个的存储文件也在这个目录下

其他模块

  • globalShortcut: 注册全局快捷键
  • Tray: 托盘
  • Menu:菜单事件

内网开发需要注意

内网开发真的很烦。。
首先需要环境,然后就是需要下载的包,其次就是需要编译后的各种文件如sqlite3编译后的文件等如果在内网开发electron就需要把这些需要的包全都复制到对应位置

大部分需要配置的文件都在C:\Users\xxxxxx代表你所在的文件夹,
部分下载缓存文件在C:\Users\xxx\AppData\Local目录下的electron 和 electron-builderer里

内网01.png

参考的学习资料

以下文章大部分阅读过,觉得不错推荐给大家

electron

electron构建相关

electron打包相关

electron更新

electron

electron编译

electron tips


coderlfy
39 声望3 粉丝