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 的运行规则, 你可以基于此去实现自己想要的功能

相关推荐
顾安r3 小时前
11.8 脚本网页 星际逃生
c语言·前端·javascript·flask
Hello.Reader3 小时前
Data Sink定义、参数与可落地示例
java·前端·网络
im_AMBER3 小时前
React 17
前端·javascript·笔记·学习·react.js·前端框架
谷歌开发者4 小时前
Web 开发指向标 | Chrome 开发者工具学习资源 (六)
前端·chrome·学习
一晌小贪欢4 小时前
【Html模板】电商运营可视化大屏模板 Excel存储 + 一键导出(已上线-可预览)
前端·数据分析·html·excel·数据看板·电商大屏·大屏看板
发现你走远了4 小时前
连接模拟器网页进行h5的调试(使用Chrome远程调试(推荐)) 保姆级图文
前端·chrome
街尾杂货店&5 小时前
css - 实现三角形 div 容器,用css画一个三角形(提供示例源码)简单粗暴几行代码搞定!
前端·css
顺凡5 小时前
删一个却少俩:Antd Tag 多节点同时消失的原因
前端·javascript·面试
小白路过5 小时前
CSS transform矩阵变换全面解析
前端·css·矩阵
爬山算法6 小时前
Redis(110)Redis的发布订阅机制如何使用?
前端·redis·bootstrap