这些是MobX-State-Tree (MST) 的核心语法,用于定义可观察的状态树模型。我来详细解释每个部分:
1.types对象的作用
types是 MST 库导出的类型构建器集合,用于定义数据模型的形状、验证规则和行为。
import { types } from "mobx-state-tree"; // types 提供的方法: types.model() // 定义数据模型 types.compose() // 组合多个模型 types.string // 字符串类型 types.number // 数字类型 types.boolean // 布尔类型 types.array // 数组类型 types.map // Map类型 types.optional() // 可选类型 types.union() // 联合类型 types.literal() // 字面量类型 // ...等等2.Validation模型 - 验证规则定义
const Validation = types.model({ controlledTags: Types.unionTag(["Image"]), });作用和解释:
// 等价于: const Validation = { controlledTags: "只能与 'Image' 标签配合使用" }; // 这定义了: // 1. 验证规则:RectangleLabels 必须与 Image 标签一起使用 // 2. 类型约束:controlledTags 必须是 "Image" 类型 // 3. 运行时检查:如果配置错误会报错实际应用场景:
<!-- ✅ 正确的配置 --> <View> <RectangleLabels name="labels" toName="image"> <Label value="Person" /> </RectangleLabels> <Image name="image" value="$image" /> <!-- 必须有Image标签 --> </View> <!-- ❌ 错误的配置(会触发验证错误) --> <View> <RectangleLabels name="labels" toName="text"> <!-- 指向了Text而不是Image --> <Label value="Person" /> </RectangleLabels> <Text name="text" value="$text" /> <!-- 这里是Text,不是Image --> </View>3.ModelAttrs模型 - 核心属性定义
const ModelAttrs = types.model("RectangleLabelsModel", { pid: types.optional(types.string, guidGenerator), // 属性1 type: "rectanglelabels", // 属性2 children: Types.unionArray(["label", "header", "view", "hypertext"]), // 属性3 });详细分解:
属性 | 类型定义 | 解释 | 示例值 |
|---|---|---|---|
|
| 可选字符串,默认用 |
|
| 字面量字符串 | 固定类型标识,用于序列化/反序列化 |
|
|
| 联合数组,只能包含指定的几种类型元素 |
|
Types.unionArray的工作原理:
// 内部实现类似: const unionArray = (allowedTypes) => { return types.array( types.union({ dispatcher: (snapshot) => { if (snapshot.type === "label") return LabelModel; if (snapshot.type === "header") return HeaderModel; if (snapshot.type === "view") return ViewModel; if (snapshot.type === "hypertext") return HypertextModel; } }) ); };4.types.compose()- 模型组合(Mixin模式)
const Composition = types.compose( ControlBase, // 第1层:基础控制功能 LabelsModel, // 第2层:标签管理功能 ModelAttrs, // 第3层:自定义属性 RectangleModel, // 第4层:矩形绘制功能 Validation, // 第5层:验证规则 LabelMixin, // 第6层:标签混入 SelectedModelMixin.props({ _child: "LabelModel" }), // 第7层:选中状态 );组合的工作原理:
flowchart TD A[“types.compose”] --> B[“模型继承链”] B --> C[“ControlBase<br>基础功能”] C --> D[“LabelsModel<br>标签管理”] D --> E[“ModelAttrs<br>自定义属性”] E --> F[“RectangleModel<br>矩形功能”] F --> G[“Validation<br>验证规则”] G --> H[“LabelMixin<br>标签混入”] H --> I[“SelectedModelMixin<br>选中状态”] I --> J[“最终组合模型<br>拥有所有层的功能”] J --> K[“属性合并<br>方法继承<br>验证叠加”]组合规则:
属性合并:后面模型的属性会覆盖前面同名属性
方法继承:所有模型的方法都会被继承
验证叠加:所有验证规则都会生效
5.实际模型结构示例
组合后的完整模型结构如下:
// 组合后的 RectangleLabelsModel 包含: { // 来自 ControlBase name: string, toName: string, // ...其他基础属性 // 来自 LabelsModel labels: Array<Label>, selectedLabels: Array<Label>, // ...标签管理方法 // 来自 ModelAttrs pid: string, // 自动生成的唯一ID type: "rectanglelabels", // 固定类型 children: Array<label | header | view | hypertext>, // 来自 RectangleModel x: number, y: number, width: number, height: number, rotation: number, // ...矩形绘制方法 // 来自 Validation controlledTags: "Image", // 验证规则 // 来自 LabelMixin labelActions: Object, // 标签相关操作 // 来自 SelectedModelMixin selected: boolean, select(): void, unselect(): void, // ...选中状态管理 }6.MST 类型系统的优势
// 1. 强类型检查 const model = RectangleLabelsModel.create({ type: "rectanglelabels", children: ["label", "text"] // ❌ 错误:'text' 不在允许的列表中 }); // 2. 运行时验证 const model = RectangleLabelsModel.create({ type: "rectangle", // ❌ 错误:type必须是"rectanglelabels" }); // 3. 自动序列化 const snapshot = getSnapshot(model); // 输出:{ pid: "abc123", type: "rectanglelabels", ... } // 4. 不可变更新 const newModel = applySnapshot(model, { ...snapshot, x: 100 }); // 5. 响应式观察 autorun(() => { console.log(model.selected); // 自动追踪依赖 });7.完整的类型继承链示例
// 假设各个基础模型的定义: const ControlBase = types.model({ name: types.string, toName: types.string, }); const LabelsModel = types.model({ labels: types.array(LabelModel), }); const RectangleModel = types.model({ x: types.number, y: types.number, width: types.number, height: types.number, }); // 组合时相当于: const RectangleLabelsModel = types.model("RectangleLabelsModel", { // 来自 ControlBase name: types.string, toName: types.string, // 来自 LabelsModel labels: types.array(LabelModel), // 来自 ModelAttrs pid: types.optional(types.string, guidGenerator), type: "rectanglelabels", children: Types.unionArray(["label", "header", "view", "hypertext"]), // 来自 RectangleModel x: types.number, y: types.number, width: types.number, height: types.number, // 来自 Validation controlledTags: Types.unionTag(["Image"]), // 来自 LabelMixin 和 SelectedModelMixin 的方法 // ...actions, views, etc. }).actions(self => ({ // 来自各个模型的方法 fromControlBase() { /* ... */ }, fromLabelsModel() { /* ... */ }, fromRectangleModel() { /* ... */ }, }));总结
types是 MST 的类型系统核心,它提供了:
types.model() - 定义数据结构形状types.compose() - 组合多个模型(类似多重继承)类型检查器 - 确保数据符合定义
运行时验证 - 在创建和更新时验证数据
序列化支持 - 自动转换为纯JS对象
这种架构的优势:
✅强类型安全:减少运行时错误
✅代码复用:通过组合复用功能
✅可维护性:清晰的关注点分离
✅开发体验:优秀的TypeScript支持
✅性能优化:精确的响应式更新
这就是为什么 Label Studio 选择 MST 来构建其复杂的状态管理系统——它提供了类型安全、可组合性和响应式更新的完美结合。