Electron搭建桌面端应用

前言

本文档主要介绍如何使用electron+vite+vue3+electron-forge来构建桌面应用程序。

Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。

Electron-forge是一个处理 Electron 应用程序打包与分发的一体化工具。

环境准备

  • 在开始之前,确保你的开发环境已经安装了Node和npm。

node: v16.18.1

npm: 8.19.2

安装 Vite 和 Vue 3

  • 创建并进入项目目录,安装 Vite 和 Vue 3

mkdir 你的项目

cd 你的项目

npm install vite vue@next

配置Vite

  • 在项目根目录下会有一个vite.config.js文件,进行如下配置

修改vue的base属性为./ 相对路径

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  base: './',  //修改vue的base属性为./ 相对路径
  plugins: [
    vue(),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
  server: {
    host: '0.0.0.0',  // 启动后浏览器窗口输入地址就可以进行访问
    port: 8081, // 端口号
    open: true, //是否自动打开浏览器
    proxy: { // 反向代理配置
    }
  },
})

项目引入Electron

  • 安装electron

# 安装Electron

npm install --save-dev electron
  • 在项目根目录下创建electron的目录,里面创建main.jspreload.js的文件

 创建项目启动主窗口,加载vue的页面

const { app, BrowserWindow, ipcMain, shell, dialog } = require('electron')
const path = require('node:path')
const createWindow = () => {
    const win = new BrowserWindow({
        width: 1100,
        height: 800,
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false,
            preload: path.join(__dirname, './preload.js')
        },
        autoHideMenuBar: true, // 是否自动隐藏菜单栏
        icon: "icon/logo.png"
    })

    // 应用标题
    win.setTitle('xxxx')

    // 加载 index.html路径
    win.loadFile('dist/index.html')

    // win.loadURL('xxxx')
}

// 这段程序将会在 Electron 结束初始化
// 和创建浏览器窗口的时候调用
// 部分 API 在 ready 事件触发后才能使用。
app.whenReady().then(() => {
    ipcMain.handle('ping', () => 'pong')
    createWindow()

    app.on('activate', () => {
        // 在 macOS 系统内, 如果没有已开启的应用窗口
        // 点击托盘图标时通常会重新创建一个新窗口
        if (BrowserWindow.getAllWindows().length === 0) {
            createWindow()
        }
    })
})

// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此, 通常
// 对应用程序和它们的菜单栏来说应该时刻保持激活状态,
// 直到用户使用 Cmd + Q 明确退出
app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit()
    }
})
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('versions', {
    node: () => process.versions.node,
    chrome: () => process.versions.chrome,
    electron: () => process.versions.electron,
    ping: () => ipcRenderer.invoke('ping')
    // 除函数之外,我们也可以暴露变量
})
  • 修改package.json文件内容

配置electron启动文件,删除package.json文件中的 type:“module” 行,否则会有一个警告!

{
  "name": "xxx",
  "version": "xxx",
  "description": "xxx",
  // "type": "module",
  "main": "electron/main.js",
  "author": "xxx",
  "license": "xxx",
  "scripts": {
    "dev": "vite",
    "electron":"electron .",
  },
}

Electron-forge配置

  • 安装electron-forge

# electron-forge依赖安装

npm install --save-dev @electron-forge/cli
  • 运行electron-forge import

# electron-forge自动构建运行脚本

npx electron-forge import
  • 转换脚本完成后,Forge 会将一些脚本添加到您的package.json文件中。

  //...
  "scripts": {
    "dev": "vite",
    "electron":"electron .",
    "start": "electron-forge start",
    "package": "electron-forge package",
    "make": "electron-forge make"
  },
  //...
  • 您还应该注意到您的package.json现在安装了更多的包 在devDependencies下,以及一个导出配置的新forge.config.js文件。

"devDependencies": {
      "@electron-forge/cli": "^7.4.0",
      "@electron-forge/maker-deb": "^7.4.0",
      "@electron-forge/maker-rpm": "^7.4.0",
      "@electron-forge/maker-squirrel": "^7.4.0",
      "@electron-forge/maker-zip": "^7.4.0",
      "@electron-forge/plugin-auto-unpack-natives": "^7.4.0",
      "@electron-forge/plugin-fuses": "^7.4.0",
      "@electron/fuses": "^1.8.0",
      "electron": "^30.1.0",
    },
  • 接下来让我们配置一下forge.config.js文件。

module.exports = {
  packagerConfig: {
    asar: true,
  },
  rebuildConfig: {},
  makers: [
    {
      name: '@electron-forge/maker-squirrel',
      config: {},
    },
    {
      name: '@electron-forge/maker-zip',
      platforms: ['darwin'],
    },
    {
      name: '@electron-forge/maker-deb',
      config: {},
    },
    {
      name: '@electron-forge/maker-rpm',
      config: {},
    },
  ],
};

完整代码展示

  • package.json

{
  "name": "xxx", // 项目的名称,也是打包之后的程序名称
  "version": "1.0.1", // 版本信息
  "description": "xxx", // 描述信息,必填
  "main": "electron/main.js",
  "author": "xxx", // 作者信息,必填
  "license": "MIT",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "start": "electron-forge start", // 启动electron的脚本
    "package": "electron-forge package",
    "make": "electron-forge make" // 打包electron的脚本
  },
  "dependencies": {
    "@vueuse/core": "^10.10.0",
    "axios": "^1.6.8",
    "chinese-lunar": "^0.1.4",
    "chinese-lunar-calendar": "^1.0.1",
    "dayjs": "^1.11.11",
    "electron-squirrel-startup": "^1.0.1",
    "element-plus": "^2.7.2",
    "js-cookie": "^3.0.5",
    "jsencrypt": "^3.3.2",
    "postcss-px2rem": "^0.3.0",
    "vue": "^3.4.21",
    "vue-router": "^4.3.2"
  },
  "devDependencies": {
    "@electron-forge/cli": "^7.4.0",
    "@electron-forge/maker-deb": "^7.4.0",
    "@electron-forge/maker-rpm": "^7.4.0",
    "@electron-forge/maker-squirrel": "^7.4.0",
    "@electron-forge/maker-zip": "^7.4.0",
    "@electron-forge/plugin-auto-unpack-natives": "^7.4.0",
    "@electron-forge/plugin-fuses": "^7.4.0",
    "@electron/fuses": "^1.8.0",
    "@vitejs/plugin-vue": "^5.0.4",
    "concurrently": "^8.2.2",
    "electron": "^30.1.0",
    "file-saver": "2.0.5",
    "sass": "^1.77.0",
    "vite": "^5.2.8"
  },
  "config": {
    "forge": {
      "packagerConfig": {
        "appVersion": "1.0.1",
        "icon": "./icon/icon.ico",
        "name": "xxx" 
      },
      "makers": [
        {
          "name": "@electron-forge/maker-squirrel",
          "config": {
            "name": "xxx",
            "icon": "./icon/icon.ico"
          }
        },
        {
          "name": "@electron-forge/maker-zip",
          "platforms": [
            "darwin"
          ]
        },
        {
          "name": "@electron-forge/maker-deb",
          "config": {}
        },
        {
          "name": "@electron-forge/maker-rpm",
          "config": {}
        }
      ]
    }
  }
}
  • electron/main.js

const { app, BrowserWindow, ipcMain, shell, dialog } = require('electron')
const path = require('node:path')
const createWindow = () => {
    const win = new BrowserWindow({
        width: 1100,
        height: 800,
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false,
            preload: path.join(__dirname, './preload.js')
        },
        autoHideMenuBar: true, // 是否自动隐藏菜单栏
        icon: "icon/logo.png"
    })

    // 应用标题
    win.setTitle('控势安全')

    // 加载 index.html
    win.loadFile('dist/index.html')

    // win.loadURL('http://192.168.110.224:8081/')

    // 捕获新窗口事件并使用shell打开链接
    ipcMain.on('open-url', (event, url) => {
        event.preventDefault();
        shell.openExternal(url);
    });

    ipcMain.on('open-local-file', (event, folderPath) => {
        shell.openPath(folderPath)
    });

    ipcMain.on('click-notification', (event) => {
        win.focus()
    });

    // 监听渲染进程发送的 'select-folder' 消息
    ipcMain.on('select-folder', (event) => {
        dialog.showOpenDialog(win, {
            properties: ['openDirectory']
        }).then(result => {
            if (result.canceled) {
                console.log('用户取消了选择');
                event.reply('select-folder-reply', null); // 发送取消选择的消息
            } else {
                const folderPath = result.filePaths[0]; // 获取用户选择的第一个文件夹路径
                console.log('用户选择的文件夹路径:', folderPath);
                event.reply('select-folder-reply', folderPath); // 发送选择的文件夹路径给渲染进程
            }
        }).catch(err => {
            console.error('选择文件夹时出错:', err);
            event.reply('select-folder-reply', null); // 发送错误消息
        });
    });
}

// 这段程序将会在 Electron 结束初始化
// 和创建浏览器窗口的时候调用
// 部分 API 在 ready 事件触发后才能使用。
app.whenReady().then(() => {
    ipcMain.handle('ping', () => 'pong')
    createWindow()

    app.on('activate', () => {
        // 在 macOS 系统内, 如果没有已开启的应用窗口
        // 点击托盘图标时通常会重新创建一个新窗口
        if (BrowserWindow.getAllWindows().length === 0) {
            createWindow()
        }
    })
})

// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此, 通常
// 对应用程序和它们的菜单栏来说应该时刻保持激活状态,
// 直到用户使用 Cmd + Q 明确退出
app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit()
    }
})
  • electron/preload.js

const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('versions', {
    node: () => process.versions.node,
    chrome: () => process.versions.chrome,
    electron: () => process.versions.electron,
    ping: () => ipcRenderer.invoke('ping')
    // 除函数之外,我们也可以暴露变量
})
  • forge.config.js

module.exports = {
  packagerConfig: {
    asar: true,
  },
  rebuildConfig: {},
  makers: [
    {
      name: '@electron-forge/maker-squirrel',
      config: {},
    },
    {
      name: '@electron-forge/maker-zip',
      platforms: ['darwin'],
    },
    {
      name: '@electron-forge/maker-deb',
      config: {},
    },
    {
      name: '@electron-forge/maker-rpm',
      config: {},
    },
  ],
};

项目打包

# 先打包vue项目

npm run build

# 再打包electron

npm run make

项目打包完成后,编译器会返回打包生成路径地址,从本地进入生成文件路径。

里面会存在一个make(含安装包)文件夹和一个打包出来的项目文件夹。

双击进入项目文件夹,点击运行你的应用程序吧!

参考文档

注意

项目启动electron应用需要修改vue项目的路由模式,使用hash模式,不然会出现应用运行启动空白界面

const router = createRouter({
    // mode: 'history',
    history: createWebHashHistory(),
    routes
})

const routes = [
    {
        path: '/',
        name: '欢迎界面',
        component: () => import('@/views/Welcome.vue')
    },
]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值