极客手中的利器:Electron

九路 等级 919 0 0

作为一个前端开发人员,你可能已经听说过Electron了,

你知道VS Code是基于这个技术开发的。

不但VS Code,目前一些大热的软件:飞书、Slack、skype的桌面版都是基于这个技术开发的。

即使如此,这也并不足以引起你的重视,

毕竟桌面软件式微,移动端和Web开发才是大方向,你的工作可能根本就不会涉及桌面开发领域的内容。

但我想告诉你的是:

即使工作不会涉及到,也应该学一下Electron,因为它是极客手里的利器,你可以通过他做很多Amazing的事情

本文主要讲讲Electron是什么,以及它能做什么极客工作。

极客手中的利器:Electron

本文大部分内容摘自我的新书《Electron实战——入门、进阶与性能优化》,目前刚刚上市:

京东购买地址: https://item.jd.com/69220543055.html

天猫购买地址: https://detail.tmall.com/item.htm?spm=a230r.1.14.144.38ec66adZeGfZS&id=618291446747&ns=1&abbucket=16

(_市面上还有另外两本关于Electron的书,它们都没有涉及本文中所讲的这些极客知识_)

同时,也欢迎对这个领域知识感兴趣的朋友加我的QQ群:949674481,一起交流

Electron是什么

官方的介绍是:

基于它开发者可以“使用 JavaScript,HTML 和 CSS 构建跨平台的桌面应用程序”。

我们知道 JavaScript,HTML 和 CSS都是前端的技术,前端技术是要用在浏览器中的,

Electron能让开发者这么做,它内部至少有一个浏览器核心。

没错,它内部的浏览器核心就是谷歌的Chromium

你可以把Chromium理解为Chrome的精简版,

Chromium不包含tab页的控制,搜索引擎、书签等等这类东西,

但它包含:排版引擎、渲染引擎、脚本引擎、调试工具等。

它是一个很纯净的浏览器核心(还称不上一个浏览器,只是一个浏览器核心)。

但单单有浏览器核心还不够,

因为开发一个桌面应用还涉及到文件系统访问、进程控制、使用各种网络通信协议等

(浏览器对HTTP协议支持的很好,但对更底层的TCP/IP协议就没什么支持了)

再加上各种常见加解密、集成原生程序模块等需求,

如果自己实现一套库以支持这些需求,显然工作量非常巨大,Electron团队索性把Node.js集成进来了

但这依然不够,

因为Node.js毕竟更善于做服务端的事情,大部分开发者使用Node.js都是在命令行下使用的,

像使用系统菜单、系统级快捷键、剪切板、托盘图标、系统对话框这些能力,

Node.js就显得无能为力了,

所以这部分工作是Electron团队自己用C++实现的,而且各个平台都实现了一遍,以保证跨平台需求。

可以说Chromium、Node.js和一套C++库是构成Electron的三大基石。

当然这样分析有点浮于表面,还有很多细节值得深究,

比如Electron是如何打通Chromium和Node.js的事件循环的、

Electron是如何通过主进程控制各个窗口,以及各个窗口是如何相互通信的等。

这篇文章里我们就先暂且不详细讨论这些内容。

另外,Electron也不是以我分析的这种方式设计演化来的,

它的由来还要从它的一个竞品NW.js说起

NW.js和Electron非常相似,我们前面说的三大基石NW.js也都有。

非但如此,这两个框架还有着极深的渊源,以下内容摘自我书稿的一部分:

2011年左右,中国英特尔开源技术中心的王文睿(Roger Wang)希望能用Node.js来操作WebKit,而创建了node-webkit项目,这就是NW.js的前身。当时的目的并不是用这两个技术来开发桌面GUI应用。  

中国英特尔开源技术中心大力支持了这个项目,不但允许王文睿分出一部分精力来做这个开源项目,还给了他招聘名额,允许他招聘其他工程师来一起完成这个项目。
2012年,故事的另一个主角赵成(Cheng Zhao)加入到王文睿的小组,并对node-webkit项目做出了大量的改进。
后来赵成离开了中国英特尔开源技术中心,帮助github团队尝试把node-webkit应用到Atom编辑器上,但由于当时node-webkit还并不稳定,且node-webkit项目的走向也不受赵成的控制,这个尝试最终以失败告终。
但赵成和github团队并没有放弃,而是着手开发另一个类似node-webkit的项目:Atom Shell,这个项目就是Electron的前身,赵成在这个项目上倾注了大量的心血,这也是这个项目后来广受欢迎的关键因素之一,再后来github把这个项目开源出来,最终更名为Electron。
你可能从没听说过这两个名字,但开源界就是有这么一批“英雄”,他们不为名利而来,甘做软件行业发展的铺路石,值得这个领域的所有从业者尊敬。

这些技术(三大基石)组合在一起,不但能开发出大型商业应用的桌面客户端,

而且也为一些极客应用提供了很大的便利性。下面我们把典型的几个拿出来聊聊:

注入脚本

Electron内部拥有一个完整得浏览器核心,

你可以用程序操纵这个浏览器核心,让它加载一个第三方网页,比如:淘宝的生意参谋、网易云音乐、gitee等,

但单单加载这些网页,并没有什么稀奇的,毕竟在浏览器里也能加载这些网页。

最有意思的是,你还可以给这些网页注入脚本,比如像下面这样,注入一个Js文件到目标网页:

let win = new BrowserWindow({
 webPreferences: { 
   preload: path.join(appPath, 'yourPreload.js'),
   nodeIntegration: true 
 }
});
win.loadURL('https://www.baidu.com/');

如果你只希望注入一两句代码,也可以通过如下形式注入脚本:

let decryptStr = await this.win.webContents.executeJavaScript(`window.__allen_decrypt('${encryptStr}')`);

这些脚本是在目标网页的作用域下执行的,与目标网页的工程师自己写的代码没什么区别。

想象一下,你如果想调用目标网页的某个服务端接口,你是不是应该考虑如何模拟token,如何跨域等等

现在你只需要在脚本里直接写调用接口的逻辑就可以了。

另外,如果你不喜欢目标网页的页面样式,你也可以直接注入一段样式脚本,代码如下:

let key = await win.webContents.insertCSS("html, body {  background-color: #f00 !important; }");
//await win.webContents.removeInsertedCSS(key); //在未来的某个时刻,还可以移除掉这个样式的作用

值得一提的是,你注入的脚本还可以访问Node.js的API

也就是说,你在脚本中获取到了目标网页的资源后,可以直接写到你本地文件里。

突破同源策略

注入了脚本,获取到了受限的资源,你可能希望把这些资源提交到你自己的服务器上,

或者你可能希望在注入的脚本里,访问另一个网站的API,以获取更多的资源,

这个时候,如果没做特殊配置的话,同源策略就会起作用,限制你这么干,

浏览器往往会报如下错误:

Access to XMLHttpRequest at 'https://www.domain1.com/aggsite/AggStats' from origin 'http://www.domain2.com:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

“同源策略”是浏览器的一个安全功能,

“同源”是指如果两个页面的协议(http/https)、端口和主机都相同,则两个页面具有相同的源。

同源策略规定不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。

只有同一个源的脚本才具备读写cookie、session、ajax等的操作的权限。

在Electron中突破同源策略,就是一两个配置的事情,代码如下:

let win = new BrowserWindow({
    width: 800,height: 600,
    webPreferences: {
        nodeIntegration: true,
        webSecurity: false, //此参数禁用当前窗口的同源策略
    }
})
win.loadURL('https://www.baidu.com/');

这样设置之后,浏览器的所有同源策略限制就全部失效了。

如果你只是希望在https域下访问http的资源,那么你可以不用关掉整个同源策略,

只需要把allowRunningInsecureContent这个配置设置为true即可(它同样也是webPreferences下的一个属性)

如果你设置webSecurity为false了,那么allowRunningInsecureContent会自动设置为true。

读写HttpOnly标记的Cookie

由于浏览器每次与服务端的交互,都会携带同域下的所有Cookie,

所以网站开发者往往会把标记用户身份的信息(比如用户token)放到Cookie里。

一般情况下,前端开发工程师可以使用document.cookie访问浏览器里存储的同域的Cookie,

但也有例外,凡标记了HttpOnly的Cookie,通过这种方式都是访问不到的。

网站开发者之所以这么做,主要是为了防止跨站脚本攻击(XSS)和跨站请求攻击(CSRF)

跨站脚本攻击(XSS,是Cross Site Scripting的缩写),一旦网站允许用户提交内容,并且会在网站的某些页面上显示用户提交的内容,比如留言或者博客,那么不做防范的话,就有可能受到跨站脚本攻击。恶意用户会在提交内容时在内容中夹带一些恶意JavaScript脚本,当其他用户访问页面时,浏览器会运行这些恶意脚本,恶意脚本有可能会窃取用户的Cookie、页面上的用户隐私信息等,并发送到恶意用户的服务器。他们可以通过这些窃取来的信息模拟用户身份完成非法操作。这就是跨站脚本攻击。  
跨站请求攻击(CSRF,是Cross Site Request Forgery的缩写),当用户登录了自己信赖的网站后,用户身份信息(token)会被保存保存在用户的浏览器上,后来用户又不小心打开了一个恶意网站,这个恶意网站可能会要求浏览器请求用户信赖的网站(通过iframe等形式),如果用户信赖的网站没有做安全防范的话,可能会被恶意网站获取到用户的敏感信息(token),从而给用户带来伤害。

但这个限制在Electron面前也不值一提,我们可以通过下面这种方式读写受限访问的Cookie:

//获取Cookie
async function(name) {
    let cookies = await remote.session.defaultSession.cookies.get({name});
    if(cookies.length>0) return cookies[0].value;
    else return '';
}
//设置Cookie
async function(cookie) {
    await remote.session.defaultSession.cookies.set(cookie);
}

通过这种方式,无论Cookie有没有设置HttpOnly属性,都可以成功读写。

转发/修改请求

有的时候你不单单是希望给第三方网页附加代码逻辑,

而是希望侵入式的修改第三方网站自身的代码逻辑。

但往往第三方的JavaScript代码是在一个闭包作用域内执行的

你的代码没办法注入到这个作用域内,去访问作用域内的变量或方法

碰到这样的状况该怎么办呢?

此时,你第一步要做的,就是分析清楚它的脚本是如何执行的,是哪个脚本文件执行的。

这个过程是一个非常有趣的过程,不可避免的你要用到谷歌浏览器的开发者调试工具,

如果对方的代码是压缩过的,你可能还要给它“美化”一下,再逐步调试(如下图)。

极客手中的利器:Electron

搞清楚逻辑之后,就把他的脚本文件下载下来,然后在这个文件中加上你的逻辑,

你的逻辑可能就是粗暴的把它闭包作用域内的变量暴露到window对象上。

这样你注入的脚本,就可以访问这个变量了。

(这个过程有很多技巧,因为与Electron无关,我就不展开了)

修改完这个脚本文件后,把这个脚本文件host到你自己的一个服务器上,

然后通过Electron,把网页加载这个脚本文件的请求,转发到你自己的服务器上去,

这个转发请求的代码如下:

win.webContents.session.webRequest.onBeforeRequest({ urls: ["https://*/*"] }, async (details, cb) => {
    if (details.url === 'https://g.alicdn.com/dt/op-mc/vendors.js') {
        cb({ redirectURL: 'http://domain.com/vendors.js' });
    } else {
        cb({})
    }
});

当这个网页再试图加载这个脚本时,得到的结果,将是你修改过的脚本。

有些网站,对一些敏感数据保护的很好,客户端请求这些数据时,得到的是服务端加密过的数据,

客户端执行解密后,再使用这些数据。

对于这类网站,Electron的这个能力,无疑是非常有力的。

当然,你也可以考虑使用service worker来办这个事儿,甚至可以自己在客户端模拟一个响应,不用再经由你的服务器转发了。

如果你没有自己的服务器,也可以通过Node.js的能力,自己在软件里起一个localhost的服务。

反“防盗链”

有的时候,你可能只是想把目标网站的一些静态资源嵌入到你的应用程序中,

比如:图片或者视频

然而如果目标网站已经做了防盗链的工作,你这个功能可能就没那么容易实现了。

防盗链的主要目的有两个:一个是版权问题,别人未经授权就使用你的资源,

另一个是流量压力的问题,盗链产生了大量的请求,这些请求对于网站运营者来说没任何价值。

防盗链最常见的做法就是识别HTTP的Refer请求头,这个请求头代表着发起请求时前一个网页的地址,

网站运维工程师会根据这个Refer请求头来推测出当前请求是否为一个盗链请求,

(判断这个Refer请求头的内容是不是自己域名下的一个地址)

Electron允许开发者监听发起请求的事件,并允许开发者在发起请求前修改请求头

我们可以在这个事件里修改这个Refer请求头,代码如下:

let session = window.webContents.session;
let requestFilter = { urls: ["http://*/*", "https://*/*"] };
session.webRequest.onBeforeSendHeaders(
    requestFilter,
    (details, callback) => {
      if (details.resourceType == "image" && details.method == "GET") {
        delete details.requestHeaders["Referer"]; //这里我直接删掉了这个请求头,也可以修改成其他内容
      }
      callback({ requestHeaders: details.requestHeaders });
    }
);

给应用植入socks5代理科学上网

Chrome本身是支持代理的

使用Electron你可以通过编程的方式把你的代理内置到你的应用程序中

这样你的用户就可以自由的访问国外的一些网站了

这部分的技术我不多做介绍了,书里写了。

最后

我曾经用Electron开发过一个文章发布工具,可以把文章一键发布到:博客园、CSDN、简书、大鱼号、头条号等

不过很久没维护了,希望以后有时间再重写一版。

收藏
评论区

相关推荐

极客手中的利器:Electron
作为一个前端开发人员,你可能已经听说过Electron(https://www.electronjs.org/)了, 你知道VS Code是基于这个技术开发的。 不但VS Code,目前一些大热的软件:飞书、Slack、skype的桌面版都是基于这个技术开发的。 即使如此,这也并不足以引起你的重视, 毕竟桌面软件式微,移动端和Web开发才是大方向
(三)使用electron
前提已经实现了(一)里面的效果连接:[https://www.cnblogs.com/yuNotes/p/12884930.html](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.cnblogs.com%2FyuNotes%2Fp%2F12884930.html) #### 1
(原)Electron+vue 应用实战
1.electron架构思考 ============== 在做electron桌面开发中,Electron+vue当下算是性价比的比较高的。但是electron算是小众开发,遇到问题,基本上就是掉进深坑,爬好久才出来。 为了做一个项目,我翻遍了国内好多网站。看到一篇好的文章。[Electron 应用实战 (架构篇)](https://www.oschi
3、electron打包生成exe文件
打包方式1:DOS窗口命令打包 --------------- DOS 下,输入 npm install electron-packager -g**全局安装**我们的打包神器: cnpm install electron-packager -g 在项目文件夹下面,也就是根目录下面打开DOS执行打包命令: electron-packa
electron(1)安装和使用
下载和安装 # Clone the Quick Start repository git clone https://github.com/atom/electron-quick-start # Install the dependencies and run npm install && npm start 会
AudioContext方法播放音频
`const fs = require('fs'); const electron = require('electron'); const dataPath = (electron.app || electron.remote.app).getPath('userData'); const fPath =` ${dataPath}/file/unzip\`
Electron 快速入门及最新安装教程
> 作者:程序员学院   官方网址:[https://www.chengxuyuan.com](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.chengxuyuan.com)    微信公众号:华仁程序员学院 一、 Electron简介 ------------- Electr
Electron中通过asar打包后ffi
在使用Electron开发完应用程序后,需要打包生成各平台对应的可执行文件供用户使用。 这里以Mac平台为例,最简单的打包方案是使用**electron-packager**来对应用程序进行打包操作,如执行最简单的指令`electron-packager .`执行完成后会在同级目录生成`${appName}-${platfomr}-${arch}`目录如e
Electron开发使用Vue Devtools
转自 \[[https://orchidflower.oschina.io/2017/03/29/Using-Vue-Devtools-in-Electron/](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Forchidflower.oschina.io%2F2017%2F03%2F29
Electron开发跨平台桌面程序入门教程
最近一直在学习 Electron 开发桌面应用程序,在尝试了 java swing 和 FXjava 后,感叹还是 Electron 开发桌面应用上手最快。我会在这一篇文章中实现一个HelloWord的应用程序,记录学习过程中的坑。 Electron是什么呢? ------------- Electron 可以让你使用纯 JavaScript 调用丰富的
Electron桌面端所见即所得
### 突然让你开发Electron应用,你能hold住吗? 如果领导突然说需要开发一款前端桌面端应用,那么对于我们前端er来说选择Electron是一件顺理成章的事情。但事实上很多同学对于Electron都不太了解和熟悉。 **如果突然让我们去开发Electron应用,很多人都会陷入迷茫和懵逼的状态**。然后在依靠网上相对较少的资料,慢慢摸索、一路踩坑
Electron踩坑记录
心血来潮想做一个PC端应用,就来学学Electron,以下为学习Electron时的踩坑记录。 心血来潮想做一个PC端应用,就来学学Electron,以下为学习Electron时的踩坑记录。 安装 -- 在国内安装`electron`的时候,可能会因为网络原因遇到卡在`Building fresh packages...`(yarn)或者是卡在`nod
Rust 的 GUI 框架生态概览
本文比较全面比较了目前主流的 Rust 的 GUI 框架的表现。其中 ++ 表示非常好,-- 表示非常差,而 o 表示处于平均水平。 参与对比的框架共有 8 个,详细的比较结果如下:   Electron + Neon Electron + FFI Electron + NodeJS Cpp Addon Rust Program + Qt sta
TMS WEB Core v1.2预览版:新的Electron应用程序支持
#### 2019年2月20日,星期三 几个月前,我们已经开始与[Electron进行](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Ftmssoftware.com%2Fsite%2Fblog.asp%3Fpost%3D495)实验。在工作概念验证之后,我们的目标是为Delphi开发人
【electron】ipc模块使用
electron 进程模型 electron 使用多进程架构与chromium类似,electron使用多进程架构。单一进程架构能够节省资源,然而浏览器经常遇到恶意代码,可能导致进程崩溃,浏览器的多个页签公用一个进程必然会一损俱损,因此多进程架构更适合些()。在开发electron的过程中,我们主要接触两个进程: 主进程main process与渲染进程re