:::success 知识点
- 能够独立搭建一个 electron 的学习环境
:::
:::warning
备注
安全性问题:
文中提到了一些安全性的问题,不过仅仅就是提一嘴罢了,在学习阶段无需过分去关注安全问题,这是一个比较大的话题,入门 Electron 之后,再结合官方文档的建议,熟悉下 Electron 中的安全性问题即可。
工具:
文中还提及了俩辅助 electron 学习的工具:
- Electron API Demos(有中文版)
- Electron Fiddle
根据实际需求选择性食用吧,这玩意儿个人感觉没必要装,学习 Electron 的 API,其实可以快速(≈ 1min)手搭一个 Electron 学习环境来学。
难点:
文章中提到的 nodeIntegration 和 contextIsolation 配置不易理解(理解不了不要砸进去太多的时间,先跳过),简言之就是 electron 官方为了解决安全问题而加入的。
现在需要知道的是:如果想要在渲染进程中访问 nodejs api 和 electron api 的话,那么我们需要这么配置:{ nodeIntegration: true, contextIsolation: false }
:::
笔记
首先,需要简单初始化一下开发环境,跟着 2.1 的流程走即可。如果出现安装 electron 报错,或者 electron 安装过慢,可以回看一下书中的解决方案。
包管理器:书中使用的是 yarn,不过这东西根据喜好选择即可,用 npm、pnpm、cnpm、yarn…… 都行。
工程初始化之后,在工程的根目录下边,添加一个 index.html 文件,内容如下:
<html>
<head>
<title>窗口标题</title>
</head>
<body>
<div style="padding: 60px; font-size: 38px; font-weight: bold; text-align: center;">
Hello Worlk
</div>
</body>
</html>
再添加一个 index.js 文件,内容如下:
const { app, BrowserWindow } = require('electron')
let win = null
app.on('ready', () => {
win = new BrowserWindow({
webPreferences: {
nodeIntegration: true,
// contextIsolation: false
}
})
win.loadFile('index.html')
win.on('closed', () => win = null)
})
app.on('window-all-closed', () => app.quit())
**app**
代表着整个应用,通过它可以获取应用程序生命周期中的各个事件。我们在app
的ready
事件中创建了窗口,并且把窗口对象交给了一个 全局引用,这样做是为了 不让 JavaScript 执行引擎在垃圾回收时回收这个窗口对象。new BrowserWindow(webPreferences: { nodeIntegration: true })
在创建窗口时,我们传入了配置对象webPreferences: { nodeIntegration: true }
,此配置对象 告知 Electron 需要为页面集成 Node.js 环境,并赋予 index.html 页面中的 JavaScript 访问 Node.js 环境的能力。- 书中提到的这种写法,是针对旧版本,比如老师在写书时 Electron 的
v8
版本,可以使用上面这种写法。但是最新的 Electronv25
,还得加上一个字段contextIsolation: false
- 安全问题:如果加载的页面是一个互联网页面,你无法验证改页面提供的内容是否可靠,应该关闭这个选项:
webPreferences: { nodeIntegration: false }
,否则这个页面上的一些恶意脚本也会拥有访问 Node.js 环境的能力,可能会给你的用户造成损害。
- 书中提到的这种写法,是针对旧版本,比如老师在写书时 Electron 的
win.loadFile('index.html')
窗口创建完成后,我们让窗口加载了 index.html。win.on('closed', () => win = null)
在窗口关闭时,我们把win
这个全局引用置空了,通常我们会将这个操作放在 应用退出时执行。但是,由于在本应用中我们其实只有一个窗口,只要该窗口被关闭,就意味着所有窗口被关闭,退出了**app**
,所以这本应该在**app**
退出时执行的逻辑,完全可以写在窗口关闭的事件中。app.on('window-all-closed', () => app.quit())
如果app
应用中的所有窗口都关闭了,那么默认退出应用。yarn start
启动应用,窗口的标题即为网页的title
,窗口的内容即为网页body
内的元素。
Electron API Demos
Electron API Demos 是一个辅助学习 Electron API 的一个桌面应用。可以在 releases 中获取该应用的安装包,根据自己的系统选择对应的安装包直接安装即可。
这个应用默认是英文的操作界面,也有对应的 中文版,不过中文版貌似并没有给我们直接提供 releases 下载链接,若需要的话,得自行拉代码,然后到本地运行。
需要注意的是,这玩意儿貌似在 22-12-06 已经“归档”(archived)了,也就是这个 github 仓库不再维护了。它将不再随着 Electron 的不断更新而更新,后续新版的 Electron 中出现的新版 API,我们无法在这个应用上体验。
Electron Fiddle
Electron Fiddle:这是 Electron Fiddle 的官网,和 Electron API Demos 类似,Electron Fiddle 也是一个用来辅助学习 Electron 的桌面应用程序
为什么开启 nodeIntegration
之后,不能通过 script 标签来引入 jQuery 呢?
这个问题的原因在于 Electron 和浏览器环境的一些关键区别。在 Node.js 环境中(包括 Electron 的主进程和通过 nodeIntegration
启用了 Node.js 支持的渲染进程中),全局作用域有一个 require
函数,它用于加载 Node.js 模块。
另一方面,jQuery 也使用 require
这个名称,但用途不同。在浏览器环境中,jQuery 使用 require
作为一个内部函数名,用于加载 jQuery 模块。
当你在 Electron 的渲染进程中启用 **nodeIntegration**
并尝试通过 **<script>**
标签引入 jQuery 时,jQuery 的 **require**
函数会与 Node.js 的 **require**
函数冲突。这是因为 Node.js 的 **require**
已经存在于全局作用域中,而 jQuery 又试图在同一全局作用域中定义自己的 **require**
函数。这导致 jQuery 无法正常加载,因为它不能正确处理这种冲突。
为了避免这个问题,你可以在 Electron 中禁用 nodeIntegration
,这样就不会在全局作用域中定义 Node.js 的 require
函数。然后,你可以使用 Electron 的 contextIsolation
和 preload
脚本特性来在渲染进程中安全地使用部分 Node.js 功能,而无需启用 nodeIntegration
。在 preload
脚本中,你可以选择性地暴露 Node.js 的 require
或其他函数给渲染进程,这样就不会与 jQuery 冲突。
另一种解决方案是,你可以通过 Node.js 的 require
函数来加载 jQuery,而不是通过 <script>
标签。这样,jQuery 将作为 Node.js 模块加载,并且不会与全局 require
函数冲突。例如,你可以在 Electron 的渲染进程中这样使用 jQuery:
const $ = require('jquery');
请注意,这种方法需要你通过 npm 安装 jQuery 到你的 Electron 应用中: npm install jquery
。
contextIsolation 和 nodeIntegration
**contextIsolation**
是 Electron 中的一个重要安全特性。它的目的是将渲染器(renderer)进程中的 JavaScript 上下文隔离开,防止预加载(preload)脚本和渲染进程中的脚本彼此干扰。当 **contextIsolation**
设为 **true**
时,你的预加载脚本运行在一个单独的 JavaScript 上下文中,而网页的 JavaScript 运行在另一个上下文中。
默认情况下,从 Electron 12 开始,**contextIsolation**
是开启的。这意味着即使你开启了 **nodeIntegration**
,你的网页脚本也不能直接访问 Node.js API,因为它们现在运行在隔离的 JavaScript 上下文中。你需要使用 contextBridge
API 在预加载脚本中明确地将你希望网页能访问的 Node.js 功能暴露给渲染进程。
在你将 contextIsolation
设为 false
后,预加载脚本和网页脚本都将运行在同一个 JavaScript 上下文中,所以你的网页脚本可以直接访问 Node.js API。然而,请注意,关闭 contextIsolation
可能会降低应用的安全性,特别是如果你的应用加载的是不受信任的远程内容。在这种情况下,恶意的 JavaScript 可能会尝试访问 Node.js API,从而造成潜在的安全风险。
这就是为什么 window.require === require
成立当 contextIsolation: false
时:因为在这种情况下,window
对象和全局作用域是在同一个 JavaScript 上下文中,所以它们实际上是同一个对象。
为了在保持应用安全的同时能在渲染进程中访问 Node.js API,你应该使用 contextIsolation: true
和 contextBridge
API。你可以在预加载脚本中使用 contextBridge
将你需要的 Node.js 功能安全地暴露给渲染进程。例如:
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld(
'myAPI', {
doSomething: () => ipcRenderer.send('do-something')
}
)
然后在网页脚本中,你可以通过 window.myAPI.doSomething()
来调用你在预加载脚本中暴露的函数。