54 lines
2.6 KiB
Markdown
54 lines
2.6 KiB
Markdown
# mobx是什么
|
||
|
||
MobX 负责页面上会变化的状态。mobx-keystone 负责可以保存、比较和恢复的实体。页面组件只负责渲染,不存放业务状态。
|
||
|
||
## 目录
|
||
|
||
- [页面状态](#页面状态)
|
||
- [ViewModel 的生命周期](#viewmodel-的生命周期)
|
||
- [实体与快照](#实体与快照)
|
||
|
||
## 页面状态
|
||
|
||
ViewModel 里需要触发重渲染的字段标成 `observable`,修改这些字段的方法标成 `action`。组件用 `mobx-react-lite` 的 `observer` 包起来,字段变化后才会重画。
|
||
|
||
`SecurityViewModel` 的 `errorMessage`、`isLoading` 就是页面状态。它们跟着这一次打开的登录页,所以这个类标 `@injectable('Transient')`。
|
||
|
||
普通字段不用 observable。不会显示在界面上的局部变量也不放进 ViewModel。
|
||
|
||
## ViewModel 的生命周期
|
||
|
||
ViewModel 继承 `PageViewModel`。页面不自己 `new`,而调用 `useViewModel`。
|
||
|
||
```ts
|
||
export function useViewModel<T extends PageViewModel>(identifier: new (...args: any[]) => T): T {
|
||
const [viewModel] = useState(() => {
|
||
const vm = rootContainer.get(identifier);
|
||
vm.created();
|
||
return vm;
|
||
});
|
||
|
||
useEffect(() => {
|
||
viewModel.ready();
|
||
const onPageHide = () => viewModel.beforeRefresh();
|
||
window.addEventListener('pagehide', onPageHide);
|
||
return () => {
|
||
window.removeEventListener('pagehide', onPageHide);
|
||
viewModel.dispose();
|
||
};
|
||
}, [viewModel]);
|
||
|
||
return viewModel;
|
||
}
|
||
```
|
||
|
||
`created` 在实例取出时调用。`ready` 在页面挂上后调用,适合开始加载数据,例如 `HomeViewModel.ready` 去拉项目列表。`beforeRefresh` 在页面被浏览器收起时调用。离开页面时调用 `dispose`。
|
||
|
||
## 实体与快照
|
||
|
||
`Project`、`Document`、`LineShape` 使用 mobx-keystone 的 `@model`。要保存的字段用 `tProp` 声明,修改实体的方法标 `@modelAction`。对外改文档的方法在聚合根上:`Project.addShape`、`Project.undo`、`Project.redo`、`Project.touch`。
|
||
|
||
快照是实体在某一刻的普通对象。`getSnapshot` 取出它,`applySnapshot` 把实体恢复成那一份。`Project.addShape` 接受图形前,把 `Document` 的快照放进聚合自己的 `past`,`undo` 和 `redo` 再用 `applySnapshot` 换回来。`past` 和 `future` 在 `onInit` 里标成 `observable`,页面才能根据 `canUndo`、`canRedo` 重绘。它们不是 `tProp`,`getSnapshot(project)` 不包含撤销栈。
|
||
|
||
保存到本地库或提交给接口之前,仓储取出项目快照再写入。实体类仍然留在内存里给界面使用。编辑器 ViewModel 只持有当前的 `project`、`store`、工具和草稿。
|