使用Vite和Electron构建跨平台桌面应用

Viewed 0

Electron是一个跨平台的桌面应用程序开发框架,它允许开发人员使用Web技术(如HTML、CSS和JavaScript)构建桌面应用程序,这些应用程序可以在Windows、macOS和Linux等操作系统上运行。

Electron的核心是Chromium浏览器内核和Node.js运行时环境。Chromium内核提供了现代浏览器的功能,例如HTML5和CSS3支持,JavaScript引擎等,而Node.js运行时环境则提供了服务器端JavaScript的能力和模块系统,这使得开发人员可以使用Node.js的模块和工具来构建桌面应用程序。

许多知名应用都是使用Electron构建的,例如Visual Studio Code、Atom和Postman。

要开始一个Electron项目,我们可以结合Vue和Vite。首先,创建Vue项目并安装相关依赖:

npm init vue
npm install
npm install electron electron-builder -D

如果安装过程中出现超时问题,可以配置镜像源来加速下载。

为了在开发时能够同时启动Vite开发服务器和Electron应用,而不是分别启动两次,我们可以创建自定义的Vite插件。在项目根目录创建一个plugins文件夹,并编写以下文件:

  • plugins/vite.electron.dev.ts:用于开发模式启动Electron。
  • plugins/vite.electron.build.ts:用于打包Electron项目。

首先,需要创建Electron的主进程文件src/background.ts

import { app, BrowserWindow } from 'electron'

app.whenReady().then(async () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false,
      webSecurity: false,
    }
  })

  if (process.argv[2]) {
    win.loadURL(process.argv[2])
  } else {
    win.loadFile('index.html')
  }
})

这段代码创建了一个基本的Electron窗口,并根据命令行参数加载URL或本地文件。在开发模式下,通过传递URL参数可以连接到Vite开发服务器,实现热更新。

接下来,在vite.electron.dev.ts中编写开发插件:

import type { Plugin } from 'vite'
import type { AddressInfo } from 'net'
import { spawn } from 'child_process'
import fs from 'fs'

export const viteElectronDev = (): Plugin => {
  return {
    name: 'vite-electron-dev',
    configureServer(server) {
      const initElectron = () => {
        require('esbuild').buildSync({
          entryPoints: ['src/background.ts'],
          bundle: true,
          outfile: 'dist/background.js',
          platform: 'node',
          target: 'node12',
          external: ['electron']
        })
      }

      initElectron()

      server?.httpServer?.once('listening', () => {
        const addressInfo = server?.httpServer?.address() as AddressInfo
        const IP = `http://localhost:${addressInfo.port}`
        let electronProcess = spawn(require('electron'), ['dist/background.js', IP])

        fs.watchFile('src/background.ts', () => {
          electronProcess.kill()
          initElectron()
          electronProcess = spawn(require('electron'), ['dist/background.js', IP])
        })

        electronProcess.stdout?.on('data', (data) => {
          console.log(`日志: ${data}`)
        })
      })
    }
  }
}

这个插件在Vite开发服务器启动时,使用esbuild编译TypeScript主进程代码,然后启动Electron进程。它还监听background.ts文件的变化,实现热重载。

对于生产打包,创建vite.electron.build.ts插件:

import type { Plugin } from 'vite'
import * as electronBuilder from 'electron-builder'
import path from 'path'
import fs from 'fs'

export const viteElectronBuild = (): Plugin => {
  return {
    name: 'vite-electron-build',
    closeBundle() {
      const initElectron = () => {
        require('esbuild').buildSync({
          entryPoints: ['src/background.ts'],
          bundle: true,
          outfile: 'dist/background.js',
          platform: 'node',
          target: 'node12',
          external: ['electron'],
        })
      }

      initElectron()

      const json = JSON.parse(fs.readFileSync('package.json', 'utf-8'))
      json.main = 'background.js'
      fs.writeSync(fs.openSync('dist/package.json', 'w'), JSON.stringify(json, null, 2))

      fs.mkdirSync(path.join(process.cwd(), "dist/node_modules"))

      electronBuilder.build({
        config: {
          appId: 'com.example.app',
          productName: 'vite-electron',
          directories: {
            output: path.join(process.cwd(), "release"),
            app: path.join(process.cwd(), "dist"),
          },
          asar: true,
          nsis: {
            oneClick: false,
          }
        }
      })
    }
  }
}

这个插件在Vite构建完成后触发,先编译主进程代码,然后修改package.json的main字段,并创建必要的目录,最后使用electron-builder进行打包。

最后,在vite.config.ts中配置这些插件:

import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteElectronDev } from './plugins/vite.electron.dev'
import { viteElectronBuild } from './plugins/vite.electron.build'

export default defineConfig({
  plugins: [
    vue(),
    viteElectronDev(),
    viteElectronBuild()
  ],
  base: './',
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

通过以上配置,我们实现了一个集成Vite和Electron的完整开发环境,支持开发时的热更新和生产打包。

0 Answers