# 关于预加载脚本
Electron 的主进程和渲染进程有着清楚的分工。
Electron 的主进程是一个拥有着完全操作系统访问权限的 Node.js 环境。但出于安全原因,渲染进程默认跑在网页页面上,而并非 Node.js里。为了将 Electron 的不同类型的进程桥接在一起,我们需要使用被称为 预加载 的特殊脚本。
# 使用预加载脚本来增强渲染器
预加载脚本会在渲染器的网页加载之前注入。
下面我们来创建一个将应用中的 Chrome、Node、Electron 版本号暴露至渲染器的预加载脚本。
1.新建一个 preload.js
文件。该脚本通过 versions
这一全局变量,将 Electron 的 process.versions
对象暴露给渲染器。
// preload.js
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('versions', {
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron,
// 能暴露的不仅仅是函数,我们还可以暴露变量
})
2
3
4
5
6
7
8
9
2.将脚本运用进渲染器中。在 BrowserWindow 构造器中使用 webPreferences.preload
传入脚本的路径。
const { app, BrowserWindow } = require('electron')
const path = require('path')
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
})
win.loadFile('index.html')
}
app.whenReady().then(() => {
createWindow()
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
这里使用了两个Node.js概念:
__dirname
(opens new window) 字符串指向当前正在执行脚本的路径 (在本例中,它指向你的项目的根文件夹)。path.join
(opens new window) API 将多个路径联结在一起,创建一个跨平台的路径字符串。
现在渲染器能够全局访问 versions
了,让我们快快将里边的信息显示在窗口中。 这个变量不仅可以通过 window.versions
访问,也可以很简单地使用 versions
来访问。
# 访问 versions
1.新建一个 renderer.js
脚本, 这个脚本使用 document.getElementById
(opens new window) DOM 接口来替换 id
属性为 info
的 HTML 元素显示文本。
const information = document.getElementById('info')
information.innerText = `本应用正在使用 Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), 和 Electron (v${versions.electron()})`
2
2.执行该脚本
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<meta
http-equiv="X-Content-Security-Policy"
content="default-src 'self'; script-src 'self'"
/>
<title>来自 Electron 渲染器的问好!</title>
</head>
<body>
<h1>来自 Electron 渲染器的问好!</h1>
<p>👋</p>
<p id="info"></p>
</body>
<script src="./renderer.js"></script>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
项目运行后将会输出:
本应用正在使用 Chrome (v106.0.5249.119), Node.js (v16.16.0), 和 Electron (v21.2.0)
# 在进程之间通讯
Electron 的主进程和渲染进程有着清楚的分工并且不可互换。 这代表着无论是从渲染进程直接访问 Node.js 接口,亦或者是从主进程访问 HTML 文档对象模型 (DOM),都是不可能的。
解决这一问题的方法是使用进程间通信 (IPC)。可以使用 Electron 的 ipcMain
模块和 ipcRenderer
模块来进行进程间通信。
为了从你的网页向主进程发送消息,你可以使用 ipcMain.handle
设置一个主进程处理程序(handler),然后在预处理脚本中暴露一个被称为 ipcRenderer.invoke
的函数来触发该处理程序(handler)。
比如添加一个全局函数 ping
:
1.预处理脚本中设置的 invoke
调用
将 invoke
作为ping
暴露出去
// preload.js
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('versions', {
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron,
ping: () => ipcRenderer.invoke('ping'),
// 能暴露的不仅仅是函数,我们还可以暴露变量
})
2
3
4
5
6
7
8
9
10
2.在主进程中设置 handle 监听器
每当渲染器触发 invoke
时,此函数被用作一个回调。然后,返回值将作为一个 Promise 返回到最初的 invoke
调用。
// main.js
const { ipcMain } = require('electron')
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
})
ipcMain.handle('ping', () => 'pong')
win.loadFile('index.html')
}
2
3
4
5
6
7
8
9
10
11
12
13
14
3.调用 invoke
// renderer.js
const func = async () => {
const response = await window.versions.ping()
console.log(response) // 打印 'pong'
}
func()
2
3
4
5
6
7