Skip to content

创建插件进阶指南

本指南深入讲解 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')
}

调试技巧

渲染进程调试

  1. 打开插件窗口
  2. 使用快捷键打开 DevTools:
    • macOS: Cmd + Option + I
    • Windows/Linux: Ctrl + Shift + I
  3. 使用 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 FileMetadata

4. 性能优化

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(包含依赖)。对于大型插件,考虑拆分为多个子插件或使用外部资源。

下一步

Stellar Efficiency, Born in Innovation.