本文主要有以下内容:
Electron环境Electron主进程、渲染进程环境Electron主进程、渲染进程- 渲染进程、窗口、index.html之间的关系
webpack如何处理主进程、渲染进程: target: 'electron-renderer', electron-main- 神奇的
remote-主进程、渲染进程通信
在看这篇之前,如果你对Electron基础已经了解,则可以继续,如果不了解,建议先看下:
Electron环境
Electron是一个跨平台的桌面应用技术,从开发的角度,可以片面的理解为js的一个执行环境。比如: 浏览器是js的一个执行环境,node也是js的一个执行环境,同样Electron也是js的一个执行环境。那么什么叫执行环境呢,其实我的理解是一个执行环境包含2个内容:
可解析、执行该语言的解析器
会有一些环境下可运行的基础设施(api)提供给该语言运行
举个例子:
- 浏览器里有v8引擎解析执行js, 在window对象上也有丰富的api提供给运行的js使用。符合上述2点。当然离开浏览器,我们可以用node去运行js,但是没法使用浏览器的api。就是因为node环境并不存在那些基础设施。
node也有v8解析执行js,同时也有丰富的api提供给js使用,比如fs这种,但你在浏览器里运行,则不会有这样的基础设施api可以使用。
因此,Electron本质上是一个js的执行环境,所以这个环境包含哪些内容呢,请看下图:
可见Electron环境其实包含了浏览器环境、Node环境、系统原生的一些API,包含了浏览器环境、Node环境也就意味着其实也包含了v8语言解释器。因此在Electron里可以运行浏览器的api,node的api,部分系统的api调用。
Electron 主进程、渲染进程环境
上面我们讲到,Electron其实是js的执行环境,包含浏览器环境、Node环境、系统原生的一些API,那么意味着在Electron里写的js,都可以使用上述三个环境的各种api嘛?当然不是,如果是这样,就会造成混乱。因此,在Electron这个大环境里,又分了js运行的小环境:主进程环境、渲染进程环境。不同环境里,有他们可以执行的api。请看下图:
由图可见,在Electron大环境里,分了Main process、Renderer Process环境,不同环境有可执行的不同的api,当然也有共同可以执行的一些api。Main process、Renderer Process环境的相同点是,都会有v8可以解析js,不同点是可以使用的api不全相同。
那么,有人就会问了,为什么在这个Electron大环境里,还需要去分Main process、Renderer Process两个小环境,去限制不同环境的api调用呢?那接下来大家得知道,在Electron里什么是主进程、渲染进程了,知道这个是什么,才知道为什么要限制他们的运行环境api。
Electron 主进程、渲染进程
习惯了浏览器的单进程编写,可能接触Electron的"多进程"有点困扰,但还好,在写代码的时候还是单进程的写法。只是需要理解一点进程的东西。
通过任务管理器,我们可以看到,其实Electron启动后会有好多进程,除了主进程、渲染进程、还会有一些辅助进程。这里,我们主要描述主进程、渲染进程的作用和意义。
在这之前,大家可以自己去了解下进程和线程的概念。我就说一个进程的特点:彼此进程是不会共享内存和状态的。举个例子:在浏览器里面,一个Tab页其实就是在单个进程里,2个tab之间不会共享内容,比如tab1网页中的window上挂在一个window.a,但在tab2中的window上你并不会获得window.a这个变量,因为他们在不同的进程里。这样的一个好处就是,一个Tab死掉了,并不会影响另一个网页的内容,也并不会卡死浏览器。
基于此,我们会以浏览器这个大家熟悉的东西来理解Electron的主进程、渲染进程,以及他们之间的关系。
其实浏览器这个应用本身,是一个进程,打开它就会运行起一个主进程(当然会有其余辅助子进程,暂不考虑),当我们打开一个标签页的时候,就开启了一个渲染进程,大家可以简单的看一下这个图:
他们之间有这些关系:
- 一个标签页死了,另一个标签页不会死,照常运行
- 一个标签页死了,浏览器不会卡死,还是可以开启其他窗口
- 一个标签页死了,浏览器也会死
说好的,进程之间互不影响呢,怎么一个标签页死了,还会出现浏览器死了的情况,这就涉及到主进程和渲染进程之间的一个基本关系了。浏览器是主进程,他可以创建标签页这种子进程。就好比,浏览器是地球,一个标签页就是一个人。一个人挂了,地球不会挂。也不会影响另一个人挂不挂,但是假如这个人在挖洞,把地球挖炸了,那其实地球就挂了,所有人也挂了。
可以看出,主进程会管理(创建、销毁)渲染进程(标签页),有以下特点:
- 渲染进程结束,主进程不会结束(标签页关了,浏览器还在)
- 主进程结束,渲染进程都会结束(浏览器关闭,标签页都会关闭)
- 渲染进程如果太过分,资源用光,也会导致主进程崩溃
接下来我们在Electron里来看一下,进程的特点。
首先,我们来新建一个小demo:
mkdir electron-process-test // 新建文件夹electron-process-test
cd electron-process-test
npm init
ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/ yarn add electron@6.0.9 -D
再新建一个main.js, 写入:
console.log('hello');
接着我们用electron运行这个js,跟node运行js类似(node main.js),此处在package.json中添加命令:
{
...
"dev": "electron main.js",
...
}
最后运行 yarn dev, 打出了'hello', 我们在任务管理器可以看到:
上述步骤,我们就启动了一个Electron主进程,即图中的带图标的那个Electron进程,他启动了一个额外的辅助进程Electron helper,是GPU处理进程。主要是那个主进程。
接着我们打开一个窗口,这个窗口其实就是一个渲染进程,我们来试一下,在main.js中:
const {app, BrowserWindow} = require('electron');
app.on('ready', () => {
const win = new BrowserWindow({
width: 400,
height: 400
});
});
我们再次运行:yarn dev,发现打开了一个窗口,我们在看下任务管理器:
还是2个进程,一个Electron, 一个Electron Helper,因为这个窗口在没有加载页面(本地html文件或网页)的时候,不会开启渲染进程。所以我们加载一个页面试试:
const {app, BrowserWindow} = require('electron');
app.on('ready', () => {
const win = new BrowserWindow({
width: 400,
height: 400
});
win.loadURL('https://www.baidu.com');
});
在看任务管理器:
发现多了一个Electron Helper, 虽然不知道哪个helper是这个窗口的,因为都叫这个名字,但是这个就是渲染进程了。在一个窗口加载一个页面的时候就会新开一个。
我们试试再开一个窗口:
const {app, BrowserWindow} = require('electron');
app.on('ready', () => {
const win = new BrowserWindow({
width: 400,
height: 400
});
win.loadURL('https://www.baidu.com');
const win1 = new BrowserWindow({
width: 400,
height: 400
});
win1.loadURL('https://www.baidu.com');
});
yarn dev 再看任务管理器, 就会发现又多了一个Electron helper, 多了一个渲染进程:

以上过程,我们直观感受到了什么是主进程、渲染进程:
- 主进程就是使用
electron main.js的时候,就会开启一个主进程:Electron. - 渲染进程会在新建窗口后,
loadURL的时候开始渲染进程:Electron Helper. ps:loadURL也可以加载一个HTML,就跟我们浏览器的Tab差不多了。
win.loadURL(`file://${__dirname}/../index.html`);
渲染进程、窗口、index.html之间的关系
他们之间有如下关系:
- 主进程中
new BrowserWindow()会开启一个系统窗口,但不会开启一个独立的进程,窗口由主进程管理。 - 窗口可以
loadURL加载一个页面,此时会开启这个页面的渲染进程。 - 当
loadURL加载一个index.html文件的时候,此时渲染进程里运行的就是这个index.html的内容。
webpack如何处理主进程、渲染进程: target: 'electron-renderer', 'electron-main'
前面,我们讲述了:主进程、渲染进程,以及他们之间的关系,那开发、发布的时候,我们该怎么处理相关的js呢。首先,我们应当有如下概念:
- 不同进程的js,肯定是不同的入口,因此采用
webpack4的多入口方式打包即可。 - 主进程js(就是electron main.js中main.js这个入口相关的js),需要一个生产环境webpack配置文件。因为开发环境下不需要webpack做什么,所以不需要。
- 渲染进程js(即index.html里面加载的js)开发环境下需要用webpack-dev-server做热刷新,所以会单独一个webpack配置文件。
- 渲染进程js在生产环境下,需要一个webpack配置文件。
- 为什么主进程的和渲染进程的js生产环境下不使用同一个配置文件,因为处理方式有些不同,因此不再同一个文件里写多入口方式配置而写成两个文件。
- 如果打开两个及以上窗口,即会有很多个渲染进程,则只需要在render的配置文件里使用多入口配置就好了。
所以会有如下3个配置文件:
- webpack.config.main.prod.js // 主进程生产环境配置文件
- webpack.config.render.prod.js // 渲染进程生产环境配置文件
- webpack.config.render.dev.js // 渲染进程开发环境配置文件
具体的配置内容,可以看Electron-从零到一搭建-编写基础看框架。这里我讲一下webpack配置里的:target
我们比较熟悉的就是在webpack中配置:
- target: 'node'
- target: 'web'
表示的就是,处理js的时候采用node(采用web)环境去处理,这样,在识别到一些api比如node的
fs的时候,就不会报错,知道是什么。 同理,我们在处理我们的js的时候也需要根据其运行环境去处理,这样就能识别一些语法、api用法等。 - 主进程设置:target: 'electron-main'
- 渲染进程设置:target: 'electron-renderer'
神奇的remote-主进程、渲染进程通信
这个我就不写了,写累了,自己去看下面推荐的文章吧,去体会。
此处给大家推荐一些文章去深入了解: