在这篇文章中,我们将使用Electron和React创建一个简单的桌面应用程序。它将是一个名为 "scratchpad "的小型文本编辑器,在你输入时自动保存更改,类似于 刮刮乐.我们将注意通过使用Electron Forge,即Electron团队提供的最新的构建工具,使应用程序安全。
Electron Forge是 "一个用于创建、发布和安装现代Electron应用程序的完整工具"。它提供了一个方便的开发环境,以及为多平台构建应用程序所需的一切配置(尽管我们不会在本文中触及这一点)。
我们假设你知道Electron和React是什么,尽管你不需要知道这些来跟随文章。
你可以在GitHub上找到完成的应用程序的代码。
设置
本教程假定你的机器上已经安装了Node。如果不是这样,请前往官方下载页面,为你的系统抓取正确的二进制文件,或者使用版本管理器,如nvm。我们还将假设你已经安装了Git。
下面我将使用的两个重要术语是 "主 "和 "渲染器"。Electron应用程序是由一个Node.js JavaScript文件 "管理 "的。这个文件被称为 "main "进程,它负责任何与操作系统相关的工作,并负责创建浏览器窗口。这些浏览器窗口运行Chromium,并被称为Electron的 "渲染器 "部分,因为它是真正将东西渲染到屏幕上的部分。
现在让我们开始建立一个新的项目。由于我们想使用Electron Forge和React,我们将前往Forge网站并查看集成React的指南。
首先,我们需要用webpack模板来设置Electron Forge。下面是我们如何在一个终端命令中做到这一点。
$ npx create-electron-app scratchpad --template=webpack
运行该命令将需要一点时间,因为它设置和配置了从Git到webpack到package.json 文件的一切。当这些都完成后,我们cd 到那个目录,这就是我们所看到的。
➜ scratchpad git:(master) ls
node_modules
package.json
src
webpack.main.config.js
webpack.renderer.config.js
webpack.rules.js
我们将跳过node_modules 和package.json ,在我们偷看src 文件夹之前,让我们看一下 webpack 文件,因为有三个。这是因为Electron实际上运行了两个JavaScript文件:一个是Node.js部分,称为 "main",它在这里创建浏览器窗口并与操作系统的其他部分进行通信;另一个是Chromium部分,称为 "renderer",它是实际显示在你屏幕上的部分。
第三个webpack文件--webpack.rules.js --是设置Node.js和Chromium之间的任何共享配置以避免重复的地方。
好了,现在是时候看看src 这个文件夹了。
➜ src git:(master) ls
index.css
index.html
main.js
renderer.js
没有太大问题:一个HTML和CSS文件,以及一个用于主程序和渲染器的JavaScript文件。这看起来不错。我们将在文章的后面打开这些文件。
添加React
配置webpack可能是非常令人生畏的,所以幸运的是,我们可以在很大程度上按照指南将React集成到Electron中。我们首先要安装所有我们需要的依赖项。
首先是devDependencies 。
npm install --save-dev @babel/core @babel/preset-react babel-loader
紧接着是React和React-dom作为常规依赖项。
npm install --save react react-dom
在安装了所有的依赖项之后,我们需要教webpack支持JJSX。我们可以在webpack.renderer.js 或webpack.rules.js 中完成,但我们将按照指南,把下面的加载器添加到webpack.rules.js 。
module.exports = [
...
{
test: /\.jsx?$/,
use: {
loader: 'babel-loader',
options: {
exclude: /node_modules/,
presets: ['@babel/preset-react']
}
}
},
];
好的,这应该可以了。让我们快速测试一下,打开src/renderer.js ,将其内容替换为以下内容。
import './app.jsx';
import './index.css';
然后创建一个新的文件src/app.jsx ,并加入以下内容。
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(<h2>Hello from React in Electron!</h2>, document.body);
我们可以通过在控制台中运行npm start 来测试这是否有效。如果它打开的窗口显示 "Hello from React in Electron!",就说明一切正常了。
你可能已经注意到,当窗口显示时,devtools是打开的。这是因为main.js 文件中的这一行。
mainWindow.webContents.openDevTools();
暂时不用管它,因为在我们工作时它会派上用场。在文章的后面,当我们配置它的安全和其他设置时,我们会涉及到main.js 。
至于控制台中的错误和警告,我们可以安全地忽略它们。将React组件安装在document.body ,确实会有第三方代码干扰的问题,但我们不是一个网站,不会运行任何不属于我们的代码。Electron也给了我们一个警告,但我们以后会处理这个问题。
建立我们的功能
作为提醒,我们将建立一个小的划痕板:一个小的应用程序,在我们输入任何东西的时候都会保存下来。
首先,我们将添加CodeMirror和react-codemirror,这样我们就可以得到一个易于使用的编辑器。
npm install --save react-codemirror codemirror
让我们来设置CodeMirror。首先,我们需要打开src/renderer.js ,导入并要求一些CSS。CodeMirror有几个不同的主题,所以选择一个你喜欢的,但在这篇文章中我们将使用Material主题。你的renderer.js现在应该看起来像这样。
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/material.css';
import './app.jsx';
import './index.css';
注意我们是如何在CodeMirror的CSS之后导入我们自己的文件的。我们这样做是为了以后能更容易地覆盖默认的样式。
然后在我们的app.jsx 文件中,我们要导入我们的CodeMirror 组件,如下所示。
import CodeMirror from 'react-codemirror';
在app.jsx ,创建一个新的React组件,加入CodeMirror。
const ScratchPad = () => {
const options = {
theme: "material"
};
const updateScratchpad = newValue => {
console.log(newValue)
}
return <CodeMirror
value="Hello from CodeMirror in React in Electron"
onChange={updateScratchpad}
options={options} />;
}
同时替换渲染函数来加载我们的ScratchPad组件。
ReactDOM.render(<ScratchPad />, document.body);
当我们现在启动应用程序时,我们应该看到一个文本编辑器,上面写着 "Hello from CodeMirror in React in Electron"。当我们在其中输入时,更新的内容会显示在我们的控制台中。
我们还看到,有一个白色的边框,我们的编辑器实际上并没有填满整个窗口,所以让我们做一些事情。当我们这样做的时候,我们将在我们的index.html 和index.css 文件中做一些内部管理。
首先,在index.html ,让我们删除body元素内的所有内容,因为我们不需要它。然后,我们将标题改为 "Scratchpad",这样,当应用程序加载时,标题栏就不会说 "Hello World!"。
我们还将添加一个Content-Security-Policy 。这意味着什么在本文中无法处理(MDN有一个很好的介绍),但它本质上是一种防止第三方代码做我们不希望发生的事情的方法。在这里,我们告诉它只允许来自我们的原点(文件)的脚本,而不允许其他。
总而言之,我们的index.html ,将是非常空的,看起来像这样。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Scratchpad</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self';">
</head>
<body></body>
</html>
现在让我们移到index.css 。我们可以删除现在在那里的所有东西,用这个来代替它。
html, body {
position: relative;
width:100vw;
height:100vh;
margin:0;
background: #263238;
}
.ReactCodeMirror,
.CodeMirror {
position: absolute;
height: 100vh;
inset: 0;
}
这做了几件事。
- 它删除了body元素默认拥有的空白。
- 它使CodeMirror元素的高度和宽度与窗口本身相同。
- 它为主体元素添加了相同的背景颜色,所以它能很好地融合在一起。
请注意我们是如何使用inset的,它是top、right、bottom和left值的简写CSS属性。因为我们知道我们的应用程序总是要在Chromium 89版本中运行,所以我们可以使用现代的CSS而不用担心支持问题
所以这很好:我们有一个可以启动的应用程序,并且可以让我们在其中输入。很好!但是,当我们关闭应用程序,并向它输入信息的时候,我们就会发现,它已经被关闭。
除了,当我们关闭应用程序并再次重启时,一切又都消失了。我们想写到文件系统中,这样我们的文本就被保存了,而且我们想尽可能安全地做到这一点。为此,我们现在将注意力转移到main.js 文件上。
现在,你可能也注意到了,尽管我们给html 和body 元素添加了背景色,但当我们加载应用程序时,窗口仍然是白色的。这是因为我们的index.css 文件需要几毫秒的时间来加载。为了改善这种外观,我们可以在创建浏览器窗口时,将其配置为特定的背景颜色。因此,让我们去我们的main.js 文件并添加一个背景颜色。改变你的mainWindow ,使它看起来像这样。
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
backgroundColor: "#263238",
});
现在,当你开始时,白色的闪光应该消失了!