vue vite ts electron ipc arm64

初始化

npm init vue # 全选 yes
npm i # 进入项目目录后使用
npm install electron electron-builder -D
npm install commander -D # 额外组件

增加文件

新建 plugins 文件夹

src/background.ts

属于主进程

ipcMain.on、ipcMain.handle 都用于主进程监听 ipc,ipcMain.on 用于监听 ipcRenderer.send,ipcMain.handle 用于监听 ipcRenderer.invoke 并 return xxx

ipc 单向:
从渲染进程发向主进程:ipcRenderer.send
从主进程发向渲染进程:window.webContents.send

ipc 双向:
从渲染进程发向主进程,主进程还会返回发向渲染进程:ipcRenderer.invoke
从主进程发向渲染进程,渲染进程还会返回发向主进程:没有类似于 ipcRenderer.invoke 的,需要间接实现。主进程使用 window.webContents.send,渲染进程使用 ipcRenderer.send

import { app, BrowserWindow, screen, ipcMain } from 'electron'
import path from 'path'
import { Command } from 'commander';

app.whenReady().then(() => {
  const command = new Command
  let width, height
  let options

  command
    .option('-m, --maximize', 'maximize window')
    .option('-l, --location <>', 'location of load index page', 'index.html')
    .option('-d, --dev', 'openDevTools')
    .option('--no-sandbox', 'other')
    .parse()

  options = command.opts()

  if (options.maximize) {
    width = screen.getPrimaryDisplay().workAreaSize.width
    height = screen.getPrimaryDisplay().workAreaSize.height
  }
  else {
    width = 800
    height = 600
  }

  const window = new BrowserWindow({
    width: width,
    height: height,

    autoHideMenuBar: true,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  if (options.location.indexOf(':') >= 0)
    window.loadURL(options.location)
  else
    window.loadFile(options.location)

  if (options.dev)
    window.webContents.openDevTools()

  ipcMain.on('rtm', () => {
    console.log('rtm')
    window.webContents.send('mtr')
  })

  ipcMain.on('rtm_p', (e, p) => {
    console.log(p)
    window.webContents.send('mtr_p', `mtr_p ${p}`)
  })

  ipcMain.handle('rtmmtr_p', (e, p) => {
    console.log(p)
    return 'rtmmtr_p_return'
  })
})

src/preload.ts

预加载脚本,用来给渲染进程提供使用 ipcRenderer 的方法
rtm 是渲染进程发向主进程;rtmmtr 是从渲染进程发向主进程,主进程还会返回发向渲染进程;mtr 是主进程发向渲染进程

import { contextBridge, ipcRenderer } from 'electron'

contextBridge.exposeInMainWorld('electronAPI', {
  rtm: () => ipcRenderer.send('rtm'),
  rtm_p: (p: any) => ipcRenderer.send('rtm_p', p),
  rtmmtr_p: (p: any) => ipcRenderer.invoke('rtmmtr_p', p),
  mtr: (p: any) => ipcRenderer.on('mtr', p),
  mtr_p: (p: any) => ipcRenderer.on('mtr_p', p),
})

src/renderer.d.ts

给渲染进程用的 preload.ts 里的方法的类型声明

export interface IElectronAPI {
    rtm: () => Promise,
    rtm_p: (p: any) => Promise,
    rtmmtr_p: (p: any) => Promise,
    mtr: (p: any) => Promise,
    mtr_p: (p: any) => Promise,
}

declare global {
    interface Window {
        electronAPI: IElectronAPI
    }
}

plugins/vite.electron.dev.ts

自定义 dev 方法,用于启动 vite 后带起 electron

// 导入需要使用的类型和库
import type { Plugin } from 'vite'
import type { AddressInfo } from 'net'
import { spawn } from 'child_process'
import fs from 'fs'

// 导出Vite插件函数
export const viteElectronDev = (): Plugin => {
    return {
        name: 'vite-electron-dev',
        // 在configureServer中实现插件的逻辑
        configureServer(server) {
            // 定义初始化Electron的函数
            const initElectron = () => {
                // 使用esbuild编译TypeScript代码为JavaScript
                require('esbuild').buildSync({
                    entryPoints: ['src/background.ts', 'src/preload.ts'],
                    bundle: true,
                    outdir: 'dist',
                    platform: 'node',
                    external: ['electron']
                })
            }

            // electron 运行
            let electron_run = (ip: string) => {
                initElectron()
                // 启动Electron进程
                let electronProcess = spawn(require('electron'), ['dist/background.js', '-l', ip, '-d'])

                // 监听Electron进程的stdout输出
                electronProcess.stdout?.on('data', (data) => {
                    console.log(`${data}`);
                });

                return electronProcess
            }

            // 监听Vite的HTTP服务器的listening事件
            server?.httpServer?.once('listening', () => {
                // 获取HTTP服务器的监听地址和端口号
                const address = server?.httpServer?.address() as AddressInfo
                const ip = `http://localhost:${address.port}`

                let electronProcess = electron_run(ip)

                // 监听主进程代码的更改
                fs.watch('src', () => {
                    // 杀死当前的Electron进程
                    electronProcess.kill()

                    electronProcess = electron_run(ip)
                })
            })
        }
    }
}

plugins/vite.electron.build.ts

自定义 build 方法,这里打包了 linux 的 x64、arm64 的包

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

// 导出Vite插件函数
export const viteElectronBuild = (): Plugin => {
    return {
        name: 'vite-electron-build',

        // closeBundle是Vite的一个插件钩子函数,用于在Vite构建完成后执行一些自定义逻辑。
        closeBundle() {

            // 定义初始化Electron的函数
            const initElectron = () => {
                // 使用esbuild编译TypeScript代码为JavaScript
                require('esbuild').buildSync({
                    entryPoints: ['src/background.ts', 'src/preload.ts'],
                    bundle: true,
                    outdir: 'dist',
                    platform: 'node',
                    external: ['electron']
                })
            }

            // 调用初始化Electron函数
            initElectron()

            // 修改package.json文件的main字段,不然会打包失败
            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))

            // 创建一个空的node_modules目录 不然会打包失败
            fs.mkdirSync(path.join(process.cwd(), "dist/node_modules"));

            // 使用electron-builder打包Electron应用程序
            electronBuilder.build({
                config: {
                    appId: 'com.example.app',
                    productName: 'vite-electron',
                    directories: {
                        output: path.join(process.cwd(), "release"), //输出目录
                        app: path.join(process.cwd(), "dist"), //app目录
                    },
                    linux: {
                        "target": [
                            {
                                "target": "AppImage",
                                "arch": ["x64", "arm64"]
                            }
                        ]
                    }
                }
            })
        }
    }
}

修改源文件

src/App.vue

属于渲染进程

window.electronAPI.xxx() 就是预加载脚本(preload.ts)给渲染进程提供的使用 ipcRenderer 的方法
window.electronAPI.mtr 和 …mtr_p (mtr:main to renderer)用于监听主进程发过来的消息
由于 window.electronAPI.rtmmtr_p 使用 ipcRenderer.invoke,这是异步方法,如果不在其前面加 await 而直接获取会得到一个用于异步执行的对象(Promise),其内容包含了需要异步执行的东西,await 就是等待该对象运行结束从而获取正确值,而 await 需要其调用者是异步的,所以 increment() 也加上了 async(异步标志)






修改配置文件

tsconfig.node.json

{
  "extends": "@tsconfig/node18/tsconfig.json",
  "include": [
    "vite.config.*",
    "vitest.config.*",
    "cypress.config.*",
    "nightwatch.conf.*",
    "playwright.config.*",
    "plugins/**/*.ts"
  ],
  "compilerOptions": {
    "composite": true,
    "module": "ESNext",
    "types": ["node"]
  }
}

vite.config.ts

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

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { viteElectronDev } from './plugins/vite.electron.dev'
import { viteElectronBuild } from './plugins/vite.electron.build'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    vueJsx(),
    viteElectronDev(),
    viteElectronBuild()
  ],
  base: './', //默认绝对路径改为相对路径 否则打包白屏
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

使用

启动:npm run dev
打包:npm run build

npm run dev启动后桌面出现应用界面,并自动打开开发者工具。修改 src 下的任何文件都会自动编译并重启应用
打包后启动可以添加 -m 全屏,-d 打开开发者工具

其他

  • https://xiaoman.blog.csdn.net/article/details/131713875?spm=1001.2014.3001.5502
  • https://www.electronjs.org/zh/docs/latest/tutorial/context-isolation
  • https://www.electronjs.org/zh/docs/latest/tutorial/ipc

你可能感兴趣的:(electron,vue.js,electron,javascript)