在这一节中,我们将学习如何将播放音频、上一首、下一首和暂停音频时的状态保存到 Realm 数据库。
1. 保存音频播放数据
在 fetchShow 中,我们首先从 API 获取音频数据,并从 dva 的 state 中提取当前播放的音频信息(如 id、title、thumbnailUrl、currentTime 等),然后保存到 Realm 数据库中。
*fetchShow(payload, {call, put, select}) {
const {data} = yield call(axios.get, REQUEST_URL);
// 获取当前播放器的状态
const {id, title, thumbnailUrl, currentTime}: PlayerModelState = yield select(({player}: RootState) => player);
saveProgram({id, title, thumbnailUrl, currentTime, duration: getDuration()});
},
2. 暂停时更新播放进度
在暂停时,我们需要更新当前播放的时间:
*pause(_, {put, select}) {
const {id, currentTime}: PlayerModelState = yield select(({player}: RootState) => player);
saveProgram({id, currentTime});
},
3. 上一首、下一首
由于上一首和下一首的功能会触发 fetchShow,因此无需再次单独添加保存方法,fetchShow 已经处理了这些情况。
4. 减少代码重复
如上所示,保存播放数据的逻辑已经非常简洁。正是因为在之前的章节中,我们已经很好的封装了业务逻辑。如果没有 dva 的话,这些逻辑会直接放在页面中,导致代码混乱,增加后期修改的难度。
尽量将 业务逻辑 和 UI 逻辑 分离,这样在后期的开发过程中能大大减少修改和维护的工作量。
5. 处理 IProgram 类型
在定义 saveProgram 函数时,参数类型为 any,这虽然可以工作,但不推荐。为了更好地利用 TypeScript 的类型检查,我们将其改为 IProgram 类型。
export function saveProgram(data: IProgram) {
try {
realm.write(() => {
realm.create('Program', data, true); // true 表示若 id 存在则更新
});
} catch (e) {
console.log('保存失败');
}
}
但是我们遇到了一个问题:IProgram 中的属性默认是必填的,而我们更新时只传入了 id 和 currentTime,因此会报错。为了解决这个问题,我们可以将 IProgram 的属性改为可选项:
export interface IProgram {
id: string;
title?: string;
thumbnailUrl?: string;
currentTime?: number;
duration?: number;
}
6. 使用 Partial 类型
更优雅的做法是使用 TypeScript 的 Partial 类型,它会自动将所有属性设置为可选:
export function saveProgram(data: Partial<IProgram>) {
try {
realm.write(() => {
realm.create('Program', data, true);
});
} catch (e) {
console.log('保存失败');
}
}
Partial 类型可以让我们只传入需要更新的属性,避免了手动修改类型的麻烦。
7. 总结
在这一节中,我们通过 dva 的 select 和 saveProgram 方法,将音频播放的状态持久化到 Realm 数据库中。我们还使用了 TypeScript 的高级类型 Partial 来简化代码,使得在保存数据时只传入必要的字段。
下一节,我们将继续实现音频播放记录的显示功能。