Electron 集成谷歌扩展实现原理,相关知识点汇集,line插件集成demo,gitee代码,坑点解析

背景

目前谷歌扩展有 50W 之多,覆盖面比较广,而 Electron 又是基于谷歌浏览器内核开发的,如果将两者结合,那么就能大大节省很多开发时间,开发出来的客户端功能却更加丰富,不过 Electron 并没有完美支持谷歌扩展,尤其是谷歌扩展 MV3 版本中提供的大量的 API,Electron 本身都是不支持的,这就给开发者一种误导,Electron 不支持 MV3 的扩展。

原理分析

  1. 谷歌扩展的 API 就是一些权限,这些权限就是客户端的权限,然后再把这些权限转移给扩展开发者;

  2. 再来看小程序的实现,就是将微信能够获取到的相册,图片,视频,摄像头等手机所能获取到的权限转移给 html5 网页,通过注入一个变量 wx.xxx 的方式,提供了大量 api,而底层通过进程间通信,例如 postmessage 来实现数据的传递以及事件的传递;

  3. 再看,如果我们自己想用 html5 开发安卓 APP,并且想让 html5 按钮调用手机相册,那么我们就提供一个对象,该对象有一个函数可以拉起手机相册,并获取手机相册的照片并返回 base64 数据,那么我们把这个对象注入到 webview 里面去,webview 调用这个对象的这个函数时,就等同于调用安卓下的 api 接口,实现微信小程序的功能。

  4. 综上所述,谷歌扩展,就是用谷歌浏览器操作 windows 或者 mac,本身已经获得了一些权限,同时还把这些权限暴露给谷歌扩展,实现了更丰富的网页操作过程。

实现原理

  1. Electron 本身就是一个客户端,本身就能拿到各种桌面端权限,同时又能与网页之间通信,如使用 preload.js 进行通信,这就完成了网页与 Electron 进程之间的事件传递和数据传递,于是当扩展在网页中调用一些 API,例如 tabs,storage,cookie,download,notifications 时,你只需要重写这些对象里面的函数,并且将这些函数的动作和行为传递给 Electron,实现异步的事件传递,那么就变相地兼容了这些扩展。

核心知识

  1. 插件的归属应该属于用户层级,而用户层级由 session 决定,一个用户可以有多个 session,session 包含了访问一个网站时的特有配置,就像类个对象的概念,session 是一次访问网页时的实例,session 本身又是独立存在的,就相当于去政务大厅办事,一个公务员如果对接一个来访者就需要将来访者寄过来的资料找到,然后开始给这个人处理事务,这些资料就是 session,而公务员就是 window 或者 webview,这样来讲应该就更清晰了,而插件相当于归属该用户的一个特殊流程和证书等,所以扩展属于一个 session;

  2. session 分为内存式,也即我们所说的无痕模式,访问完成后,所有的用户数据还有缓存统统从内存释放,另外一种就是持久化,所有的用户数据和缓存都会存放在用户目录下的一个文件夹下,被称为分区(partition),持久化分区前面要带个 persist:

  3. 所有的 window 或者 webview 都有默认的 session,defaultSession,就像你打开谷歌浏览器,每次新开一个用户时,都有一些默认数据一样,所以大家尽量不要在 defaultSession 搞事情,不懂的随便弄,懂了就要精细点,不然影响一片 session,不但资源浪费,还影响性能,针对不同的 session.fromPartition() 来做不同的事情

  4. window 和 webview 和 webContent 是两码事,前者可以理解为容器本身的设置,后者就是网页内容的操作,比如注入 js 进行点击或者输入等,都是 webContent 来做的事情,但是类似是否开启 nodeintegration 这种事情就是容器来做的事情了,有些事情 webview.loadUrl 这个函数其实际调用的应该是 webview.webContent.loadUrl,webContent 创建时间要晚于 webview 的时间,所以 webview 有时候调用的函数会出现未定义错误,原因就是 webview 调用了 webContent 的 api 导致的。了解到这个知识对开发者很有用,你就知道一些错误为什么发生了,这时你的脑子就是多线渲染,而不是单一 webview 渲染,只要多线就会出现不同步的问题,就会出现并行发生的问题。

  5. electron 的 window 和 webview 没有谷歌浏览器的 tab 功能,且谷歌浏览器的 tab 功能共享一个 session(即同一个用户数据),比如 tab A 登录了 facebook,tab B 打开后也是登录了 facebook,两者是互通的,而在 electron 中,没有 tab 的概念后,tab A 和 tab B 就需要创建两个 webview,而 tab 标签就需要自己来实现了,因此针对 谷歌扩展 MV3 来说,chrome.tabs 某些 API 就无法实现。

  6. session 和 webview 是两个独立体系,且 session 的创建必须要在 webview 创建之前创建,如果不事先手动创建 session,那么 webview 的属性 partition 就会先探测 session 的 ID 是否已存在,已存在则直接用,不存在则创建,来保证 webview 的启动时带着 session 的

  7. session loadExtension 是一个耗时的过程,且因为 session 本身与 webview 的渲染是并行存在的,因此 session loadExtension 未完成时,就让 webview 访问扩展提供的chrome-extension://extension_id/option.html 页面,会出现空白现象,因此,创建 webview 前尽量先把 session 准备好,并且通过 await loadExtension 彻底载入成功后,再渲染 webview,这样就不会有任何问题

  8. 开发前端一定要有并行处理的思维,千万别一根筋开发,否则开发出来的东西时好时坏,自己也不知道问题出在哪里,要充分理解 Promise 的用法,充分理解 async/await 解决的场景,充分地思考逻辑的调度的时序,既不能阻碍并行程序的执行,又要让串行程序(前端的默认都是异步并行不阻塞的)正常执行,才能让代码写得很健壮,从理论上绝对保证不出问题。

Promise 一个基于协程下的任务队列状态管理任务包 解决复杂的异步转同步问题_promise 任务队列执行不同的任务-CSDN博客

代码案例

electron-demo: electron 22 初始代码开发和讲解 - Gitee.com

代码截图

@electron/remote 技术大家看之前的文章,这里略

Electron 渲染进程直接调用主进程的API库@electron/remote引用讲解-CSDN博客

这里使用 remote 的技术,主要是原来的代码需要在主进程的 web-contents-created事件里搞,且那个过程如果加了 await,会导致其他进程阻塞,但是渲染进程没有调用 electron.session 的权利,所有用 remote 进行桥接代劳,实现异步转同步的操作,在 webview 创建前,就先把 session 准备好

下面这段代码,经过上面的知识点讲解,应该一下子就理解了吧,但我们不用这种方式,这种方式的可控性太差,除非你用了 webview 标签,否则尽量不要在这里倒腾 session

// 修改webview(whatsapp站点)
app.on('web-contents-created', (event, webContents) => {
    if (webContents.getType() === 'webview') {
        const ses = webContents.session
        const extensionPath = path.join(__static, '/line/ophjlpahpchlmihnnnihgmmeilfjmjjc/3.3.0_0');
        ses.loadExtension(extensionPath).then()
    }
})

我们用 remote 重新封装的 session.fromPartition 来异步转同步操作,先让主进程装填好 session,然后通过 Promise 的方式将异步转成同步的方式,实时获取当前 webview 需要用的 session,这里没用到 await 的方式我还没有深究 remote 源码是怎么做到的,总之能用即可(ipcRenderer.sendSync),然后再用 await session.loadExtension,等待插件彻底调用 OK 后,再动态开始创建 webview,下面是 webview 的动态创建过程,这样 webview 就可以在插件准备好访问扩展的 option.html 而不出现空白的问题了。

那些 Electron 未能支持的 api,例如 chrome.tabs/download/notifications 怎么办?

补充知识点
  1. preload.js 默认与 webContent 内容是隔离的,是运行在一个沙盒环境中的,在里面追加 window.xxx 是不会影响 webContents 内容的,但是也有例外就是你关闭了上下文隔离,下面代码就是关闭,关上后,electron 沙盒环境会报错(但不影响程序运行而且也能往 window 中追加了)

webview.setAttribute('webpreferences', 'allowRunningInsecureContent,contextIsolation=false');

报错如下

但是 electron 没提供 dom 运行前的 js 注入,不像谷歌扩展,可以选择 document_start 注入脚本 

最终效果

根据提供的引用内容,我们可以得知 Electron 底层是通过 http/https post 来上传崩溃日志的请求,因此我们可以通过 wireshark 抓包工具抓到 Electron 捕获崩溃并上传至服务器的请求。但是,Electron 本身并不支持集成 wireshark,因为 wireshark 是一个独立的抓包工具,需要单独安装和运行。 如果你想在 Electron 应用程序中集成 wireshark,你可以考虑使用 Electron 的 child_process 模块来启动 wireshark 进程,并通过 IPC 通信机制与 Electron 主进程进行通信。具体步骤如下: 1.在 Electron 应用程序中安装 wireshark,并确保 wireshark 可以在命令行中运行。 2.在 Electron 主进程中使用 child_process 模块启动 wireshark 进程,并通过 IPC 通信机制与 Electron 主进程进行通信。具体代码如下: ```javascript const { spawn } = require('child_process'); const wireshark = spawn('wireshark', ['-k']); wireshark.stdout.on('data', (data) => { console.log(`stdout: ${data}`); }); wireshark.stderr.on('data', (data) => { console.error(`stderr: ${data}`); }); wireshark.on('close', (code) => { console.log(`child process exited with code ${code}`); }); ``` 3.在 Electron 渲染进程中使用 IPC 通信机制与 Electron 主进程进行通信,并获取 wireshark 抓包数据。具体代码如下: ```javascript const { ipcRenderer } = require('electron'); ipcRenderer.on('wireshark-data', (event, data) => { console.log(data); }); ``` 4.在 wireshark 中设置过滤器,以便只捕获 Electron 应用程序的网络流量。 以上是一种可能的实现方式,但需要注意的是,集成 wireshark 可能会对 Electron 应用程序的性能产生影响,因此需要谨慎使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

森叶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值