2023金秋版:基于electron-vite构建React桌面客户端

Electron是一个基于Chromium和Node.js,可以使用HTML、CSS和JavaScript构建跨平台应用的技术框架,兼容Mac、Windows和 Linux。虽然B/S是目前开发的主流,但是C/S仍然有很大的市场需求。受限于浏览器的沙盒限制,网页应用无法满足某些场景下的使用需求,而桌面应用可以方便地读写本地文件、发起跨域请求、调用更多系统资源,再加上Web开发低成本、高效率的优势,这种方式越来越受到开发者的喜爱。

2023年4月,我发布了《2023新春版:手把手教你搭建Electron24+React18+Antd5架构工程》,那篇文章是基于React官方提供的Create-React-App(简称CRA)起步进行构建的。如今CRA已被React抛弃,连新版React官网都没有再提到CRA了。而Vite成为了主流。因此,本系列教程也及时跟进时代的步伐,更新为Vite脚手架工具。综合考虑多方面因素,最终选择electron-vite作为本教程的主角。electron-vite现已推出1.x正式版,虽然没有被Electron和Vite官方提到,但经过实战,体验还是不错的。在省去了手动融合Electron和Vite繁琐过程的同时,还实现了V8字节码、主进程和预加载脚本热更新等非常实用的功能,要比自己从头搭建容易得多。

本教程将electron-vite开发过程详细讲述,希望能够帮助各位省去摸索的时间,少走弯路,快速完成项目开发。React技术栈的小伙伴不要错过哟!

本教程也同步推出《2023金秋版:基于electron-vite构建Vue桌面客户端》,欢迎Vue技术栈的小伙伴来阅读。

先睹为快

先看下目录了解本教程都有哪些内容。

章节目录

css 复制代码
1 Electron核心概念
• 1.1 主进程(main)
• 1.2 渲染进程(renderer)
• 1.3 预加载脚本(preload)
2 初始化项目
• 2.1 使用electron-vite新建项目
• 2.2 精简项目
• 2.3 去掉renderer的src目录
3 Vite基础配置
• 3.1 配置国内镜像源
• 3.2 支持Sass/Scss/Less/Stylus
• 3.3 设置路径别名
4 项目架构搭建
• 4.1 项目目录结构设计
• 4.2 关于样式命名规范
• 4.3 设置全局公用样式
5 引入Ant Design 5.x
• 5.1 安装Ant Design
• 5.2 设置Antd为中文语言
6 渲染进程页面开发
• 6.1 构建Login页面
• 6.2 构建Home页面
• 6.3 实现页面路由跳转
• 6.4 在React组件中实现页面路由跳转
• 6.5 在非React组件中实现页面路由跳转
7 主进程与渲染进程通信方法一:send与on/once
• 7.1 预加载脚本(preload)开发
• 7.2 主进程开发
• 7.3 继续渲染进程开发
• 7.4 运行效果
• 7.5 关于ipcRenderer.on/once
8 主进程与渲染进程通信方法二:invoke与handle
• 8.1 主进程开发
• 8.2 在渲染进程显示Electron版本号
9 常用配置
• 9.1 设置应用图标
• 9.2 设置APP窗口大小
• 9.3 取消跨域限制
• 9.4 设置DevTools快捷键
• 9.5 禁止build环境的DevTools
10 build项目
• 10.1 执行build命令
• 10.2 解决windows版本编译时无法下载的问题
• 10.3 设置build版应用icon
• 10.4 其他应用信息配置
• 10.5 解决macOS build版本的Coding Siging错误
• 10.6 build后的目录结构
11 其他说明
• 11.1 源代码保护
• 11.2 敏感字符串保护
• 11.3 主进程热更新
• 11.4 禁止同个Electron程序多开
• 11.5 允许windows用户更改安装目录
• 11.6 批量升级全部项目npm依赖包
12 项目Git源码
结束语

本Demo主要依赖包版本

Node.js 18.18.0

electron 26.3.0

electron-builder 24.6.4

vite 4.4.11

react 18.2.0

react-dom 18.2.0

react-router-dom 6.16.0

antd 5.9.4

less 4.2.0

sass 1.69.0

stylus 0.60.0
※注:

代码区域每行开头的:

"+" 表示新增

"-" 表示删除

"M" 表示修改

1 Electron核心概念

学习Electron最先要掌握的就是他的主进程与渲染进程概念。网上很多相关教程也进行了详细介绍,又是画关系图又是文字描述的。这里只想把最核心的内容总结一下,以最简单最通俗的方式让各位迅速理解。

掌握三个核心概念即可:

1.1 主进程(main)

每个Electron应用都有一个单一的主进程,作为应用程序的入口点。主进程运行在Node.js 环境,因此可以使用Node.js的所有API能力。主进程并不在浏览器环境中运行,因此不进行页面渲染。

1.2 渲染进程(renderer)

渲染进程说白了就是平时的Web页面前端开发。每个Electron应用都会为每个打开的BrowserWindow生成一个单独的渲染器进程。但是渲染进程是无法直接调用Node.js的API的。

1.3 预加载脚本(preload)

为了让渲染进程与主进程进行通信从而形成一个整体,预加载脚本的作用就是他们之间的桥梁。预加载脚本与渲染进程共享同一个全局window,因此可以通过window来传递数据。但并不是简单地通过给window添加属性就能使用,以下方式是无法把preload.js共享给渲染进程使用的:

javascript 复制代码
// 预加载脚本(preload.js)
window.myAPI = {
  desktop: true
}
javascript 复制代码
// 渲染进程
console.log(window.myAPI)
// => undefined(获取不到)

这是因为Electron的语境隔离(Context Isolation),使得预加载脚本与渲染进程的主要运行环境是隔离的,以避免将任何API都加入到渲染进程的网页中。

因此,需要使用contextBridge来安全地实现交互:

php 复制代码
// 预加载脚本(preload.js)
const { contextBridge } = require('electron')

contextBridge.exposeInMainWorld('myAPI', {
  desktop: true
})
javascript 复制代码
// 渲染进程
console.log(window.myAPI)
// => { desktop: true } (成功获取)

以上方式就是通过contextBridge.exposeInMainWorld将preload.js中的myAPI数据共享给渲染进程的网页使用。

官网详细介绍:

www.electronjs.org/zh/docs/lat...

2 初始化项目

2.1 使用electron-vite新建项目

找个合适的目录,执行项目创建命令。

npm命令:

sql 复制代码
npm create @quick-start/electron

yarn命令:

sql 复制代码
yarn create @quick-start/electron

pnpm命令:

sql 复制代码
pnpm create @quick-start/electron

本教程使用yarn,后续不再赘述npm和pnpm的命令。

如果没有安装yarn,可执行以下命令全局安装:

csharp 复制代码
npm install --global yarn

yarn中文网站:

yarn.bootcss.com/

执行后,会要求填写项目名称,这里我填写的是electron-vite-react-app,可根据情况自定。

yaml 复制代码
? Project name: electron-vite-react-app

然后,会要求选择框架,选择react:

css 复制代码
? Select a framework: 
    vanilla
    vue
>   react
    svelte
    solid

最后是几个配置选项:

yaml 复制代码
? Add TypeScript? >> No / Yes   
是否使用TypeScript?选No,本教程使用JavaScript。如果喜欢TypeScript,请选择Yes。
? Add Electron updater plugin? >> No / Yes 
是否添加Electron updater插件?选择Yes。
? Enable Electron download mirror proxy? >> No / Yes
是否开启Electron镜像下载代理。在国内网络环境,强烈建议选择Yes。

项目创建完成后,执行以下命令:

bash 复制代码
cd electron-vite-react-app  (进入项目目录)
yarn(安装依赖包)

安装完成后,执行启动命令:

yarn dev

运行效果如下:

2.2 精简项目

接下来,删除用不到的目录和文件,最简化项目。

bash 复制代码
    ├─ /.vscode
    ├─ /build
    ├─ /node_modules
    ├─ /out
    ├─ /resources
    ├─ /src
    |  ├─ /main
    |  ├─ /preload
    |  ├─ /renderer
    |  |  ├─ /src
-   |  |  |  ├─ /assets
    |  |  |  ├─ /components
-   |  |  |  |  └─ Versions.jsx
    |  |  |  ├─ App.jsx
    |  |  |  └─ main.jsx
    |  |  └─ index.html
    ├─ .editorconfig
    ├─ .eslintignore
    ├─ .eslintrc.cjs
    ├─ .gitignore
    ├─ .npmrc
    ├─ .prettierignore
    ├─ .prettierrc.yaml
    ├─ dev-app-update.yml
    ├─ electron-builder.yml
    ├─ electron.vite.config.js
    ├─ package.json
    ├─ README.md
    └─ yarn.lock

2.3 去掉renderer的src目录

以上项目结构有两个src,容易混淆,而且路径表达也略显啰嗦。可以把src/renderer/src目录里的文件放到src/renderer目录中,然后删除src/renderer/src目录。

调整后的renderer目录如下:

bash 复制代码
...(略)
|  ├─ /renderer
|  |  ├─ /components
|  |  ├─ App.jsx
|  |  ├─ index.html
|  |  └─ main.jsx
...(略)

以上文件删除后,页面会报错。这是因为相应的文件引用已不存在。需要继续修改代码,先让项目正常运行起来。

逐个修改以下文件,最终精简代码依次如下:

src/renderer/App.jsx:

javascript 复制代码
function App() {
    return <div className="App">Electron-Vite-React-App</div>
}

export default App

src/renderer/main.jsx:

javascript 复制代码
import ReactDOM from 'react-dom/client'
import App from './App'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<App />)

src/renderer/index.html:

xml 复制代码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
        <title>Electron-Vite-React</title>
        <meta
            http-equiv="Content-Security-Policy"
            content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
        />
    </head>

    <body>
        <div id="root"></div>
        <script type="module" src="/main.jsx"></script>
    </body>
</html>

在windows环境中,src/renderer/index.html的<title>内容将显示在客户端窗口标题栏中。

执行yarn dev,运行效果如下:

3 Vite基础配置

3.1 配置国内镜像源

npm和yarn默认是从国外源站拉取依赖包的,为提高下载速度和稳定性,建议配置为国内镜像源。

yarn registry国内镜像:

arduino 复制代码
yarn config set registry https://registry.npmmirror.com

npm registry国内镜像:

arduino 复制代码
npm config set registry https://registry.npmmirror.com

yarn node-sass国内镜像:

arduino 复制代码
yarn config set SASS_BINARY_SITE https://npmmirror.com/mirrors/node-sass/

npm node-sass国内镜像(仅限于npm < v9的版本):

arduino 复制代码
npm config set SASS_BINARY_SITE https://npmmirror.com/mirrors/node-sass/

yarn Electron国内镜像:

arduino 复制代码
yarn config set electron_mirror https://npmmirror.com/mirrors/electron/

npm Electron国内镜像(仅限于npm < v9的版本):

arduino 复制代码
npm config set electron_mirror https://npmmirror.com/mirrors/electron/

※注: 从npm v9 版本开始,npm 命令限制了npm config set只能设置npm官方规定的config (docs.npmjs.com/cli/v9/usin...%25EF%25BC%258C%25E5%259B%25A0%25E6%25AD%25A4%25E6%2597%25A0%25E6%25B3%2595%25E5%2583%258F%25E6%2597%25A7%25E7%2589%2588%25E6%259C%25AC%25E4%25B8%2580%25E6%25A0%25B7%25E8%25AE%25BE%25E7%25BD%25AE%25E9%2595%259C%25E5%2583%258F%25E7%25AB%2599%25E4%25BA%2586%25EF%25BC%258C%25E4%25B8%258D%25E8%25BF%2587%25E4%25BE%259D%25E6%2597%25A7%25E5%258F%25AF%25E4%25BB%25A5%25E9%2580%259A%25E8%25BF%2587%25E7%25BC%2596%25E8%25BE%2591 "https://docs.npmjs.com/cli/v9/using-npm/config)%EF%BC%8C%E5%9B%A0%E6%AD%A4%E6%97%A0%E6%B3%95%E5%83%8F%E6%97%A7%E7%89%88%E6%9C%AC%E4%B8%80%E6%A0%B7%E8%AE%BE%E7%BD%AE%E9%95%9C%E5%83%8F%E7%AB%99%E4%BA%86%EF%BC%8C%E4%B8%8D%E8%BF%87%E4%BE%9D%E6%97%A7%E5%8F%AF%E4%BB%A5%E9%80%9A%E8%BF%87%E7%BC%96%E8%BE%91") ~/.npmrc 文件的方式实现旧版本npm config set的效果。

如果不清楚本地当前yarn或者npm的配置,可以执行以下命令查看:

yarn查看方法:

arduino 复制代码
yarn config list

npm查看方法:

arduino 复制代码
npm config list

如果npm版本在v9及以上的话,需要通过修改项目目录的.npmrc文件来配置镜像源。强烈建议进行以下配置。

修改项目根目录的.npmrc:

ini 复制代码
    ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/
+   SASS_BINARY_SITE=https://npmmirror.com/mirrors/node-sass/

经过以上配置,Electron依赖包下载就会变得很快。

3.2 支持Sass/Scss/Less/Stylus

Vite本身提供了对.scss/.sass/.less/.styl/.stylus文件的内置支持。无需再安装特定的Vite插件,但必须安装相应的预处理器依赖。

支持Sass/Scss,执行以下命令安装:

csharp 复制代码
yarn add -D sass

支持Less,执行以下命令安装:

csharp 复制代码
yarn add -D less

支持Stylus,执行以下命令安装:

csharp 复制代码
yarn add -D stylus

安装后,就可以直接使用以上对应的CSS预处理语言了,非常方便。

CSS预处理Vite官方说明:

cn.vitejs.dev/guide/featu...

3.3 设置路径别名

electron-vite已经预设了路径别名配置。

例如:src/renderer/App.jsx,可以直接省略成@renderer/App.jsx。

由于本教程已经删除了src/renderer/src目录,因此需要修改对应的预设配置。

修改electron.vite.config.js:

css 复制代码
    ...(略)
    renderer: {
        resolve: {
            alias: {
-               '@renderer': resolve('src/renderer/src')
+               '@renderer': resolve('src/renderer')
            }
        },
        plugins: [react()]
    }
    ...(略)

4 项目架构搭建

4.1 项目目录结构设计

项目目录结构可根据项目实际灵活制定。这里分享下我常用的结构。重点是renderer目录的结构设计,主要分为公用模块目录、组件模块目录、页面模块目录、路由配置目录等几个部分,让项目结构更加清晰合理。

lua 复制代码
├─ /.vscode
├─ /build                      <-- build编译过程性输出目录
├─ /dist                       <-- 最终build的输出目录
├─ /node_modules
├─ /out                        <-- dev和build编译过程性输出目录
├─ /resources                  <-- 主进程和preload的公共资源目录
├─ /src
|  ├─ /main                    <-- 主进程开发目录
|  ├─ /preload                 <-- preload开发目录
|  ├─ /renderer                <-- 渲染进程开发目录
|  |  ├─ /api                  <-- api目录
|  |  |  └─ index.jsx          <-- api库
|  |  ├─ /common               <-- 全局公用目录
|  |  |  ├─ /fonts             <-- 字体文件目录
|  |  |  ├─ /images            <-- 图片文件目录
|  |  |  ├─ /js                <-- 公用js文件目录
|  |  |  └─ /styles            <-- 公用样式文件目录
|  |  |  |  ├─ frame.styl      <-- 全部公用样式(import本目录其他全部styl)
|  |  |  |  ├─ reset.styl      <-- 清零样式
|  |  |  |  └─ global.styl     <-- 全局公用样式
|  |  ├─ /components           <-- 公共模块组件目录
|  |  |  ├─ /header            <-- 头部导航模块(示例)
|  |  |  |  ├─ index.jsx       <-- header主文件
|  |  |  |  └─ header.styl     <-- header样式文件
|  |  |  └─ ...                <-- 其他模块
|  |  ├─ /pages                <-- 页面组件目录
|  |  |  ├─ /home              <-- home页目录
|  |  |  |  ├─ index.jsx       <-- home主文件
|  |  |  |  └─ home.styl       <-- home样式文件
|  |  |  ├─ /login             <-- login页目录
|  |  |  |  ├─ index.jsx       <-- login主文件
|  |  |  |  └─ login.styl      <-- login样式文件
|  |  |  └─ ...                <-- 其他页面
|  |  ├─ /router               <-- 路由配置目录
|  |  |  └─ index.jsx          <-- 路由配置文件
|  |  ├─ App.jsx               <-- 项目主页面文件(删除)
|  |  ├─ main.jsx              <-- 渲染进程入口文件
|  |  └─ index.html            <-- 渲染进程HTML模板
├─ .editorconfig               <-- IDE配置文件
├─ .eslintignore               <-- 忽略eslint检查的配置文件
├─ .eslintrc.cjs               <-- eslint配置文件
├─ .gitignore   
├─ .npmrc                      <-- npm镜像源配置文件
├─ .prettierignore             <-- 忽略prettier代码格式化的配置文件
├─ .prettierrc.yaml            <-- prettier代码格式化配置文件
├─ dev-app-update.yml
├─ electron-builder.yml        <-- build配置文件
├─ electron.vite.config.js     <-- electron-vite配置文件
├─ package.json
├─ README.md
└─ yarn.lock

注意以上项目结构中,由于本教程使用页面路由,src/renderer/App.jsx将在后续章节中被删除。

接下来,就按照上面的目录结构开始构建项目。

4.2 关于样式命名规范

以我多年来的开发经验来讲,合理的样式命名规范对项目开发有很大的帮助,主要体现在以下方面:

(1)避免因样式名重复导致的污染。

(2)从命名上可直观区分"组件样式"、"页面样式"(用于给在此页面的组件样式做定制调整)、"全局样式"。

(3)快速定位模块,便于查找问题。

分享一下本教程的样式命名规范:

G-xx: 表示全局样式,用来定义公用样式。

P-xx: 表示页面样式,用来设置页面的背景色、尺寸、定制化调整在此页面的组件样式。

M-xx: 表示组件样式,专注组件本身样式。

后续教程中,可以具体看到以上规范是如何应用的。

4.3 设置全局公用样式

我个人比较喜欢Stylus简洁的语法,因此本教程以Stylus作为css预处理语言。各位可以根据自己的习惯,自由选择Sass/Scss、Less、Stylus。

新建清零样式文件,src/renderer/common/styles/reset.styl。

由于reset.css代码较多,这里不再放出。非常推荐参考这个reset css,代码比较全面,更新也比较及时(截至本文写作时,是2023年9月20日更新的)。

CSS reset代码详见:github.com/elad2412/th...

新建全局样式文件,src/renderer/common/styles/global.styl:

arduino 复制代码
html, body, #root
  height: 100%
/*清浮动*/
.clearfix:after
  content: "."
  display: block
  height: 0
  clear: both
  visibility: hidden
.clearfix
  display:block

全局样式将应用于项目的所有页面,可根据需要自行补充或调整。

新建全局样式总入口文件,src/renderer/common/styles/frame.styl:

scss 复制代码
@import './reset.styl';
@import './global.styl';

在frame.styl里引入其他公用样式,就方便一次性全部应用到项目中了。

然后在src/renderer/main.jsx里引入frame.styl:

javascript 复制代码
    import ReactDOM from 'react-dom/client'
    import App from './App'
+   // 全局样式
+   import '@renderer/common/styles/frame.styl'
    
    const root = ReactDOM.createRoot(document.getElementById('root'))
    root.render(<App />)

这样在所有页面里就可以直接使用全局样式了。

现在运行项目,可以发现reset、global中的样式已经生效。

5 引入Ant Design 5.x

Ant Design是一款非常优秀的UI库,在React项目开发中使用非常广泛。Ant Design发布5.x后,使用起来更加快捷,而且在主题换肤方面更加便捷。本次分享也特别说明下如何使用Ant Design(以下简称Antd)。

5.1 安装Ant Design

执行:

csharp 复制代码
yarn add antd

然后修改src/renderer/App.jsx 来验证下Antd:

javascript 复制代码
import { Button } from 'antd'

function App() {
    return (
        <div className="App">
            <h1>Electron-Vite-React-App</h1>
            <Button type="primary">Button</Button>
        </div>
    )
}

export default App

执行yarn dev:

可以看到Antd的Button组件正常显示出来了。

※注: Antd 5.x已经没有全局污染的reset样式了。因此不用再担心使用了Antd会影响页面样式。

5.2 设置Antd为中文语言

Antd默认语言是英文,需进行以下设置调整为中文。

修改src/renderer/main.jsx:

javascript 复制代码
    import ReactDOM from 'react-dom/client'
    import App from './App'
+   import { ConfigProvider } from 'antd'
+   // 引入Ant Design中文语言包
+   import zhCN from 'antd/locale/zh_CN'
    // 全局样式
    import '@/common/styles/frame.styl'
    
    const root = ReactDOM.createRoot(document.getElementById('root'))
M   root.render(
M       <ConfigProvider locale={zhCN}>
M           <App />
M       </ConfigProvider>
M   )

6 渲染进程页面开发

本教程包含Login、Home两个业务页面,使用react-router-dom实现页面跳转。本教程重点讲解Electron,因此不再深入讲解react-router-dom。

工程文件变动如下:

❤️❤️❤️------试读结束------❤️❤️❤️

后续精彩章节

bash 复制代码
6 渲染进程页面开发
• 6.1 构建Login页面
• 6.2 构建Home页面
• 6.3 实现页面路由跳转
• 6.4 在React组件中实现页面路由跳转
• 6.5 在非React组件中实现页面路由跳转
7 主进程与渲染进程通信方法一:send与on/once
• 7.1 预加载脚本(preload)开发
• 7.2 主进程开发
• 7.3 继续渲染进程开发
• 7.4 运行效果
• 7.5 关于ipcRenderer.on/once
8 主进程与渲染进程通信方法二:invoke与handle
• 8.1 主进程开发
• 8.2 在渲染进程显示Electron版本号
9 常用配置
• 9.1 设置应用图标
• 9.2 设置APP窗口大小
• 9.3 取消跨域限制
• 9.4 设置DevTools快捷键
• 9.5 禁止build环境的DevTools
10 build项目
• 10.1 执行build命令
• 10.2 解决windows版本编译时无法下载的问题
• 10.3 设置build版应用icon
• 10.4 其他应用信息配置
• 10.5 解决macOS build版本的Coding Siging错误
• 10.6 build后的目录结构
11 其他说明
• 11.1 源代码保护
• 11.2 敏感字符串保护
• 11.3 主进程热更新
• 11.4 禁止同个Electron程序多开
• 11.5 允许windows用户更改安装目录
• 11.6 批量升级全部项目npm依赖包
12 项目Git源码
结束语

阅读完整版

📖 完整教程可订阅我的公众号【卧梅又闻花】

《2023金秋版:基于electron-vite构建React桌面客户端》

项目Git源码

本项目已上传至Gitee和GitHub,方便各位下载。

Gitee:

gitee.com/betaq/elect...
GitHub:

github.com/Yuezi32/ele...

相关推荐
刘杭8 小时前
在react项目中使用Umi:dva源码简析之redux-saga的封装
前端·javascript·react.js
会蹦的鱼11 小时前
React学习day07-ReactRouter-抽象路由模块、路由导航、路由导航传参、嵌套路由、默认二级路由的设置、两种路由模式
javascript·学习·react.js
真的很上进18 小时前
【Git必看系列】—— Git巨好用的神器之git stash篇
java·前端·javascript·数据结构·git·react.js
大表哥61 天前
在react中 使用redux
前端·react.js·前端框架
因为奋斗超太帅啦1 天前
React学习笔记(三)——React 组件通讯
笔记·学习·react.js
西瓜本瓜@1 天前
React + React Image支持图像的各种转换,如圆形、模糊等效果吗?
前端·react.js·前端框架
黄毛火烧雪下1 天前
React 的 useEffect 钩子,执行一些异步操作来加载基本信息
前端·chrome·react.js
蓝莓味柯基1 天前
React——点击事件函数调用问题
前端·javascript·react.js
资深前端之路1 天前
react jsx
前端·react.js·前端框架
白鹭凡1 天前
react 甘特图之旅
前端·react.js·甘特图