最佳实践
插件开发中的错误处理、状态管理、路径兼容等实践指南
最佳实践
错误处理
所有异步操作都应使用 try/catch 包裹,并通过 window.notify 向用户反馈:
async riskyOperation() {
try {
this.setProps({ statusText: '处理中...' })
const result = await suchmusic.execProgram('tool', ['--input', 'file.ncm'])
this.setProps({ statusText: '完成' })
window.notify('处理成功', 'success')
return { code: true, data: result }
} catch (err) {
this.setProps({ statusText: '失败' })
window.notify('处理失败: ' + (err.message || '未知错误'), 'error')
return { code: false, message: err.message }
}
}⚠️ 不要在插件方法中让未捕获的错误传播到宿主层。宿主会显示通用错误提示,用户体验差。
异步操作中的状态管理
长时间操作应使用 setProps 实时更新 UI 状态:
async processBatch(files: string[]) {
const total = files.length
this.setProps({ statusText: '处理中 0/' + total })
for (let i = 0; i < files.length; i++) {
await processFile(files[i])
this.setProps({ statusText: '处理中 ' + (i + 1) + '/' + total })
}
this.setProps({ statusText: '完成: ' + total + ' 个文件' })
}日志记录
养成良好的日志习惯,方便用户和开发者排查问题:
// ✅ 好的日志 — 有关键信息和步骤
console.log('开始获取歌单详情, playlistId=' + playlistId)
console.log('歌单名称: ' + playlistInfo.name + ', 共 ' + trackCount + ' 首')
// ❌ 差的日志 — 信息不足
console.log('ok')
console.log('done')对于需要展示给用户的日志,使用 log 类型的 UIField + setProps 追加内容:
_log(msg: string) {
this._logLines.push('[' + new Date().toLocaleTimeString() + '] ' + msg)
if (this._logLines.length > 200) {
this._logLines = this._logLines.slice(-100)
}
this.setProps({ importLog: this._logLines.join('\n') })
}ℹ️ 建议限制日志行数(如最多保留 200 行),避免内存无限增长。
文件路径处理的跨平台兼容
Such 运行在 Windows / macOS / Linux 上,处理文件路径时注意:
// ✅ 使用 suchmusic.getSetting('musicFolder') 获取音乐目录
// 不要硬编码路径
const musicFolder = suchmusic.getSetting('musicFolder')
// ✅ 路径拼接使用宿主提供的目录分隔符
// 在 Windows 上 getSetting 返回的路径已使用反斜杠
const destPath = musicFolder + '\\myfile.mp3'
// ⚠️ 如果从网络获取路径,注意转换分隔符
const normalizedPath = remotePath.replace(/\//g, '\\')权限最小化声明
只声明插件实际需要的权限,避免引起用户安全顾虑:
// ✅ 歌单导入插件只需要 fetch 和 playlist
permissions: ['fetch', 'playlist']
// ✅ 文件处理插件只需要 file_system
permissions: ['file_system']状态初始化
在 initialization() 中初始化所有 UI 状态:
initialization() {
this.setProps({
// 输入框默认值
inputPath: '',
// 状态标签
statusText: '就绪',
// 日志
outputLog: '',
// 从宿主读取的配置
musicDir: suchmusic.getSetting('musicFolder')
})
}方法返回值
所有供 UI 按钮调用的方法(uiSchema.actions[].method)建议返回统一格式:
{
code: boolean, // 是否成功
message?: string, // 错误信息(失败时)
// ... 其他业务数据
}这便于 UI 层根据返回值做出不同展示。
版本管理
实现 checkUpdate 方法时,建议:
- 通过 GitHub Releases API 或其他稳定接口获取最新版本
- 使用语义化版本比较(不要简单字符串比较)
- 网络异常时返回
null而非抛出错误
async checkUpdate() {
try {
const resp = await suchmusic.fetch(
'https://api.github.com/repos/yourname/your-plugin/releases/latest'
)
const release = await resp.json()
const latestVer = release.tag_name.replace(/^v/, '')
if (compareVersions(latestVer, this.version) > 0) {
return {
hasNew: true,
version: latestVer,
url: release.html_url,
changelog: release.body || ''
}
}
return { hasNew: false, version: '', url: '', changelog: '' }
} catch {
return null // 检查失败
}
}