vokex.app
v0.1.5
Published
超轻量级桌面应用构建库 - Vite 构建后一键打包为原生可执行文件
Maintainers
Readme
Vokex
超轻量级桌面应用构建库。将前端代码通过 Vite 构建后一键嵌入到预编译的 Rust 壳中,生成单个原生可执行文件(最小 ~1.8MB)。架构类似 Tauri 的极简版,壳基于 tao + wry(系统 WebView)。
目前仅支持 Windows。macOS、Linux 将在后续版本中添加。
特性
- 超轻量:构建产物最小 ~1.8MB,仅依赖系统 WebView
- 零 Rust 门槛:
npm install vokex.app即可使用,不需要 Rust 工具链 - Vite 原生集成:Vite 插件自动接管开发和构建流程
- 单文件输出:前端资源 zlib 压缩后嵌入到可执行文件尾部
- 双模式运行:开发时加载 localhost,生产时从自身尾部读取资源
- 丰富的 API:14 个模块,112 个公开方法
- TypeScript 优先:完整的类型支持
- 安全沙箱:远端页面默认禁用危险 API,支持白名单配置
- 远程窗口:支持加载远端 URL 创建子窗口
- 文件搜索:支持 glob 模式匹配(
*.txt,**/*.js)
快速开始
1. 创建项目
npm create vite@latest my-app -- --template vanilla
cd my-app
npm install vokex.app2. 配置 Vite
// vite.config.ts
import { defineConfig } from "vite";
import { vokexPlugin } from "vokex.app/vite-plugin";
export default defineConfig({
plugins: [
vokexPlugin({
name: "我的应用",
identifier: "com.example.myapp",
version: "1.0.0",
icon: "icon.png",
window: {
title: "我的应用",
width: 1200,
height: 800,
},
devtools: process.env.NODE_ENV === 'development',
}),
],
});3. 使用 API
import { app, fs, browserWindow, menu, dialog } from "vokex.app";
// 应用就绪后执行
app.on("ready", async () => {
const name = await app.getName();
console.log(`${name} 已启动`);
});
// 文件操作
const content = await fs.readFile("config.json");
await fs.writeFile("output.txt", "Hello World");
// 窗口管理
const win = await browserWindow.create({ title: "子窗口", width: 600, height: 400 });
// 原生菜单
await menu.setApplicationMenu([
{ type: 'submenu', label: '文件', submenu: [
{ id: 'new', label: '新建' },
{ type: 'separator' },
{ type: 'native', nativeLabel: 'quit' },
]},
]);
// 对话框
const result = await dialog.showOpenDialog({
filters: [{ name: "文本文件", extensions: ["txt", "md"] }],
});4. 构建与开发
# 开发模式(启动 dev server + 原生壳)
npm run dev
# 构建打包(输出到 release/ 目录)
npm run build
# 验证构建产物
npx vokex.app validate release/我的应用.exe架构
┌───────────────────────────────────────┐
│ 前端代码 (HTML/JS/CSS) │
│ import { app, fs } from "vokex.app" │
├───────────────────────────────────────┤
│ 运行时 Bridge (注入 JS) │
│ window.__VOKEX__.call("fs.readFile") │
├───────────────────────────────────────┤
│ IPC: postMessage ↔ evaluate_script │
├───────────────────────────────────────┤
│ Rust 壳 (wry + tao) │
│ 窗口管理 │ API 路由 │ 资源加载 │
├───────────────────────────────────────┤
│ 系统 WebView │
│ Windows: WebView2 macOS: WKWebView │
└───────────────────────────────────────┘IPC 通信链路:
前端: window.__VOKEX__.call("fs.readFile", [path])
→ window.ipc.postMessage(JSON)
→ Rust wry IPC handler
→ 主线程事件循环
→ dispatch 到对应 API 模块
→ 同步直接 eval / 异步线程池完成后 eval
→ window.__VOKEX_IPC__(response)
→ Promise resolveAPI 参考
所有 API 都是异步的,返回 Promise。
app - 应用管理
import { app } from "vokex.app";| 方法 | 说明 |
|---|---|
| quit() | 退出应用 |
| exit(code?) | 退出并返回指定退出码 |
| restart() | 重启应用 |
| getAppPath() | 获取应用可执行文件路径 |
| getPath(name) | 获取系统路径:home appData desktop documents downloads temp cwd |
| getVersion() | 获取版本号 |
| getName() | 获取应用名称 |
| getIdentifier() | 获取应用标识符 |
| getLocale() | 获取系统语言 |
| getPid() | 获取进程 PID |
| getArgv() | 获取启动参数 |
| getEnv(key) | 获取环境变量 |
| getPlatform() | 获取平台:win32 darwin linux |
| getArch() | 获取架构:x64 arm64 |
| requestSingleInstanceLock() | 请求单实例锁,返回 true 表示是首个实例 |
| on(event, callback) | 监听应用事件 |
事件: ready window-all-closed before-quit second-instance activate
browserWindow - 窗口管理
import { browserWindow } from "vokex.app";创建窗口:
const win = await browserWindow.create({
title: "子窗口",
width: 800,
height: 600,
url: "page.html", // 相对路径或完整 URL
center: true,
resizable: true,
icon: "icon.png",
});静态方法:
| 方法 | 说明 |
|---|---|
| create(options?) | 创建新窗口,返回 BrowserWindow 实例 |
| getAll() | 获取所有窗口信息 |
| getFocused() | 获取当前聚焦窗口信息 |
| getById(id) | 按 ID 获取窗口实例 |
| getWindow(id) | 同 getById |
| getCurrentWindow() | 获取当前窗口实例 |
| getFocusedWindow() | 获取当前聚焦窗口实例 |
窗口实例方法:
| 方法 | 说明 |
|---|---|
| getId() | 获取窗口 ID |
| close() | 关闭窗口 |
| show() / hide() | 显示 / 隐藏 |
| minimize() / maximize() / restore() | 最小化 / 最大化 / 恢复 |
| unmaximize() | 取消最大化 |
| focus() / blur() | 聚焦 / 失焦 |
| isMaximized() isMinimized() isFullScreen() isFocused() isVisible() | 状态查询 |
| isResizable() / setResizable(flag) | 可调整大小 |
| isMinimizable() / setMinimizable(flag) | 可最小化 |
| isMaximizable() / setMaximizable(flag) | 可最大化 |
| isClosable() / setClosable(flag) | 可关闭 |
| isDecorated() / setDecorated(flag) | 窗口装饰(标题栏) |
| setFullScreen(flag) | 全屏切换 |
| setTitle(title) / getTitle() | 设置 / 获取标题 |
| setSize(w, h) / getSize() | 设置 / 获取大小 |
| setMinimumSize(w, h) / setMaximumSize(w, h) | 限制窗口尺寸范围 |
| setPosition(x, y) / getPosition() | 设置 / 获取位置 |
| center() | 窗口居中 |
| setOpacity(opacity) | 设置透明度 (0.0~1.0) |
| setBackgroundColor(color) | 设置背景色 |
| setIcon(icon) | 设置窗口图标 |
| setAlwaysOnTop(flag) | 置顶 |
| setAlwaysOnBottom(flag) | 置底 |
| loadFile(path) / loadURL(url) | 加载本地文件 / URL |
| reload() | 重新加载 |
| setProgressBar(progress) | 任务栏进度条 (0.0~1.0) [Windows] |
| setSkipTaskbar(flag) | 隐藏任务栏图标 |
| flashTaskbar(flag) | 闪烁任务栏 |
| requestUserAttention(level?) | 请求用户关注 |
| setContentProtection(enabled) | 内容保护(防截图) |
| setVisibleOnAllWorkspaces(visible) | 所有工作区可见 |
| scaleFactor() | 获取缩放因子 |
| getInnerPosition() | 获取客户区位置 |
| getOuterSize() | 获取窗口外部大小 |
| setCursorIcon(icon) | 设置光标图标 |
| setCursorPosition(x, y) | 移动光标 |
| setCursorGrab(grab) | 锁定光标 |
| setCursorVisible(visible) | 隐藏/显示光标 |
| sendMessage(message, targetWindow) | 窗口间通信 |
| on(event, callback) / off(event, callback) | 监听 / 移除事件 |
事件: close resize move minimize maximize restore focus blur enter-full-screen leave-full-screen window.message
fs - 文件系统
import { fs } from "vokex.app";| 方法 | 说明 |
|---|---|
| readFile(path) | 读取文件为文本 |
| readFileBinary(path) | 读取文件为 Base64 |
| writeFile(path, data) | 写入文件 |
| appendFile(path, data) | 追加内容 |
| deleteFile(path) | 删除文件 |
| readDir(path) | 列出目录内容,返回 DirEntry[] |
| createDir(path) | 创建目录 |
| removeDir(path) | 删除目录 |
| stat(path) | 获取文件信息 |
| exists(path) | 检查文件是否存在 |
| copyFile(src, dest) | 复制文件 |
| moveFile(src, dest) | 移动 / 重命名文件 |
| glob(options) | glob 模式搜索文件 |
GlobOptions:
| 选项 | 类型 | 说明 |
|---|---|---|
| pattern | string | glob 模式,如 *.txt, **/*.js |
| cwd | string? | 搜索目录,默认当前目录 |
| ignore | string[]? | 排除模式列表 |
| nodir | boolean? | 只返回文件,不返回目录 |
| absolute | boolean? | 返回绝对路径 |
| dot | boolean? | 是否包含隐藏文件(以 . 开头) |
http - 网络请求
import { http } from "vokex.app";| 方法 | 说明 |
|---|---|
| request(url, options?) | 通用请求 |
| get(url, options?) | GET 请求 |
| post(url, data?, options?) | POST 请求 |
| put(url, data?, options?) | PUT 请求 |
| delete(url, options?) | DELETE 请求 |
RequestOptions: method headers body timeout
HttpResponse: statusCode statusText headers body ok
dialog - 对话框
import { dialog } from "vokex.app";| 方法 | 说明 |
|---|---|
| showMessageBox(options) | 消息对话框,返回 { response, cancelled } |
| showErrorBox(options) | 错误对话框 |
| showOpenDialog(options?) | 打开文件对话框,返回文件路径或 null |
| showSaveDialog(options?) | 保存文件对话框,返回文件路径或 null |
showMessageBox 支持的 type: none okCancel yesNo yesNoCancel
showMessageBox 支持的 icon: info warning error
对话框选项: title defaultPath defaultName multiple directory filters
menu - 原生菜单
import { menu } from "vokex.app";| 方法 | 说明 |
|---|---|
| setApplicationMenu(menu) | 设置应用顶部菜单栏 |
| removeApplicationMenu() | 移除应用菜单栏 |
| setContextMenu(menu, x?, y?) | 弹出右键上下文菜单 |
| onMenuClick(callback) | 监听菜单项点击事件 |
菜单项类型:
| type | 说明 |
|---|---|
| normal(默认) | 普通菜单项,需要 id 和 label |
| separator | 分隔线 |
| submenu | 子菜单,需要 label 和 submenu |
| checkbox | 复选框菜单项,支持 checked 属性 |
| native | 系统原生菜单项,需要 nativeLabel |
NativeLabel(原生菜单项): separator copy cut paste selectAll undo redo minimize maximize fullscreen hide hideOthers showAll closeWindow quit about services bringAllToFront
示例:
// 应用菜单栏
await menu.setApplicationMenu([
{ type: 'submenu', label: '文件', submenu: [
{ id: 'new', label: '新建' },
{ type: 'separator' },
{ type: 'native', nativeLabel: 'quit' },
]},
{ type: 'submenu', label: '编辑', submenu: [
{ type: 'native', nativeLabel: 'copy' },
{ type: 'native', nativeLabel: 'paste' },
]},
]);
// 右键菜单
await menu.setContextMenu([
{ id: 'copy', label: '复制' },
{ id: 'paste', label: '粘贴' },
], e.x, e.y);
// 监听点击
menu.onMenuClick(({ id }) => {
console.log('点击了:', id);
});tray - 系统托盘
import { tray } from "vokex.app";| 方法 | 说明 |
|---|---|
| tray.create(options) | 创建托盘图标,返回 Tray 实例 |
TrayOptions: icon(必填) tooltip title menu
Tray 实例方法:
| 方法 | 说明 |
|---|---|
| getId() | 获取托盘 ID |
| setToolTip(text) | 设置提示文本 |
| setTitle(title) | 设置标题 (macOS) |
| setMenu(template) | 更新右键菜单 |
| setImage(icon) | 更新图标 |
| destroy() | 销毁托盘 |
| on(event, callback) | 监听事件 |
事件: click right-click double-click
示例:
const myTray = await tray.create({
icon: "icon.png",
tooltip: "我的应用",
menu: [
{ id: "show", label: "显示窗口" },
{ type: "separator" },
{ id: "quit", label: "退出" },
],
});
myTray.on("click", () => {
// 托盘左键点击
});clipboard - 剪贴板
import { clipboard } from "vokex.app";| 方法 | 说明 |
|---|---|
| readText() | 读取剪贴板文本 |
| writeText(text) | 写入剪贴板 |
| clear() | 清空剪贴板 |
notification - 系统通知
import { notification } from "vokex.app";| 方法 | 说明 |
|---|---|
| show(options) | 发送系统通知 |
NotificationOptions: title(必填) body
shell - 系统命令
import { shell } from "vokex.app";| 方法 | 说明 |
|---|---|
| openExternal(url) | 用默认浏览器打开 URL |
| openPath(path) | 用系统默认程序打开文件/目录 |
| execCommand(command, options?) | 执行 shell 命令,返回 { code, stdout, stderr, success } |
| trashItem(path) | 移到回收站 |
ExecOptions: cwd env
process - 进程信息
import { process } from "vokex.app";| 方法 | 说明 |
|---|---|
| getUptime() | 获取进程运行时长(秒) |
| getCpuUsage() | 获取 CPU 使用率,返回 { user, system } |
| getMemoryInfo() | 获取内存信息,返回 { total, available, used } |
| hostname() | 获取主机名 |
| env() | 获取所有环境变量 |
| kill(pid) | 终止指定进程 |
computer - 系统硬件信息
import { computer } from "vokex.app";| 方法 | 说明 |
|---|---|
| getCpuInfo() | CPU 信息:制造商、型号、核心数、频率等 |
| getOsInfo() | 操作系统信息:名称、版本、内核版本等 |
| getDisplays() | 显示器信息列表 |
| getMousePosition() | 鼠标位置 |
| getKeyboardLayout() | 键盘布局 |
storage - 本地持久化存储
import { storage } from "vokex.app";| 方法 | 说明 |
|---|---|
| setData(key, value) | 存储数据(JSON 序列化) |
| getData(key) | 读取数据 |
| getKeys() | 获取所有键名 |
| has(key) | 检查键是否存在 |
| removeData(key) | 删除指定键 |
| clear() | 清空所有数据 |
events - 事件总线
import { events } from "vokex.app";| 方法 | 说明 |
|---|---|
| on(event, listener) | 监听事件,返回取消监听函数 |
| off(event, listener) | 移除监听 |
| emit(event, data?) | 触发事件 |
资源嵌入格式
二进制文件尾部追加:
[MAGIC(5B "VOKEX")] [索引长度(4B LE)] [索引JSON] [zlib压缩数据] [偏移量(8B LE)]索引格式:{ "index.html": [offset, length], "assets/main.js": [offset, length] }
编译壳
如果你需要自己编译壳(而不是使用预编译版本):
cd shell
cargo build --release
# 输出: shell/target/release/vokex-shell.exe (Windows)许可证
MIT
