当前位置: 首页 > news >正文

Electron开发者该如何提升自己的技能水平

a2e4e4f7f1f3ed4e4d6710b92464a12d.gif

83472423177b4e4d7428f2a21c41e607.png

导读:这篇文章是写给那些已经可以娴熟的使用前端技术(HTML,CSS,JavaScript,Node.js)开发Electron桌面应用的开发者的。

67844805433261f1634451239f6e4f0e.png

作者:刘晓伦

来源:华章计算机(hzbook_jsj)

ccd975a9aa516d9f6550fa1cd9defa50.png

最近陆续收到一些网友的反馈:

cca4e51e8de0ebdba78698d2df68c6b3.png7c46955d38ec2e9223e8936d679c00fa.png

Electron上手才能知道,各路问题多如牛毛,想用的话,自己慢慢填坑吧。

我感觉这个库挺难用的……[飙泪笑]。

eac1709ac845bedad39aeea329db9361.png b11bf60d626f8827ca7ed654181d8fa7.png

ca2f0db46159af5ab6fa89979835a615.pnga78823e6655f087d22d5fbccdd138659.png

我感觉Electron是典型的上手容易进阶难。

开发Electron项目经常会碰到各种疑难杂症,真的非常沮丧。

63c99cc600628fb30d2ce3768a435fc5.png c5591ba8cfb7b9645dda3d9043eed6ee.png

其实网友反馈的这些问题,绝大部分并不是Electron的问题,而是下面两个问题:

  • 开发者写的代码有问题

  • 桌面应用开发没那么容易

接下来我们就聊聊这两个问题。

1

a52b119dfacd498287e7c1057b4762a3.png

Electron很稳,大多数问题是开发者代码的问题

首先Electron是一个集成项目,它集成了Node.js、Chromium两个知名项目,自己也提供了一些API。如下图所示:

0f2c5463004df6220a6b20e3a5fdc169.png

身为一个前端开发者,我们一定不会质疑Chromium和Node.js的稳定性,不然的话真的可以直接改行去做其他领域的程序员了,假设你不是前端开发者,看看现在Chrome的市场占有率,再看看从2009年一路走到现在并且构建了一个完整生态的Node.js项目,我想你也不会质疑它们。

回头再看Electron自己提供的一系列API,诸如访问硬件设备(屏幕、电源)、访问剪切板、访问系统通知、访问系统菜单之类的,这些API实在不会不会让你的应用出什么幺蛾子问题,而且一旦有问题,绝大多数情况下也能被及时的发现,及时的修复。

有些质疑者会说,那问题一定是出在Electron粘合Chromium和Node.js身上了。实际上Electron在这方面的实现方案是很谨慎的,选用的技术也都是成熟稳定的技术,Electron在跨进程通信方面,使用的就是Chromium的跨进程通信技术:命名管道。你可以在PowerShell里键入如下指令,看看你系统中有多少应用在用这个技术做进程间通信:

1> get-childitem \\.\pipe\

Electron加载Node.js也非常简单粗暴,初始化好Node.js的执行环境后,就把自己的代码和用户的代码交给Node.js执行了。Electron提供的底层API,也是遵循Node.js的底层模块开发规范开发的。

笔者的《深入浅出Electron》第二章深入讲解Electron的内部原理:

927038ee9ee398793b26e6a36681bac0.png

所以,从原理上看,Electron是不会出太大问题的,而且Electron从2013年诞生(2014年正式开源)至今,马上也要10岁了,拥趸众多,阿里、腾讯、京东、网易、美团、拼多多、都在使用它开发桌面应用,有什么问题也会很快被发现,很快被修复。

既然Electron很稳,那么为什么我们基于Electron开发的应用不稳呢?

2

250b5a9eec9b877c8ca63f690bb78884.png

Electron开发进阶难,原生应用开发更难

如果你要开发一个桌面应用,无论你用什么技术,你都会面临一系列难题,下面我举几个例子:

  • 应用运行时间久了之后,内存消耗会不断增加,怎么办?

  • 应用会无缘无故崩溃怎么办?如何调试分析崩溃报告?

  • 服务端公开了TCP长链接接口,客户端该如何与之通信?

  • 如何让我的客户端与另一个客户端进行异构跨进程通信?

  • 如何在客户端渲染展示海量数据?

  • 如何分析并优化客户端的性能短板?

  • 如何灰度发布?如何升级应用,线上产品如何热更新?如何多版本共存?

想想看,这些问题和你是不是选了Electron有关系吗?就算你用Qt、GTK、FLTK之类的原生框架开发桌面应用,这些问题就会变少或者不存在吗?

我可以负责任的告诉你,不会的,只会变的更多,而且处理这些问题的方法只会变得更复杂,更难以控制。非但这些问题不会变少,那些原生框架还会带来更多的新问题,比如Qt在高分屏下缩放显示上的问题:https://www.zhihu.com/question/451021591/answer/1896642195。

反观Electron,在处理这些问题上就好的多,崩溃报告有专门的崩溃报告记录支持,分析崩溃报告也有相应的工具。性能分析有Electron的contentTracing和Chromium的开发者工具。渲染海量数据是纯前端的问题,异构进程通信和服务端通信都是Node.js手到擒来的小Case。所有这些问题处理起来虽然也不容易,但毕竟比原生应用开发要简单多了,笔者的《深入浅出Electron》会有详细讲解。

44c7af100f59134ec5a0c8a5c787cb74.png

3

f43b3de9ad7e5834d4e09a2ad9297af1.png

Electron开发者进阶该学啥


1、底层原理

对一些小微型应用或者自用的小工具来说,不了解底层原理,没什么问题,功能实现了就好。但对于一些大型应用来说,开发者如果不了解底层原理,就很难持久维护你的产品。

比如,如果你不知道Chromium的多进程架构,你就很难理解Electron的主进程和渲染进程机制,如果你不了解asar文件的捆扎原理,你就很难理解Electron是如何读取并执行asar文件内的脚本资源的。

了解了Electron的原理,你就有能力对Electron的源码做修改,比如你想要更大限度的保护你的源码,那么你就可以修改Electron解析asar文件的逻辑,自己编译一个私有的Electron可执行文件。

这里说的底层原理,除了Chromium、Node.js和Electron的原理外,还应该了解Electron生态的重要库的原理,比如electron-builder,electron-updater等(前提是你打算使用它们),因为只有懂了他们的原理之后,你才能更从容的控制他们。

比如,你想在生成安装包的时候给应用程序的可执行文件签名,如果不了解electron-builder的原理的话,很难做这类控制。你不了解electron-updater的原理的话,你就很难排查为什么你的用户一直升级不成功(有可能是一个叫pending的目录没有执行权限哦)。

类似这种案例还有很多,只有掌握原理,你才可以无惧产品经理提出的各种奇葩需求。这方面相关的内容可以参考《深入浅出Electron》第3-4章。

aa4eb02fce343e761f25dd58ae9881f3.png

2、原生模块开发

Electron虽然提供了很多操作系统底层的支持,但它终究不是万能的,很多很多应用的需求还是需要系统底层API的支持才能实现,打个比方:你需要遍历当前用户桌面上所有窗口的窗口句柄,如何做呢?

Electron是没有这个API的,这不怪Electron,因为它没办法把操作系统所有底层的API都封装一遍,要想完成这个工作,就只能开发一个原生模块。

这里需要特别说明一下,开发一个Node.js的原生模块由很多坑,以前Node.js原生模块的开发者都是使用NAN框架(NativeAbstractions for Node.js)来完成工作的,后来Node.js推出了Node-API(以前叫N-API,由于冒犯了有色人种而改名为此),Node-API专门用于构建原生模块,它独立于底层JavaScript运行时,并作为Node.js的一部分进行维护。

基于Node-API开发原生模块仍存在两种方式(可选路径如此之多,真是劝退初学者),一种方法就是使用C语言开发,由于Node-API就是用C语言封装的,所以这种方法更为直接,但由于C语言过于简单直接,语言特性较少,所以开发起来显得非常麻烦。

另一种方式是基于node-addon-api项目(https://github.com/nodejs/node-addon-api)使用C++语言开发,node-addon-api项目是对Node-API的C++再包装,这种方式可以精简很多代码,下面我们通过创建一个JavaScript对象为例来对比一下这两种写法的不同。

先来看使用C语言完成这项工作的代码:

1napi_statusstatus;
 2
 3napi_valueobject, string;
 4
 5status= napi_create_object(env, &object);
 6
 7if(status != napi_ok) {
 8
 9  napi_throw_error(env, ...);
10
11  return;
12
13}
14
15status= napi_create_string_utf8(env, "bar", NAPI_AUTO_LENGTH, &string);
16
17if(status != napi_ok) {
18
19  napi_throw_error(env, ...);
20
21  return;
22
23}
24
25status= napi_set_named_property(env, object, "foo", string);
26
27if(status != napi_ok) {
28
29  napi_throw_error(env, ...);
30
31  return;
32
33}

在这段代码中,首先通过napi_create_object接口创建对象,然后通过napi_create_string_utf8接口创建字符串,最后通过napi_set_named_property接口为对象附加一个属性,并把这个属性的值设置为字符串的值,这段逻辑不但包括复杂的API使用还包括好几处错误检查,代码非常冗长拖沓。

再来看一下C++语言完成此项工作的代码:

1Objectobj = Object::New(env);
2
3obj["foo"]= String::New(env, "bar");

仅此这两句话,就把需求实现了,第一句创建对象,第二句为对象属性赋值,非常精炼直接。更多信息请参考《深入浅出Electron》第16章。

2d983903ab564449c781f48b3ebf6213.png

3、分析和调试

当我们把应用分发给用户后,经常会收到这样那样的问题反馈,最常见的就是应用程序无缘无故的崩溃了,那么这种时候该如何排查定位问题呢?这就需要分析Electron的崩溃报告了。

开发者可以通过如下代码让Electron框架追踪并记录崩溃报告,

1import{ app, protocol, crashReporter } from 'electron'
 2
 3importos from 'os'
 4
 5import{MainWindow} from './MainWindow'  //自定义的主窗口类
 6
 7letmainWin
 8
 9app.on('ready',() => {
10
11  crashReporter.start({ submitURL: '',uploadToServer: false })
12
13  //.....
14
15})

在上面的代码中我们调用了crashReporter对象的start方法,开始追踪崩溃异常,一旦应用程序崩溃,将会在如下目录下生成崩溃报告文件:

1C:\Users\[yourOSUserName]\AppData\Roaming\[yourAppName]\Crashpad\reports

崩溃报告是一个以.dmp扩展名结尾的文件。分析这个崩溃报告的方法请参阅《深入浅出Electron》。

开发者使用Electron提供的开机自启动API,为应用程序设置了开机自启动功能,那么在Windows操作系统下,用户注册表此路径(计算机\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run)下会增加如下键值对:

键:electron.app.[yourAppName]

值:C:\Program Files(x86)\[yourAppName]\[yourAppName].exe

知道了这个,你就知道为什么你用如下代码设置了开机自启动,而应用程序没有开机自启动的原因了:

1import{ app } from "electron";
2
3app.setLoginItemSettings({
4
5      openAtLogin: true
6
7})

除此之外,卸载程序在注册表里的键值是什么呢?如何逆向调试一个Electron应用呢?如何使用开发者调试工具分析应用程序的内存消耗呢?如何分析首屏加载时间呢?请参阅《深入浅出Electron》。

4、大型工程控制

了解了底层原理,能应对各种特殊的需求和问题,那么我想你距离成为一个Electron开发高手仅剩一步之遥了。当然这也是最难的一步。这一步就是学会如何驾驭大型Electron应用。

项目规模一大,很容易代码变成“屎山”,各种业务逻辑交织在一起,牵一发而动全身,谁也不敢改前人写的东西,都是以打补丁的心态开发,补丁叠着补丁,最终成为“屎山”。

其实后端分层、分模块的思路可以借鉴到客户端来改善客户端的架构方式,比如:

75dbf9c70bd40914e7ee5da5079318b4.png

也可以按职责来进行拆分,比如:

1projectName
 2
 3├─resource(需要打包到安装包内的资源,这些资源不会被编译)
 4
 5│ │ ├─ release (会被原封不动复制到客户端电脑上的资源)
 6
 7│ │ │ ├─ img(图形,字体图标等)
 8
 9│ │ │ ├─ dll(可执行文件和二进制资源)
10
11│ │ └─└─ other(其他资源文件)
12
13│ └─└─ unrelease (会被内嵌到安装包内的资源,如应用图标、安装界面图片等)
14
15├─ script(工程调试、编译过程中需要使用的脚本文件)
16
17│ ├─ common(各种环境公用的工具脚本)
18
19│ ├─ dev(开发环境的启动脚本与环境变量)
20
21│ ├─ test(测试环境的启动脚本与环境变量)
22
23│ └─ release(生产环境的启动脚本与环境变量、签名脚本、nsis脚本等)
24
25├─ node_modules/(依赖包目录)
26
27├─ test/(单元测试代码存放目录)
28
29├─ release(应用打包生成的安装包、应用升级文件、打包中间过程文件存放目录)
30
31│ ├─ bundled(应用打包前所有脚本与静态资源编译捆绑后的文件存放目录)
32
33│ ├─ win-unpacked(绿色版可执行程序及相关资源存放目录)
34
35│ └─(打包后的安装包文件、应用升级文件等)
36
37├─ src(源码)
38
39│ ├─ common(主进程与渲染进程公用的源码)
40
41│ │ ├─(事件发射接收器、字符串处理、日期处理、加解密等)
42
43│ ├─ main(主进程源码)
44
45│ │ ├─ utils(工具类:协议注册、剪切板、日志等)
46
47│ │ ├─ widgets(界面辅助部件:托盘图标、系统菜单等)
48
49│ │ ├─ diaologs(所有弹窗类存放目录,文件保存、目录打开等弹窗)
50
51│ │ ├─ windows(所有窗口类存放目录)
52
53│ └─└─ app.ts(主进程入口文件)
54
55│ ├─ renderer(渲染进程源码)
56
57│ │ ├─ assets(随Vue一起编译打包的静态资源)
58
59│ │ ├─ components(全局公共组件)
60
61│ │ ├─ pages(整个应用的所有页面,包含子页面或子控件则以页面名设置子目录)
62
63│ │ ├─ store(放置公共模块,如vuex)
64
65│ │ ├─ utils (工具类:toast、alert、i18n等)
66
67│ │ ├─ main.vue(渲染进程入口界面)
68
69│ └─└─ main.ts(渲染进程入口文件)
70
71├─ .vscode(vscode配置文件)
72
73├─ static(静态文件)
74
75├─ test(端到端测试逻辑)
76
77├─ index.html(渲染进程容器页面)
78
79├─ .prettier(beautify的配置文件,用于团队源码风格一致)
80
81├─ .npmrc(项目环境变量,主要是一些镜像源地址的配置)
82
83├─ .gitignore(git排除文件)
84
85└─ package.json

如果项目规模还在变大,我建议你去学习VSCode的源码,它的实现涉及到了很多设计模式的东西。

4

4e1f9c8d82787fe1e62c94bbf21d5643.png

立足桌面应用开发,盯紧各个技术方向


可以说桌面应用开发包罗万象,有的应用里涉及到音视频编解码技术,有的应用里涉及到人工智能、大数据分析技术,有的应用则涉及到图像学、VR、AR等技术,所以掌握了桌面应用开发技术,还不是成为桌面应用开发高手的终点,应该立足桌面应用开发,展望各个技术领域,让自己始终保持核心竞争力。

5

2c2029f65cd5cab71de356cd6a253e13.png

推荐阅读

《深入浅出Electron:原理、工程与实践》

92d41aae96a3d2483e5acf89bde3e32a.png

这是一本能帮助读者夯实Electron基础进而开发出稳定、健壮的Electron应用的著作。基于作者丰富且真实的 Electron 产品研发经历,给出了交付可靠Electron软件的宝贵经验,书中以“如何基于Electron开发桌面应用”为主线,对Electron的工作原理、大型工程构建、常见技术方案、周边生态工具等进行了细致、深入地讲解。如果你是真正将产品交付到用户手中的技术从业人员,这本书是你不可或缺的。

《Electron实战:入门、进阶与性能优化》

59320c2ef45b14a897a4971bef25354e.png

本书以实战为导向,讲解了如何用Electron结合现代前端技术来开发桌面应用。不仅全面介绍了Electron入门需要掌握的功能和原理,而且还针对Electron开发中的重点和难点进行了重点讲解,旨在帮助读者快速从入门到进阶。

关于作者:

刘晓伦,《Electron实战》、《深入浅出Electron》作者。Electron及其相关技术在企业应用领域的早期实践者,有十余年前端及C++(Qt)的开发经验,深入研究过Chromium的源码及相关的协议(DevTools Protocol和V8Debugger Protocol),其主导研发的产品为数家世界五百强企业提供服务。

07199df34fe9295b96bd0b841a9979f2.gif

6f358e40ae07d7e1dba4f81d81dbac20.png

扫码关注【华章计算机】视频号

每天来听华章哥讲书

db8127f5c4742a7d00f80c2743460794.gif

更多精彩回顾

书讯 | 1月书讯(下)| 2022年的第一本书

书讯 | 1月书讯(上)| 2022年的第一本书

资讯 | 重磅!达摩院发布2022十大科技趋势

书单 | 6本书,读懂2022年最火的边缘计算

干货 | Flink1.14.2发布,除了log4j漏洞你还需要关注什么?

收藏 | Docker冲顶技术热词,微服务应用热度不减,中国云原生开发者真实现状如何?

上新 | 【新书速递】金融领域可解释机器学习模型与实践

赠书 | 【第88期】这10本硬核技术书,带你读懂5G、物联网和边缘计算,玩转元宇宙

822d40b0381631f5bfd1d2452bb4eee9.gif

1849c0a914223e23a6fb46e71bcac286.gif

点击阅读全文购买

相关文章:

  • 终于有人把ROS机器人操作系统讲明白了
  • 一文看懂——序列数据的生成:GAN的方法
  • “三行代码,确实需要耗上一整天!”
  • GraalVM下一代JVM到底是什么?
  • 【第89期】推荐几本电商必读书
  • 一文带你了解LoongArch自主指令系统
  • 2021年数据中台行业十大关键词
  • 测试工程师的未来发展方向在哪里?
  • 一个案例讲明白!如何更安全地实现数据备份和恢复
  • 省政协委员、南京大学人工智能学院院长周志华: 科研学习探索最重要的是“兴趣”和“勤奋”...
  • 为什么现在还有985高校给大一上C语言课?
  • 如何用数字化构建企业的“韧性”?
  • 前端应用和产品逻辑的核心:交互流
  • 2月书讯 (上)| 新年到,新书到!
  • 2月书讯(下)| 新年到,新书到!
  • angular2 简述
  • CentOS7简单部署NFS
  • GitUp, 你不可错过的秀外慧中的git工具
  • input实现文字超出省略号功能
  • Java新版本的开发已正式进入轨道,版本号18.3
  • Js基础——数据类型之Null和Undefined
  • js数组之filter
  • LeetCode29.两数相除 JavaScript
  • Netty源码解析1-Buffer
  • SpingCloudBus整合RabbitMQ
  • uva 10370 Above Average
  • 百度地图API标注+时间轴组件
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 从0搭建SpringBoot的HelloWorld -- Java版本
  • 记一次删除Git记录中的大文件的过程
  • 记一次用 NodeJs 实现模拟登录的思路
  • 那些被忽略的 JavaScript 数组方法细节
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 想写好前端,先练好内功
  • 小程序 setData 学问多
  • 写给高年级小学生看的《Bash 指南》
  • 译有关态射的一切
  • 源码安装memcached和php memcache扩展
  • 最简单的无缝轮播
  • ionic异常记录
  • ​一帧图像的Android之旅 :应用的首个绘制请求
  • #### go map 底层结构 ####
  • #NOIP 2014# day.2 T2 寻找道路
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (175)FPGA门控时钟技术
  • (6)设计一个TimeMap
  • (Note)C++中的继承方式
  • (SpringBoot)第二章:Spring创建和使用
  • (附源码)计算机毕业设计SSM基于java的云顶博客系统
  • (免费领源码)Python#MySQL图书馆管理系统071718-计算机毕业设计项目选题推荐
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (转)ORM
  • (转)大型网站架构演变和知识体系
  • (转)关于如何学好游戏3D引擎编程的一些经验
  • (转)使用VMware vSphere标准交换机设置网络连接