搭建 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,如果以上有些许细节没有表达出来,可以参考此源码。