Appearance
创建插件进阶指南
本指南深入讲解 Spark 插件开发的高级特性,帮助您充分利用平台的强大能力。
插件类型详解
UI 插件
UI 插件运行在独立的渲染进程中,提供完整的 Web 技术栈支持。
适用场景:
- 带有用户界面的工具
- 数据可视化面板
- 交互式应用
- 需要复杂 UI 的插件
技术栈:
- Vue 3 / React / Svelte
- TypeScript / JavaScript
- CSS / Tailwind CSS
- Canvas / WebGL
System 插件
System 插件运行在主进程中,直接访问 Electron 和 Node.js API。
适用场景:
- 后台服务
- 系统监听器
- 定时任务
- 需要完整系统权限的功能
能力:
- 完整的 Node.js API
- Electron 主进程 API
- 操作系统级功能
- 直接文件系统访问
预加载脚本 (Preload Script)
预加载脚本是连接渲染进程和主进程的桥梁,提供安全的 IPC 通信。
基础结构
typescript
// src/preload.ts
import { contextBridge, ipcRenderer } from 'electron'
// 定义 API 类型
interface SparkAPI {
readFile: (path: string) => Promise<string>
writeFile: (path: string, content: string) => Promise<void>
onMessage: (callback: (message: string) => void) => void
}
// 使用 contextBridge 暴露安全的 API
contextBridge.exposeInMainWorld('sparkAPI', {
// 读取文件
readFile: (path: string) => ipcRenderer.invoke('fs:read', path),
// 写入文件
writeFile: (path: string, content: string) =>
ipcRenderer.invoke('fs:write', path, content),
// 监听主进程消息
onMessage: (callback: (message: string) => void) => {
ipcRenderer.on('main-message', (_, message) => callback(message))
},
// 移除监听器
removeListener: (channel: string) => {
ipcRenderer.removeAllListeners(channel)
}
})类型声明
typescript
// src/global.d.ts
interface Window {
sparkAPI: SparkAPI
}进程间通信 (IPC)
单向通信 (渲染进程 → 主进程)
typescript
// 渲染进程
await window.sparkAPI.writeFile('/path/to/file', 'Hello World')
// 主进程
ipcMain.handle('fs:write', async (_, path: string, content: string) => {
await fs.writeFile(path, content)
return { success: true }
})双向通信
typescript
// 预加载脚本
contextBridge.exposeInMainWorld('sparkAPI', {
sendCommand: (command: string, data: any) =>
ipcRenderer.send('plugin-command', command, data),
onCommandResponse: (callback: (response: any) => void) => {
ipcRenderer.on('command-response', (_, response) => callback(response))
}
})
// 渲染进程
window.sparkAPI.sendCommand('process-data', { id: 123 })
window.sparkAPI.onCommandResponse((response) => {
console.log('Response:', response)
})
// 主进程
ipcMain.on('plugin-command', async (event, command, data) => {
const result = await processCommand(command, data)
event.sender.send('command-response', result)
})广播通信
typescript
// 主进程向所有渲染进程广播
mainWindow.webContents.send('global-event', {
type: 'update',
data: { version: '1.0.1' }
})
// 渲染进程监听
window.sparkAPI.onGlobalEvent((event) => {
console.log('Global event:', event)
})Spark API 使用
文件操作
typescript
// 读取文件
const content = await window.sparkAPI.fs.readFile('/path/to/file.txt')
// 写入文件
await window.sparkAPI.fs.writeFile('/path/to/file.txt', 'Hello World')
// 列出目录
const files = await window.sparkAPI.fs.readdir('/path/to/directory')
// 监听文件变化
const watcher = await window.sparkAPI.fs.watch('/path/to/file', (event) => {
console.log('File changed:', event)
})剪贴板操作
typescript
// 读取剪贴板
const text = await window.sparkAPI.clipboard.readText()
// 写入剪贴板
await window.sparkAPI.clipboard.writeText('Hello Spark')
// 读取图片
const image = await window.sparkAPI.clipboard.readImage()
// 清空剪贴板
await window.sparkAPI.clipboard.clear()Shell 命令
typescript
// 执行命令
const result = await window.sparkAPI.shell.execute('ls -la')
// 执行命令并获取实时输出
const process = await window.sparkAPI.shell.spawn('npm', ['install'])
process.on('data', (data) => {
console.log('Output:', data)
})系统信息
typescript
// 获取系统信息
const sysInfo = await window.sparkAPI.system.getInfo()
console.log('Platform:', sysInfo.platform)
console.log('Arch:', sysInfo.arch)
console.log('Version:', sysInfo.version)
// 获取内存使用
const memory = await window.sparkAPI.system.getMemory()
console.log('Used:', memory.used)
console.log('Total:', memory.total)数据持久化
本地存储
typescript
// 存储数据
await window.sparkAPI.storage.set('user-preferences', {
theme: 'dark',
language: 'zh-CN'
})
// 读取数据
const prefs = await window.sparkAPI.storage.get('user-preferences')
// 删除数据
await window.sparkAPI.storage.delete('user-preferences')
// 清空所有数据
await window.sparkAPI.storage.clear()云同步存储
typescript
// 启用云同步
await window.sparkAPI.storage.set('user-profile', {
name: 'Spark User',
email: 'user@spark.dev'
}, { sync: true })
// 监听云同步事件
window.sparkAPI.storage.onSync((event) => {
console.log('Sync event:', event.type)
if (event.type === 'update') {
console.log('Updated keys:', event.keys)
}
})权限声明系统
权限类型
json
{
"permissions": [
"clipboard:read",
"clipboard:write",
"fs:read",
"fs:write",
"shell:execute",
"network:request",
"notification:show",
"window:create"
]
}权限说明
| 权限 | 描述 | 风险等级 |
|---|---|---|
clipboard:read | 读取剪贴板内容 | 中 |
clipboard:write | 写入剪贴板内容 | 低 |
fs:read | 读取文件系统 | 中 |
fs:write | 写入文件系统 | 高 |
shell:execute | 执行 shell 命令 | 高 |
network:request | 发起网络请求 | 中 |
notification:show | 显示系统通知 | 低 |
window:create | 创建新窗口 | 中 |
权限请求
typescript
// 在运行时请求权限
const granted = await window.sparkAPI.permissions.request('fs:write')
if (granted) {
// 用户已授权
await window.sparkAPI.fs.writeFile('/path/to/file', 'content')
}调试技巧
渲染进程调试
- 打开插件窗口
- 使用快捷键打开 DevTools:
- macOS:
Cmd + Option + I - Windows/Linux:
Ctrl + Shift + I
- macOS:
- 使用 Chrome DevTools 进行调试
主进程调试
bash
# 启动 Spark 并开启调试
npm run dev -- --inspect=9229
# 连接调试器
# 在 Chrome 中打开 chrome://inspect
# 点击 "inspect" 连接到 Spark 主进程日志输出
typescript
// 使用 Spark 的日志系统
window.sparkAPI.log.info('Info message')
window.sparkAPI.log.warn('Warning message')
window.sparkAPI.log.error('Error message')
// 查看日志
# 日志文件位置
macOS: ~/Library/Logs/Spark/
Windows: %APPDATA%/Spark/logs/
Linux: ~/.config/Spark/logs/最佳实践
1. 错误处理
typescript
// 始终处理可能的错误
try {
const result = await window.sparkAPI.fs.readFile('/path/to/file')
} catch (error) {
console.error('Failed to read file:', error)
// 向用户显示友好的错误信息
}2. 资源清理
typescript
// 组件卸载时清理资源
onUnmounted(() => {
window.sparkAPI.removeListener('main-message')
watcher?.close()
})3. 类型安全
typescript
// 使用 TypeScript 确保类型安全
interface FileMetadata {
name: string
size: number
modifiedAt: Date
}
const metadata = await window.sparkAPI.fs.getMetadata('/path/to/file') as FileMetadata4. 性能优化
typescript
// 使用防抖和节流
const debouncedSearch = debounce(async (query: string) => {
return await window.sparkAPI.search.perform(query)
}, 300)
// 使用 Web Worker 处理大量数据
const worker = new Worker('./data-processor.js', { type: 'module' })
worker.postMessage({ data: largeDataSet })常见问题 FAQ
如何在插件之间共享数据?
使用 Spark 的共享存储 API:
typescript
// 插件 A 写入数据
await window.sparkAPI.sharedStorage.set('shared-key', data)
// 插件 B 读取数据
const data = await window.sparkAPI.sharedStorage.get('shared-key')插件可以访问数据库吗?
可以。Spark 提供了内置的 SQLite 支持:
typescript
const db = await window.sparkAPI.database.open('my-plugin.db')
await db.execute('CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY, name TEXT)')
await db.execute('INSERT INTO items (name) VALUES (?)', ['Spark'])如何实现插件的热重载?
在开发模式下,Spark 会自动监听文件变化并重新加载插件。确保您的构建配置生成了正确的 source maps。
bash
npm run dev # 启用 watch 模式插件的大小有限制吗?
建议单个插件不超过 50MB(包含依赖)。对于大型插件,考虑拆分为多个子插件或使用外部资源。
