vite + electron + typescript 的启动与开发

什么是 electron

Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 ChromiumNode.js 到 二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux------不需要本地开发 经验。

它的运行核心可以总结为下图

electron 的核心概念

主进程

每个 Electron 应用都有一个单一的主进程,作为应用程序的入口点。 主进程在 Node.js 环境中运行,这意味着它具有 require 模块和使用所有 Node.js API 的能力。

预加载脚本

预加载(preload)脚本包含了那些执行于渲染器进程中,且先于网页内容开始加载的代码 。 这些脚本虽运行于渲染器的环境中,却因能访问 Node.js API 而拥有了更多的权限, 预加载脚本是运行在渲染进程的,他的优先级高于渲染进程, 他有部分的 node.js API 权限。

渲染进程

每个 Electron 应用都会为每个打开的 BrowserWindow ( 与每个网页嵌入 ) 生成一个单独的渲染器进程。 洽如其名,渲染器负责 渲染 网页内容。 所以实际上,运行于渲染器进程中的代码是须遵照网页标准的 (至少就目前使用的 Chromium 而言是如此) 。每一个 tab 栏就对应一个渲染进程。

进程间通讯

其本质就是发布订阅模式,但是这里叫 ipc(进程间通讯)

  • 主进程向渲染进程
    • 主进程通过 webContents.send 向渲染进程发送消息
    • 预加载脚本通过 ipcRenderer.on 方法发送到渲染进程
    • 渲染进程接受对应方法即可
  • 渲染进程向主进程
    • 渲染进程调用预加载脚本的方法传递消息
    • 预加载脚本通过 ipcRender.send 方法发送到主进程
    • 主进程通过 ipcMain.on 方法监听消息
  • 主进程与渲染进程双向通讯
    • 发送方使用 invoke 触发事件
    • 接受方通过 handle 处理事件

其数据核心流向可以总结为这张图

启动项目

我们使用 vite + electron + typescript 做一个简单的开机一键启动应用的工具, 在这里我们就不自己在项目中去配置 electron , 感兴趣的可以查看官网 www.electronjs.org/zh/docs/lat... 如果下载不下来,要确保 node 版本为高版本, 并使用 pnpm 下载。 这里我们直接使用便捷的脚手架进行搭建 cn.electron-vite.org/guide/

sql 复制代码
npm create @quick-start/electron@latest

根据指引构建完成后你会得到这样一个文件目录

erlang 复制代码
.
├──src
│  ├──main
│  │  ├──index.ts
│  │  └──...
│  ├──preload
│  │  ├──index.ts
│  │  └──...
│  └──renderer    # with vue, react, etc.
│     ├──src
│     ├──index.html
│     └──...
├──electron.vite.config.ts
├──package.json
└──...

可以根据自己的需要在 vite 配置文件中配置想要的相关信息

同时也可以在 ts 配置文件中配置 ts 相关配置

  • main 文件夹

    main 文件夹就是主进程, 可以和计算机底层进行 io 操作,在这里我们添加两行代码

javascript 复制代码
import { onGetAppList, onHandleStartCMD } from './startApp'

....

app.whenReady().then(() => {
  electronApp.setAppUserModelId('com.electron')

  app.on('browser-window-created', (_, window) => {
    optimizer.watchWindowShortcuts(window)
  })

  // ============== event start =============
  ipcMain.handle('getAppPath', onGetAppList)
  ipcMain.handle('execStartCMD', onHandleStartCMD)
  // ============== event end ================

  createWindow()

  app.on('activate', function () {
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

抽离的两个方法

javascript 复制代码
// startApp.ts

import { exec, execSync } from 'child_process'

// 一键启动应用
const onHandleStartCMD = (event, data) => {
  // 启动目标软件(例如启动 VSCode)

  data.forEach((item) => {
    exec(`open "/Applications/${item}"`, (error, stdout) => {
      if (error) console.error(`执行失败: ${error}`)
      else console.log(`已启动: ${stdout}`)
    })
  })
}

// 获取应用列表
const onGetAppList = async () => {
  const res = execSync('ls /Applications').toString()
  return res.split('\n').filter((item) => item !== '')
}

export { onHandleStartCMD, onGetAppList }

注意上图的启动路径是可以配置的,不用写死, 这里为了方便直接写死了 /Applications

  • preload 文件夹

    这个文件是预加载脚本,可以理解为一个通道, 通过它可以实现 主进程与渲染进程的通讯

js 复制代码
import { contextBridge, ipcRenderer } from 'electron'
import { electronAPI } from '@electron-toolkit/preload'

/**
 * send 使用 on 监听
 * invoke 使用 handle 双向通讯
 */

// Custom APIs for renderer
const api = {
  // 获取应用路径
  getAppPath: async () => {
    return await ipcRenderer.invoke('getAppPath')
  },
  // 执行启动命令
  execStartCMD: async (list: string[]) => {
    return await ipcRenderer.invoke('execStartCMD', list)
  }
}

// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
  try {
    contextBridge.exposeInMainWorld('electron', electronAPI)
    contextBridge.exposeInMainWorld('api', api)
  } catch (error) {
    console.error(error)
  }
} else {
  // @ts-ignore (define in dts)
  window.electron = {
    sendMessage(channel: string, ...args: unknown[]) {
      ipcRenderer.send(channel, ...args)
    }
  }
  // @ts-ignore (define in dts)
  window.api = api
}
  • render 文件夹

    该文件夹就是一个普通的前端项目文件夹, 可以按照正常的前端项目开发进行开发

在启动时只需要调用暴露的方法名即可 api.execStartCMD(startList)

javascript 复制代码
import React from 'react'
import { Button, Flex } from 'antd'
import { useNavigate } from 'react-router-dom'
import { useAppSelector } from '@renderer/store/hooks'

const Start: React.FC = () => {
  const navigate = useNavigate()
  const startList = useAppSelector((state) => state.startConfig.startList)

  return (
    <main>
      <Flex gap={12}>
        <section>
          <Button
            onClick={async () => {
              api.execStartCMD(startList)
            }}
          >
            一键启动
          </Button>
        </section>
        <section>
          <Button
            onClick={() => {
              navigate('/settings')
            }}
          >
            配置启动项
          </Button>
        </section>
      </Flex>
    </main>
  )
}

export default Start

打包

执行对应命令行即可

最终打包成功后会生成 dist 文件

将压缩包解压后可以获得我们的程序

使用

双击打开后

点击配置启动项

返回点击一键启动

这个是一个简单的实现, 功能并不完善, 但能够帮助你快速的理解 electron 的运行规则, 你可以基于此去实现自己想要的功能

相关推荐
wearegogog1231 分钟前
基于 MATLAB 的卡尔曼滤波器实现,用于消除噪声并估算信号
前端·算法·matlab
Drawing stars8 分钟前
JAVA后端 前端 大模型应用 学习路线
java·前端·学习
品克缤13 分钟前
Element UI MessageBox 增加第三个按钮(DOM Hack 方案)
前端·javascript·vue.js
小二·21 分钟前
Python Web 开发进阶实战:性能压测与调优 —— Locust + Prometheus + Grafana 构建高并发可观测系统
前端·python·prometheus
小沐°28 分钟前
vue-设置不同环境的打包和运行
前端·javascript·vue.js
qq_419854051 小时前
CSS动效
前端·javascript·css
烛阴1 小时前
3D字体TextGeometry
前端·webgl·three.js
桜吹雪2 小时前
markstream-vue实战踩坑笔记
前端
C_心欲无痕2 小时前
nginx - 实现域名跳转的几种方式
运维·前端·nginx
花哥码天下2 小时前
恢复网站console.log的脚本
前端·javascript·vue.js