从零开始的 Electron - 最简化起步![Electron + Vite + TypeScript]

4,338 阅读4分钟

前言

本文主要阐述了,在 Electron + Vite + TypeScript 的组合下,进行最简化配置,以进行桌面应用开发的方法。

本文并非阐述了整合的最佳实践,但尽量指出了关键部分,以方便各位开发者避免部分难以解决的问题。

本文假设你对 Electron、Vite、TypeScript、Node 等技术已经有了一些基本的认识。

如果有更好的配置方式,请在评论区讨论,万分感谢。

问题

在进行 Electron 开发工程中,经常会遇到下载依赖或环境时,网络请求失败的问题。但由于这些问题很容易在搜索引擎中检索到,本文将忽略这些比较常见的问题。

起步

使用 Vite 官方的例子,来创建一个初始化的项目:

npm init vite@latest electron-starter --template vanilla-ts

其中 vanilla-ts 代表原生 Typescript 开发,本文会尽量避免引入其他框架。但如有需要,React、Vue 等框架一般不会影响 Electron 配置。

在完成项目的创建后,删除掉除了 package.json之外的所有文件,既整个项目文件夹下只有一个 package.json 文件。

依赖

首先执行以下命令来初始化项目:

npm install

执行以下命令安装 Electron 开发依赖:

npm install electron --save-dev

执行以下命令安装 electron-builder 打包程序:

npm install electron-builder --save-dev

渲染进程 Typescript 配置

创建文件 src/view/tsconfig.json

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "lib": ["ESNext", "DOM"],
    "moduleResolution": "Node",
    "strict": true,
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    // noEmit: 重要的配置,它决定了是否编译输出 .js 文件,
    // 这里被 Vite 接管了,所以设为 true (不输出)
    "noEmit": true, 
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true
  },
  // include: 仅编译当前目录下的 Typescript 文件
  "include": ["./"]
}

主进程 Typescript 配置:

创建文件 src/main/tsconfig.json

{
  "compilerOptions": {
    "target": "ESNext",
    // module: 运行于 Node 的主进程必须遵循 CommonJS 标准
    "module": "CommonJS", 
    "lib": ["ESNext", "DOM"],
    "moduleResolution": "Node",
    "strict": true,
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    // noEmit: 重要的配置,这里不被 Vite 接管,必须输出成 .js 文件进行打包
    "noEmit": false,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    // outDir: 指定 .js 文件输出位置
    "outDir": "../../dist/main"
  },
  "include": ["./"]
}

渲染进程程序

创建文件 src/view/index.html

<!DOCTYPE html>
<html>
  <body>
    <div id="greet"></div>
    <script type="module" src="./index.ts"></script>
  </body>
</html>

创建文件 src/view/index.ts

// 通过这段代码在页面上显示简单的文字
document.getElementById('greet')!.innerText = 'hello, world'

package.json 中添加如下指令来测试当前成果:

{
  "scripts": {
    "preview": "vite"
  }
}

顺利的话,打开浏览器可以看到预设文字。

主进程程序

创建文件 src/main/index.ts

import { app, BrowserWindow } from 'electron'

app.whenReady().then(() => {
  // 这里是关键,编译后的 dist 目录被视作资源文件,
  // 而 dist/main/index.js 作为入口文件,不可以通过打包前的相对路径来访问资源文件
  // 如果这里填写了 ../view/index.html, Electron 将会找不到这个文件
  new BrowserWindow().loadFile('./dist/view/index.html')
})

Vite 配置

根目录创建文件 vite.config.ts

import { defineConfig } from 'vite'

export default defineConfig({
  root: './src/view', // 只打包渲染进程文件
  base: './', // 这里很关键,配置 Electron 通过相对路径来访问资源文件
  build: {
    // 渲染进程打包后的路径,嗯……这里由于修改了 root ,所以需要补充一下相对路径
    outDir: '../../dist/view', 
  },
})

打包配置

找一张 256 × 256 以上大小的正方形图片,用于应用 Logo,放到 src/asset 下。这里建议可以去 iconfont 随便下载一张。

修改 package.json 文件:

{
  // electron-builder 必须 name 和 version 属性
  "name": "electron-starter",
  "version": "0.0.1",
  // Electron 入口文件
  "main": "./dist/main/index.js",
  "scripts": {
    "preview": "vite",
    // 整体打包指令,其中:
    // tsc -p ./src/view - 编译渲染进程 TypeScript
    // vite build - 渲染进程打包
    // tsc -p ./src/main - 编译主进程 TypeScript
    // electron-builder - Electron 打包
    "build": "tsc -p ./src/view && vite build && tsc -p ./src/main && electron-builder"
  },
  "devDependencies": {
    "electron": "^13.1.7",
    "electron-builder": "^22.11.7",
    "typescript": "^4.3.2",
    "vite": "^2.4.3"
  },
  "build": {
    "appId": "cloud.chenh.elec", // 应用 ID
    "productName": "ElecStarter", // 打包后的应用名
    "win": { // windows 平台
      "icon": "./src/asset/logo.png", // 上文所述的 logo
      "target": "nsis"
    },
    "files": [
      "dist" // dist 作为资源文件目录
    ],
    "directories": {
      "output": "app" // 打包输出文件夹
    }
  }
}

打包

在进行上述配置之后,执行以下命令进行打包:

npm run build

app 目录下检阅成果吧!

总结

考虑以下流程:

正常的前端打包 -> 编译 Electron TypeScript -> 打包编译完成的文件至 Electron 应用程序

重点在于第二步,如果没有 TypeScript,那么一切都会好过许多。由于项目引入了 TypeScript,而 Electron 入口文件又必须是个 CommonJS,所以需要单独编译主进程相关的代码,然后再对 dist 打包目录进行二次打包至 app 目录,可以说是相当麻烦了。

Electron 打包最主要解决的还是资源文件的调用问题,其他方面倒是和普通前端开发一致。

由于本人作为一个野路子前端,对各种技术都不太深入地了解,所以如果您有更好的建议,请在评论区指出,再次万分感谢。

代码

本文所述代码已上传至 GitHub