2018年底开发了一款内部IM软件,当时使用了Electron开发,可惜内部使用了1年多后,停止维护了。五年之后的今天,又要开发一款套壳浏览器,内嵌现有的网页端,兜兜转转又捡起来了。
1.前言
开发之前也考虑过选型问题,之前开发Electron的时候确实是踩了很多坑的,重新使用Electron开发是有些犹豫的。6年前内部很多业务人员的设备都是win7, 内存没有超过8G的,Electon性能饱受诟病。
开发之前也去对比了Tauri2做了选型比较(对于js开发者)
指标上Tauri2有很大优势,但是我们的项目目标是开发速度快,内核兼容性好。 另外可以预期新的框架一定会存在一些很难解决的巨坑。所以最终还是选择了 Electron,如果是个人项目,我可能会倾向于 Tauri2
另外 Elctron 基于 chromium 内核,所以兼容性取决于 chromium 的兼容性,具体参考下方,酌情选择 Elctron版本
其中关键的 108版本对应 Electron22,因此如果有兼容 win7计划,能使用的最新版本是 22
2.项目初始化
- electron-vite Nodejs18+
- Solidjs
需求为内嵌公司网站,需要支持类 Chorme多 Tab 操作,支持各类文件下载。
2.1 自定义Tab实现
新版本30.0.0+基于 WebContentView 实现, 旧版本可以通过BrowserView实现。
- 实际运行中 webView 的代码要复杂的多,每一个 Tab 对应了一个 webView视图。由全局的 tabs 数据存储
- 每次新增 Tab 即创建一个新的 webView
- 每次切换 Tab 即改变 webView 的显示状态
- 关闭 Tab,移除webView视图,并解除引用
实际代码是通过一个 TabManger 类来维护的, 内部还增加 LRU 排序,设置最大存在视图数量。动态释放超出的视图(最久未使用)。
1.模拟 chorme 冻结机制。定时检测 Tab 最后使用状态,超过一定时间主动销毁视图。下次激活视图,重新创建视图。
2.省内存模式,针对内存过小的硬件,全部复用一个视图。
整个容器的内存占用,其实取决于,每个 webView 的 url实际的内存占用,页面越复杂,dom 越多,优化不合理,占用的内存会越高。
根据下面公式,所以内存优化的重点就是如何减少 WebViewContent的数量,作为套壳应用,每个 WebViewContent 的内存使用是不可控的
2.2 自定义文件下载
如果是标准下载可以不用处理,electron 默认会处理下载行为。比如 window.open 或者 a 标签打开可下载文件 URL。
但是会有一些 web 应用会使用特殊的方案来进行文件下载,比如说 XHR & Blob & a 标签方案 这种方案,ELectron 对于 blob 协议无法下载的,对于主进程来说这只是一个普通的链接。 而在 chrome 中
也就是 blob://XXXXXXX是 chrome 的一个自定义协议。默认 ELecton 是不支持这个协议的。并且也无法从主进程中无法读渲 染进程的内存中的Blob数据。
所以第一次尝试的方案是,在sessionwill-download hook中,执行 js 代码获取 blob 数据,传输给 Node程序,执行下载。在某些场景下,这是可以正常工作的。
但是有个场景,URL.createObjectURL 生成之后,立马进行了 remove 操作。导致在will-download的场景下,无法获取到blob 数据可能存在谬误,但是每个webViewContent对应一个进程是肯定的。
因此只能对createObjectURL进行劫持。重写原生的createObjectUR, 每次创建的时候使用临时变量将 blob 数据存储起来,触发will-download通过 blob id, 通过 ipc 通讯,获取 blob 数据后,从临时变量中移除,解除内存占用。
预计后面肯定要做下载记录,所以实际的代码实现中,会有一个 Download类来实现这一系列逻辑, 同时引入
better-sqlite3来做本地化存储。
3.构建
Electron 构建常用的两个工具是Electron-builder、Electron-Forge
两者的对比参考文档
因为之前的项目使用的就是 Electron-builder,加上它经过更多的用户验证,因此使用 Electron-buidler 作为打包工具,实际上 Electron-vite 提供有 Eletron-Forge配置教程,可以很方便的切换到 Electron-forge 打包,如有需要,自行切换 。
3.1 Electron-Builder
配置说明
为了做数据隔离,配置文件做了两套,方便系统同时安装 beta 和 prod 包。通过 AppID 以及 name 区分。
如果你需要更合理的区分不同channel,建议研究 publish channel 字段。参考文档
afterPack
主要做一些,构建完文件的处理。目前做了以下几个操作。
- 删除无用的语言包,只保留 zh_CN, 以减小体积。
- 复制不同系统架构的原生模块,本地编译失败概率高,直接下载编译过的原生模块,构建完复制到对应目录即可。
Electron-vite 推荐主进程 preload node相关的依赖不打包,但是目前依赖解析或者说某些依赖对于 dev dep 设置不规范导致 asar 文件里面包含不需要的依赖,需要在打包环节剔除。
3.2 源码加密
V8 字节码 通过 Node 标准库里的 vm 模块,可以从 script 对象中生成其缓存数据(参考)。该缓存数据可以理解为 v8 的字节码,该方案通过分发字节码的形式来达到源代码保护的目的。
需要注意的是以下几点
- 目前已知的库字节码只支持 cjs, 如果需要字节码,那么需要提前将 esm 转换为 cjs
- 字节码是按照 Electron Node.js 版本生成的, 但不同的架构不一定相互兼容。(比如我 mac intel 64 打的包,win 64 运行jsc 报错)
- 不支持 Function.prototype.toString
- 不能启用最小化混淆
使用方法, 直接引入插件即可
3.3 代码签名
目前只做了 windows 应用签名,macos 只签名没用,还需要上架才行。这一块单独抽出一章介绍。
代码签名依赖于 electron-builder, windows 底层依赖 jsign,mac 依赖 xcode
4.签名(Code Signing)
4.1 window签名
4.1.1 EV证书购买
直接淘宝搜win 代码签名
如果是企业用户,推荐EV证书(可以直接通过 Windows SmartScreen信任 ), 个人用户可以依据自己需求购买OV证书(无法直接信任,用户主动信任的次数多了(信誉累计 ),Windows SmartScreen才会信任)
EV证书分为两大类
- 云签名(如Certum EV)
优点:审核通过即可使用,无需硬件。如果希望CI/CD 中完成签名,这个方便一点。
缺点:资料多 审核严格。审核时间1-3个工作日,需要提供营业执照图片+企业法人手持身份证正面反面 + 法人身份证正反面+企业最近的水电、电话、物业费 等公共事业账单图片 选择提供其一提供即可!
- USB Token(如GlobalSign)
优点: 只需要提供公司名字,审核快,审核没那么严格
缺点: 需要拿到硬件,用于签名的机器,必须插上 USB TOKEN,才能进行签名。
以下文档以GlobalSign EV 为例
4.1.2 签名提取
下载驱动
- 访问官网地址globalsign 下载适合的驱动程序。
- 安装完成后,插入
USB TOKEN, 需要注意受安全策略影响,通过windowsRemote Desktop远程访问无法识别到 USB Toeken - 依赖于 IE 浏览器,推荐使用 windows 电脑,或者windows 电脑 Microsoft Edge的 IE模式(设置-默认浏览器-Internet Explorer 兼容性-允许在 Internet Explorer 模式下重新加载网站IE 模式-允许-将签名提取地址加入到名单里面), 提取地址有代理商(淘宝商家或者其他渠道商家)提供。
- 重启 Edge 浏览器,将提取链接粘贴到网址栏,然后进行访问。
- 输 入提取密码(提取密码请咨询您对应的销售人员),点击
下一步,访问确认,点击是 - 确认有 Token 选项,然后点击下一步
- 输入 token 密码(token 初始密码为:88888888 如果您已更改密码则输入您更改后的密码),点击 OK,然后点击下一步(此过程需要耐心等待,尽量不要操作电脑,避免提取页面卡死未响应导致提取证书失败)
- 点击安装我的证书。web 访问确认,点击
是。提示安装成功。 - 打开驱动软件检查
设置-设备-我的令牌-用户证书-证书名称(一般为企业名)如果存在即为安装成功。
SafeNet Authentication Client Tools 一般情况下是不提供私钥,我们只能导出公钥证书无法进行签名。以下签名完全基于硬件,所以必须保持能够正常的读取 USB Token 设备
4.1.3 win签win
- 客户端设置单点登录
设置-客户端设置-高级-勾选单点登录(第三个)然后保存 - 导出证书
设置-设备-我的令牌-用户证书-右键导出,导出 .cer 文件 - 安装 Windows SDK(仅适用于桌面应用程序的 Windows SDK 签名工具),在安装程序的后半部分,有一个屏幕可以在多个工具中选择要放置的内容,但只选择“Windows SDK Signing Tools for Desktop Apps”,然后单击“Install”完成。
- 在环境变量中添加了 “C:\Program Files (x86)\Windows Kits\10\App Certification Kit”。
- 双击下载.cer 证书,安装证书到当前电脑。
- 最后配置签名名称即可,一般为企业名称
构建过程中会至少提示输入一次 token 密码(签名提取时的 token 密码, 你可能改过以最新的密码为准)
4.1.4 mac签win
- 依赖 Java, 需要前置安装
安装 jsign
Electron builder 配置指定使用 js 脚本自定义签名
jsign硬件token的配置文件
如果有多个证书,通过 slotListIndex 指定使用第几个。library 大概率是一样的不用变更。如果不存在就全局搜下libeToken.dylib
自定义签名脚本
实际项目中TOKEN_PASSWORD 推荐使用环境变量。目前项目只有一人开发,且 config.mjs不在 git 版本追踪之中, 因此临时放到了本地配置文件中。
4.2 macos签名
4.2.1 准备工作
4.2.2 准备证书
证书种类说明
我们只需要签名以及公证,所以需要使用的是Developer ID Application, 具体安装方法参考XCode开发文档
使用证书有几种方案详细说明
- 指定钥匙串内的证书名称 适用于开发Mac 打包 设置系统变量
CSC_NAME - 导出证书,转换成 base64编码, 适用于CI/CD打包,设置系统变量
CSC_LINK&CSC_KEY_PASSWORD
CSC_NAME 是指类型后面的那一段字符。 【Developer ID Application: xxxxxxxxxxxxxxx Co.,Ltd. (xxxxxx)】, 即 : 后面的字符。
另外相关配置也可以在 electron builder 中配置,不过基于安全考虑,更推荐环境变量的形式
由于 gitlab runner 暂时没有 macos的运行环境,本文使用开发机打包,最终获取到CSC_NAME, 将其设置到系统环境变量里,可以修改.bashrc 文件, 末尾增加以下代码。
公证相关参数
