运用Electron+Vue3.2+TypeScript+Vite开发桌面端

语言: CN / TW / HK

Electron可以让程序员重用Web的代码,使用HTML、CSS、JavaScript来构建桌面应用,并在不同平台上使用。

Electron官网上说:“比你想象的更简单”————“如果你可以建一个网站,你就可以建一个桌面应用程序。 Electron 是一个使用 JavaScript, HTML 和 CSS 等 Web 技术创建原生程序的框架,它负责比较难搞的部分,你只需把精力放在你的应用的核心上即可。”

Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。目前 Vue 已经成为继 Rect 之后最流行的前端开发框架。

我找到了一个插件:vite-plugin-electron,可以很方便的帮我们把 electron 和 vue 结合起来,开发起来非常方便。

目录结构设计

因为我们需要使用 Electron 和 vue 进行开发,因此我们把它们分开目录存储,基础目录结构如下:

  • electron-main: 主进程目录。
  • electron-preload: 预加载代码目录,主要是定义桥接通信。
  • 其他文件: 也就是 vue 初始化后的目录。

目录结构这么设计的原因是因为我们使用的 vite-plugin-electron 插件需要使用这样的目录结构,目前还没有提供设置修改。

初始化项目目录

下面就开始初始项目:

yarn create vite electron-desktop --template vue-ts

先使用 vite 创建 vue 项目,然后我们再将 electron 嵌入到里面。

初始化完成后,我们先做一个本地 yarn 源的配置,防止下载库的时候出现异常。

配置.yarnrc

registry "https://registry.npm.taobao.org/"
electron_mirror "https://npm.taobao.org/mirrors/electron/"
electron_builder_binaries_mirror "http://npm.taobao.org/mirrors/electron-builder-binaries/"

配置完下载源后,就可以安装 electron 了。

安装Electron

yarn add -D electron electron-builder rimraf vite-plugin-electron electron-devtools-installer
  • electron-builder: 打包工具。
  • rimraf: 快速删除文件或目录工具。
  • vite-plugin-electron: vite 结合 electron 的库,关于这个插件可以参见 Vite 与 Electron 无缝衔接。
  • electron-devtools-installer: electron 开发工具。

vite-plugin-electron 插件是将 vite 和 electron 结合在一起的,可以让我们非常方便的结合 electron 和 vue,需要做一些指定的配置。

初始化electron项目

可以参考 electron 官网的快速开始项目。

  • 创建主进程目录和文件。
// electron-main/index.ts
import { app, BrowserWindow } from 'electron';
import path from 'path';
const createWindow = () => {
  const win = new BrowserWindow({
    webPreferences: {
      contextIsolation: false,
      nodeIntegration: true,
      preload: path.join(__dirname, '../electron-preload/index.js'),
    },
  });
  if (app.isPackaged) {
    win.loadFile(path.join(__dirname, '../index.html'));
  } else {
    //  Use ['ENV_NAME'] avoid vite:define plugin
    const url = `http://${process.env['VITE_DEV_SERVER_HOST']}:${process.env['VITE_DEV_SERVER_PORT']}`;

    win.loadURL(url);
  }
};
app.whenReady().then(() => {
  createWindow();
  app.on('activate', () => {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

需要注意引入的预加载文件应该是打包后的 js 文件,路径和 ts 文件路径相同,只要类型改为 js 即可。

  • 创建预加载目录和文件。

在预加载文件中我们打印一下系统平台。

// electron-preload/index.ts
import os from 'os';
console.log(os.platform());

配置vite-electron

tsconfig.json

在 tsconfig.json 中监听 electron 相关文件和提示。

"include": [..., "electron-main/**/*.ts", "electron-preload/**/*.ts"],

vite.config.ts配置

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import * as path from 'path';
import electron from 'vite-plugin-electron';
import electronRenderer from 'vite-plugin-electron/renderer';
import polyfillExports from 'vite-plugin-electron/polyfill-exports';
export default defineConfig({
  plugins: [
    vue(),
    electron({
      main: {
        entry: 'electron-main/index.ts',
      },
      preload: {
        // Must be use absolute path, this is the limit of rollup
        input: path.join(__dirname, './electron-preload/index.ts'),
      },
    }),
    electronRenderer(),
    polyfillExports(),
  ],
  build: {
    emptyOutDir: false, // 必须配置,否则electron相关文件将不会生成build后的文件
  },
});

配置主进程和预加载脚本地址。

package.json配置

{
  "name": "electron-desktop",
  "private": true,
  "version": "1.0.0",
  "main": "dist/electron-main/index.js",
  "scripts": {
    "dev": "vite",
    "build": "rimraf dist && vite build && electron-builder"
  },
  "dependencies": {
    "vue": "^3.2.25"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^2.3.1",
    "electron": "^18.2.0",
    "electron-builder": "^23.0.3",
    "electron-devtools-installer": "^3.2.0",
    "rimraf": "^3.0.2",
    "typescript": "^4.5.4",
    "vite": "^2.9.5",
    "vite-plugin-electron": "^0.4.3",
    "vue-tsc": "^0.34.7"
  }
}

主要是增加入口文件,因为 electron 还没有原生支持 ts,因此目前还是必须加载 js 文件,所以入口文件我们配置为解析后的 js 文件路径:

dist/electron-main/index.js,然后修改执行脚本,在 build 命令中增加 electron-builder 打包命令。

electron-builder打包配置

# package.json
{
    ......,
    "build": {
        "appId": "com.electron.desktop",
        "productName": "ElectronVueVite",
        "asar": true,
        "copyright": "Copyright © 2022 XingXingZaiXian",
        "directories": {
          "output": "release/${version}"
        },
        "files": [
          "dist"
        ],
        "mac": {
          "artifactName": "${productName}_${version}.${ext}",
          "target": [
            "dmg"
          ]
        },
        "win": {
          "target": [
            {
              "target": "nsis",
              "arch": [
                "x64"
              ]
            }
          ],
          "artifactName": "${productName}_${version}.${ext}"
        },
        "nsis": {
          "oneClick": false,
          "perMachine": false,
          "allowToChangeInstallationDirectory": true,
          "deleteAppDataOnUninstall": false
        }
      }
  }

electron-builder 的配置我们有一篇专门的文章介绍,这里并没有什么特殊的配置,按照需求配置即可。

到这里就配置好了所有的文件,接下来我们执行开发命令看一看效果。

yarn run dev

然后我们执行打包命令看一看效果。

yarn run build

执行完后会生成两个目录:dist 和 release。

dist 目录中生成的是前端打包文件,release 中生成的是 electron 打包文件,内容如下:

其中 win-uppacked 中生成的是无需安装的执行文件,将此目录直接压缩后就可以发送给别人,解压即可使用。ExectronVueVite_1.0.0.exe 文件是安装包,打开会显示安装过程,执行完安装过程后在系统的控制面板中的软件列表中可以看到该软件,也可以执行卸载。

打开后就是正常的软件界面。

我们创建好了项目结构,那么在使用 Vue 开发 Electron 桌面应用的时候还有一个比较重要的知识点要了解,就是消息通信。在新版本的 electron 中,推荐使用上下文隔离方式进行内部进程通信,electron 官网有很详细的介绍和示例,这里我们只介绍一种方式,其他的方式大家可以通过查看官网示例来了解。

我们的示例是在 Vue 界面中显示当前系统平台。

注册上下文隔离接口

在 electron-preload/index.ts 中添加如下代码:

import os from 'os';
import { contextBridge } from 'electron';
contextBridge.exposeInMainWorld('electronAPI', {
  platform: os.platform(),
});

编写上下文隔离接口的typescript类型声明

通过 electron 注册的上下文隔离接口会添加给 window 对象,但是原始的 window 对象并不存在这些接口和属性,ts 就会报错,这时就需要我们为其编写ts类型声明文件.d.ts。

// src/types/global.d.ts
export interface IElectronAPI {
  platform: string;
}
declare global {
  interface Window {
    electronAPI: IElectronAPI;
  }
}

在Vue中调用接口

我们在 App.vue 中调用window.electronAPI.platform 接口,把系统平台信息显示在界面上。

// src/App.vue
<script setup lang="ts">
// This starter template is using Vue 3 <script setup> SFCs
// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup
import HelloWorld from './components/HelloWorld.vue';
const platform = window.electronAPI.platform;
</script>
<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld :msg="`Hello Vue 3 + TypeScript + Vite in ${platform}`" />
</template>

执行完以上步骤后,运行项目看一下效果。