如何用Electron Forge和React建立一个安全的桌面应用程序

757 阅读6分钟

在这篇文章中,我们将使用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_modulespackage.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.jswebpack.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也给了我们一个警告,但我们以后会处理这个问题。

建立我们的功能

作为提醒,我们将建立一个小的划痕板:一个小的应用程序,在我们输入任何东西的时候都会保存下来。

首先,我们将添加CodeMirrorreact-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.htmlindex.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 文件上。

现在,你可能也注意到了,尽管我们给htmlbody 元素添加了背景色,但当我们加载应用程序时,窗口仍然是白色的。这是因为我们的index.css 文件需要几毫秒的时间来加载。为了改善这种外观,我们可以在创建浏览器窗口时,将其配置为特定的背景颜色。因此,让我们去我们的main.js 文件并添加一个背景颜色。改变你的mainWindow ,使它看起来像这样。

const mainWindow = new BrowserWindow({
  width: 800,
  height: 600,
  backgroundColor: "#263238",
});

现在,当你开始时,白色的闪光应该消失了!

继续阅读 在SitePoint 上用Electron Forge和React建立一个安全的桌面应用程序