Skip to content

搭建 Vite + Vue3 + TypeScript + Electron 项目

  • 源码参考:mine-desktop,如果以下有些许细节没有表达出来,可以参考此源码。

方法一、创建一个 Vite 项目

sh
npm init vite@latest

方法二、从零开始

初始化 package.json

json
npm init -y

安装依赖

安装 vue 依赖

sh
npm i vue vue-router
  • 安装构建工具
  • 这里使用 Less 作为 css 预编译器
sh
npm i -D @vitejs/plugin-vue vite less

安装 TS 依赖

sh
npm i -D typescript vue-tsc @types/node

安装 Electron 依赖

  • 这里使用 electron-builder 作为打包工具
sh
npm i -D electron electron-builder

安装 Eslint (可选)

sh
npm i -D @typescript-eslint/eslint-plugin @typescript-eslint/parser @vitejs/plugin-vue eslint eslint-config-airbnb-base eslint-config-prettier eslint-plugin-import eslint-plugin-prettier eslint-plugin-vue prettier

初始化文件

点击查看 vite.config.ts
ts
import { defineConfig, UserConfig } from 'vite'
import { resolve } from 'path'
import vue from '@vitejs/plugin-vue'

export const config: UserConfig = {
  base: './',
  plugins: [vue()],
  resolve: {
    // 路径别名
    alias: {
      '@': resolve(__dirname, './src')
    }
  },
  css: {
    // 配置预编译器
    preprocessorOptions: {
      less: {
        javascriptEnabled: true,
        additionalData: `@import '@/styles/variables.less';`
      }
    }
  },
  build: {
    outDir: './dist/resources/vue',
    sourcemap: false
  }
}

export default defineConfig(config)
点击查看 tsconfig.json
json
{
  "compilerOptions": {
    "incremental": false,
    "composite": false,
    "target": "esnext",
    "useDefineForClassFields": true,
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "lib": ["esnext", "dom"],
    "types": ["vite/client", "node"],
    "skipLibCheck": true,
    "baseUrl": "./",
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "vite.config.ts", "app/**/*.ts"]
}
点击查看代码
md
|-- electron-demo --------------------- 项目名称
|-- index.html -------------------- HTML 页面
|-- tsconfig.json ----------------- TS 配置
|-- vite.config.ts ---------------- Vite 配置
|-- app --------------------------- Electron 主目录
| |-- vite.config.ts ------------ 关于 Electron 的 Vite 基础配置
| |-- assets -------------------- 资源目录
| | |-- 256x256.ico ----------- 应用图标
| | |-- 256x256.png ----------- 应用图标
| |-- main ---------------------- 主程序根目录
| | |-- vite.config.ts -------- 主程序的 Vite 配置
| | |-- src ------------------- 主程序目录
| | |-- main.ts ----------- 主程序入口
| |-- preload ------------------- Electron 预加载目录
| |-- vite.config.ts -------- 预加载的 Vite 配置
| |-- src ------------------- 预加载程序目录
| |-- bridge.ts --------- bridge 模块
| |-- index.ts ---------- 预加载程序入口
|-- public ------------------------ 静态资源
| |-- favicon.ico --------------- favicon
|-- src --------------------------- vue 项目
|-- App.vue ------------------- 页面入口
|-- env.d.ts ------------------ TS 模板声明
|-- main.ts ------------------- 入口
|-- assets -------------------- 资源目录
|-- components ---------------- 公共组件
|-- router -------------------- 路由
|-- styles -------------------- 样式
|-- views --------------------- 页面
点击查看 main.ts
ts
import { createApp } from 'vue'
import Antd from 'ant-design-vue'
import router from './router'
import App from './App.vue'

const app = createApp(App)

app.use(router)
app.mount('#app')

思路

Vue 项目

  • 保持原有目录不变,在此基础上加入 Electron 配置。
  • 此软件的界面都使用 Vue 页面,和 Electron 没有任何关系,Electron 只做代码交互。

Electron 项目

  • 支持 TS:主要通过 vite 来打包 Electron 程序为 lib,从而达到实现开发为 TS 的目的。

Vue 和 Electron 通信

Electron 定义

点击查看 app/main/src/main.ts
ts
import { app, BrowserWindow, BrowserWindowConstructorOptions, ipcMain } from 'electron'
import { resolve } from 'path'

// 初始化程序

/**
 * 创建窗口
 */
const createWindow = () => {
  const browserWindowOption: BrowserWindowConstructorOptions = {
    titleBarStyle: 'hidden',
    width: 1000,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      enableWebSQL: false,
      preload: resolve(__dirname, '../preload/index.cjs') // 预加载程序,此为 `preload/src/index.ts` 使用 Vite 打包之后的 lib 文件
    }
  }
  const window = new BrowserWindow(browserWindowOption)

  window.setMenuBarVisibility(false)

  let pageUrl: string // 页面路径
  // 开发
  if (import.meta.env.MODE === 'development') {
    pageUrl = 'http://localhost:3400'
    window.webContents.openDevTools()
  } else {
    pageUrl = new URL('../../resources/vue/index.html', `file://${__dirname}`).toString() // 打包
  }
  window.loadURL(pageUrl)
  // 定义通信事件, quit 为自定义名称
  ipcMain.handle('quit', () => {
    app.quit()
  })
}

app.whenReady().then(() => {
  createWindow()
  app.on('activate', function () {
    !BrowserWindow.getAllWindows().length && createWindow()
  })
})

app.on('window-all-closed', () => {
  process.platform !== 'darwin' && app.quit()
})
  • 定义通信事件
ts
// 定义通信事件, quit 为自定义名称,注意在 Vue 里使用的时候保持一致
ipcMain.handle('quit', () => {
  app.quit()
})
点击查看 app/preload/src/bridge.ts
ts
import { contextBridge, ipcRenderer } from 'electron'
// electron 是为 Vue 注入全局变量 electron, ipcRenderer 为注入的模块
contextBridge.exposeInMainWorld('electron', {
  ipcRenderer
})

Vue 使用

vue
<template>
  <div @click="onQuit">关闭软件</div>
</template>
<script lang="ts" setup>
const onQuit = () => {
  // electron 属性为 Electron 注入的全局变量
  // quit 为 Electron 定义的名称
  globalThis.electron.ipcRenderer.invoke('quit')
}
</script>

Electron 打包配置

点击查看 electron-builder.json 或 package.json 的 build
json
{
  "appId": "mine.desktop.app",
  "electronVersion": "19.0.4",
  "copyright": "Copyright (c) 2022-present biaov",
  "asar": true,
  "directories": {
    "output": "./dist/package"
  },
  "win": {
    "icon": "./app/assets/256x256.ico",
    "requestedExecutionLevel": "highestAvailable",
    "target": [
      {
        "target": "nsis",
        "arch": ["x64"]
      }
    ]
  },
  "nsis": {
    "allowElevation": true,
    "allowToChangeInstallationDirectory": true,
    "artifactName": "mine-desktop.${ext}",
    "createDesktopShortcut": true,
    "createStartMenuShortcut": true,
    "installerIcon": "./app/assets/256x256.ico",
    "uninstallerIcon": "./app/assets/256x256.ico",
    "installerHeaderIcon": "./app/assets/256x256.ico",
    "uninstallDisplayName": "uninstall",
    "oneClick": false,
    "shortcutName": "mine-desktop",
    "useZip": true,
    "deleteAppDataOnUninstall": false,
    "displayLanguageSelector": false,
    "perMachine": true
  }
}
json
{
  "name": "electron-demo",
  "version": "1.0.0",
  "scripts": {},
  "build": {
    "appId": "mine.desktop.app",
    ...同上
  }
}

总结

  • 总体上来说,如果你不用 Electron 自带的界面,那么除了交互,其它的和 Vue 项目没有区别。
  • 源码参考:mine-desktop,如果以上有些许细节没有表达出来,可以参考此源码。

基于 MIT 许可发布