如何使用 electron-forge 搭建 React + Ts 的项目
作者:深坑妙脆角
前言
如果你有桌面应用开发的需求,且比较熟悉 Web 相关的技术,那么 Electron 会是一个非常不错的选择,作为一个使用 Web 技术构建跨平台桌面应用的框架,其允许开发者使用JavaScript、HTML 和 CSS 等 Web 技术来构建桌面应用;同时,Electron 拥有一个活跃的社区和丰富的生态系统,这不仅意味着开发者可以轻松找到解决问题的资源和工具,还意味着框架的更新和改进是持续不断的,能够紧跟技术发展的步伐
本文将介绍如何用其构建一个应用 Demo 项目
介绍
创建 Electron 应用程序项目的方式有很多,如手动集成、electron-vite 快速构建等,本文将介绍通过 electron-forge 构建的方式
主要环境
electron-forge v7.6webpackreacttypescript
Electron Forge 介绍
Electron Forge 是一个用于打包和分发 Electron 应用程序的一体化工具,可以认为是 Electron Builder 的替代品,它提供了一个全面的解决方案,包括初始化项目、本地开发、打包和发布,同时,其简化了 Electron 应用的开发和打包流程,使得开发者可以更专注于应用本身的开发
具体步骤
项目构建步骤主要如下,如果你需要源码,可从文末获取:
创建 electron 项目
在开始前,需先备有对应的工具,如:npm或yarn

本例使用的是npm,使用yarn的命令也差不多
通过模板创建项目
模板集成了一些项目构建工具,可以更为方便的为项目加入插件,主要提供了如下模板:
webpackwebpack-typescriptvitevite-typescript
创建命令
选择合适的模板后,按照下列命令创建项目
npm init electron-app@latest [project-name] -- --template=[template-name]
[project-name]:表示项目名[template-name]:表示模板名,在上侧提供的里面选择
开始创建
本例使用命令如下:
npm init electron-app@latest demo -- --template=webpack-typescript
该模板集成了webpack和typescript的能力
如果你选用vite相关模板,需注意:

创建好项目后的package.json依赖内容如下:
"devDependencies": {
  "@electron-forge/cli": "^7.6.0",
  "@electron-forge/maker-deb": "^7.6.0",
  "@electron-forge/maker-rpm": "^7.6.0",
  "@electron-forge/maker-squirrel": "^7.6.0",
  "@electron-forge/maker-zip": "^7.6.0",
  "@electron-forge/plugin-auto-unpack-natives": "^7.6.0",
  "@electron-forge/plugin-fuses": "^7.6.0",
  "@electron-forge/plugin-webpack": "^7.6.0",
  "@electron/fuses": "^1.8.0",
  "@typescript-eslint/eslint-plugin": "^5.0.0",
  "@typescript-eslint/parser": "^5.0.0",
  "@vercel/webpack-asset-relocator-loader": "1.7.3",
  "css-loader": "^6.0.0",
  "electron": "33.2.1",
  "eslint": "^8.0.1",
  "eslint-plugin-import": "^2.25.0",
  "fork-ts-checker-webpack-plugin": "^7.2.13",
  "node-loader": "^2.0.0",
  "style-loader": "^3.0.0",
  "ts-loader": "^9.2.2",
  "ts-node": "^10.0.0",
  "typescript": "~4.5.4"
},
"dependencies": {
  "electron-squirrel-startup": "^1.0.1"
}如果命令一直创建失败,可能是网络问题,可考虑使用一些镜像站
试运行
创建好项目后,可以进入项目根目录,用执行如下命令,尝试是否可以正常运行:
# 运行npm start
除此运行可能会报一些类型错误,大致如下:
Failed to load: ...\...\ele-react-demo\forge.config.ts An unhandled rejection has occurred inside Forge: TSError: 1. ⨯ Unable to compile TypeScript: forge.config.ts:1:34 - error TS2307: Cannot find module '@electron-forge/shared-types' or its corresponding type declarations.
按照错误提示添加上相关依赖即可,可能需要添加的依赖如下
"devDependencies": {
    "@electron-forge/shared-types": "^7.6.0",
    "@electron-forge/maker-base": "^7.6.0",
    "@electron-forge/plugin-base": "^7.6.0",
    "webpack": "^5.97.0",
    "@types/node": "^22.10.1"
}添加完缺少的依赖后,再次运行npm start启动项目,如运行成功,可看到如下界面

可以看到,默认会启动开发者工具,这是由于src/index.ts内的代码设置的,如下:

如不需要,可将其注释或删除,需要打开开发者工具时通过快捷键 Ctrl + Shift + I (windows 系统)打开即可
试打包
当编写好项目后,需进行打包,可运行如下命令:
# 打包 npm run make
打包好后,项目根目录会出现一个out目录,内部即是打包好的文件;打包会检测本机系统,然后生成对应的打包文件
正常打包好后,内部会有两个目录,一个为make目录,里面有对应系统的二进制安装程序,如exe;另一个目录(如**-win32-x64),里面为对应系统的便携式免安装程序,点击产品名.exe即可打开应用,此目录内有个resource文件夹,装着项目编译的一些内容,如app.asar
asar 解包方法
asar文件类似一种压缩包,通过对应的工具可以对其进行解包,得到实际的文件
- 安装解包工具:
npm install -g asar - 运行命令解包:
asar extract app.asar ./app(该命令会将 app.asar 解包到当前目录的 app 目录下) 
解包后,目录内会有如下结构:
.webpack # 项目编译部分 ├- main # electron 部分 └- renderer # web 部分 node_modules # 项目依赖 package.json
如果你是用
vite模板构建项目,文件名称可能稍有差别,但结构大致类似
集成 React
集成 React 可以让 UI 开发变得更为方便
安装依赖
首先安装对应依赖,如下:
# 安装 React 包 npm install --save react react-dom # 安装 React 类型支持相关包 npm install --save-dev @types/react @types/react-dom
修改 tsconfig
修改项目根目录下的tsconfig.json,在 compilerOptions 下添加如下内容:
"compilerOptions": {
  "jsx": "react-jsx"
}修改项目文件
在项目内使用React组件的能力,首先在src目录下新建文件app.tsx,内容如下:
/** src/app.tsx */
import { createRoot } from 'react-dom/client';
import { useState } from 'react';
// 主应用
function App() {
  const [count, setCount] = useState(0);
  return (
    <>
      <h1>Hello React</h1>
      <p>count: {count}</p>
      <button onClick={() => setCount(count + 1)}>count + 1</button>
    </>
  );
}
const root = createRoot(document.body);
root.render(<App />);接着,在src/renderer.ts内导入它

然后,运行npm start启动项目,正常启动后如下

正常情况下,无需额外配置,项目即可实现热重载,比如修改了
app.tsx内容后保存,无需重启项目,修改内容即可渲染出来
集成 SCSS
如果你需要使用一些 CSS预编译器,可参考一下步骤,这里以scss举例:
安装依赖
需安装sass相关依赖,如下:
npm install --save-dev sass sass-loader
配置 loader
修改项目根目录下的 webpack.renderer.config.ts 文件,如下:
// webpack.renderer.config.ts
// ··· ···
// 加入对对应样式文件的解析规则
rules.push({
  test: /\.(sass|scss|css)$/,
  use: [{ loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'sass-loader' }]
});
// ··· ···使用 SCSS
重命名src目录下的样式文件,将index.css改为index.scss,然后即可在文件内添加scss样式规则,例如:
/* index.scss */
body {
  h1 {
    color: #68945c;
  }
}导入 SCSS
将index.scss导入src/renderer.ts内部使用

接着,npm start启动项目,查看效果

可以看到,scss的样式已可正常渲染
配置静态资源
如果你在项目内使用了一些静态资源(如图片),如下:

我在src目录下新建了一个assets目录,专门用于放置静态资源,现在里面放了一张图片;然后我在app.tsx内引用了它;接着启动项目,如果没有意外,可以看到图片并不会显示,控制台会报错找不到对应资源,查看编译后的.webpack目录,发现缺少没有对应的图片资源
这是由于静态资源没有被正确加载或复制,以下介绍两种简单可行的方式,仅供参考:
方法一
此方法仅配置打包后的静态资源,具体步骤如下:
配置 forge.config.ts
const config: ForgeConfig = {
  packagerConfig: {
    asar: true,
    // 通过如下配置项,配置需打包的静态资源
    // 该配置会将对应资源复制到打包后的 resource 目录下
    // 这里表示复制 src/assets 整个目录
    // 打包后在 out/**-win32-x64/resource 目录下,会有一个 assets 目录
    extraResource: ['./src/assets']
  }
}使用时可以用相对路径,也可以借助path工具
// src/app.tsx
function App() {
  const [count, setCount] = useState(0);
  return (
    <>
      <h1>Hello React</h1>
      // 通过相对路径
      // 打包后的结构可看上方解答
      // web 部分在 resource/app.asar/.webpack/renderer/main_window
      // 资源部分在 resource/assets
      <img src="../../../../assets/images/cat.jpg" />
      // 也可以使用 path 拼接路径,如:path.join(process.resourcesPath, 'assets/images/cat.jpg')
      // process.resourcesPath 表示 resource 目录
      <p>count: {count}</p>
      <button onClick={() => setCount(count + 1)}>count + 1</button>
    </>
  );
}此时npm run make打包后,运行打包的文件,即可看到静态资源已经可以正常显示了

这种方法只适用于处理打包的静态资源,开发
npm start运行时依旧无法正常显示并且,复制的静态资源存在于 renderer 目录下,并为打包进 app.asar
方法二
通过一些插件实现,如rollup-plugin-copy、copy-webpack-plugin等,这里介绍copy-webpack-plugin的用法
首先安装copy-webpack-plugin插件
npm install --save-dev copy-webpack-plugin
接着配置使用插件,打开根目录下的webpack.plugins.ts文件

以上配置会将./src/assets文件夹内的内容复制到./.webpack/renderer/static文件夹内,在项目中使用时,只需通过./static目录进行访问即可,如下
// src/app.tsx
function App() {
  const [count, setCount] = useState(0);
  return (
    <>
      <h1>Hello React</h1>
      // 使用静态资源
      <img src="./static/images/cat.jpg" />
      <p>count: {count}</p>
      <button onClick={() => setCount(count + 1)}>count + 1</button>
    </>
  );
}此时无论是开发运行npm start,还是打包npm run make运行,都能正确渲染静态资源

这种方法不仅可以正常显示与开发和打包中,同时在打包时,静态资源也会一并打包进 app.asar 内
配置 Loading 页面
以下内容是一些体验优化,参考即可
使用 electron 开发,在启动时,会有一段白屏时间,这主要是由于窗口加载后,页面资源还未加载渲染好造成的,在项目集成了Rreact、Vue后,这个问题会愈发明显,白屏时间会更长
想要解决这个问题,由很多方式可以参考,如:优化资源、做一些预加载工作等
这里主要介绍一种比较简单的解决方式:加入 Loading 页面,主要思路就是在主窗口创建前,先创建一个 Loading 窗口,只用显示简单的 Loading 页面,然后再创建主窗口;窗口创建时默认都隐藏,当创建好后才显示,首先显示 Loading 窗口,待主窗口创建完成并准备好时,隐藏 Loading 窗口,展示主窗口
首先在assets目录下新建一个loading.html,里面存放着一个loading动画样式,效果如下(可自行创意实现):

接着,创建窗口渲染使用它,修改src/index.ts文件
// Loading 窗口
let loadingWin: BrowserWindow;
// Loading 窗口的渲染文档,即上侧的 loading 效果
const loadingUrl = path.join(__dirname, '../renderer/static/loading.html');
/** Loading 窗口 */
const createLoading = () => {
  return new Promise((resolve, reject) => {
    loadingWin = new BrowserWindow({
      // 一开始是否显示
      show: false,
      width: 512,
      height: 512,
      frame: false, // 无边框(窗口、工具栏等),只包含网页内容
      transparent: true // 窗口是否支持透明,在做高级效果时最好为 true
    });
    // 当窗口显示时
    loadingWin.once('show', () => {
      resolve(true);
    });
    // 配置窗口渲染文档
    loadingWin.loadFile(loadingUrl);
    // 显示 Loading 窗口
    loadingWin.show();
  });
};
/** 主窗口 */
const createWindow = () => {
  // 创建渲染窗口
  const mainWindow = new BrowserWindow({
    // 窗口图标
    // icon: './assets/logo/logo.ico',
    height: 600,
    width: 800,
    // 一开始隐藏
    show: false,
    webPreferences: {
      // 预加载脚本
      preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY
    }
  });
  // 主窗口准备还显示
  mainWindow.once('ready-to-show', () => {
    // 隐藏 Loading 窗口
    loadingWin.hide();
    // 关闭 Loading 窗口
    loadingWin.close();
    // 展示 主窗口
    mainWindow.show();
  });
  // 为窗口加载 index.html
  mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
  // 打开开发者工具
  // mainWindow.webContents.openDevTools();
};
// 当 Electron 初始化完成并准备创建渲染窗口(mainWindow)时调用
// 某些 API 只能在此事件发生后使用
// app.on('ready', createWindow);
app.on('ready', async () => {
  // 先创建 Loading 窗口
  await createLoading();
  // 再创建 主窗口
  createWindow();
});加入loading窗口后,白屏时间可以显著改善,程序启动后,会先显示loading窗口,待主窗口完全准备好后再显示主窗口,效果大致如下:
【一张动图,但这放不了】
如果需要,还可以给主窗口页面加入一个淡入的动画效果,交互体验也许会更好
总结
本文介绍了用electron-forge、webpack、typescript、react、scss搭建的一个模板项目,同时还包含静态资源配置及loading效果
如果你由于网络原因npm init创建electron项目一直失败,也可以到github克隆本项目直接修改使用

克隆完成后到项目根目录通过如下命令即可使用
# 安装依赖 pnpm install # 运行 npm start # 打包 npm run make
参考资料
Electron Forge: https://www.electronforge.io/
模板地址: https://github.com/skmcj/demo
到此这篇关于如何使用 electron-forge 搭建 React + Ts 的项目的文章就介绍到这了,更多相关electron-forge 搭建 React + Ts 项目内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
