🌙 自定义组件开发指南
Editor 支持开发自定义组件来扩展功能。组件分为三类:
- Object 组件:用于显示数据(如
Image,Text,Markdown) - Control 组件:用于标注控制(如
Number,Choice,Rectangle) - Visual 组件:用于视觉布局(如
View,Header)
🌙 组件开发步骤
🌙 1. 创建组件文件
根据组件类型,在相应目录下创建文件:
- Object 组件:
src/tags/object/YourComponent.jsx - Control 组件:
src/tags/control/YourComponent.jsx - Visual 组件:
src/tags/visual/YourComponent.jsx
🌙 2. 定义 Model(数据模型)
使用 mobx-state-tree 定义组件的数据模型:
import { types } from "mobx-state-tree";
import Base from "./Base"; // 或 control/Base.js
import { AnnotationMixin } from "../../mixins/AnnotationMixin";
import ProcessAttrsMixin from "../../mixins/ProcessAttrs";
const Model = types
.model({
type: "your-component", // 组件类型标识
value: types.maybeNull(types.string), // 组件属性
// 添加其他属性...
})
.actions((self) => ({
// 定义操作方法
updateValue(store) {
// 处理数据更新逻辑
},
}));
// 组合 Mixins
const YourComponentModel = types.compose(
"YourComponentModel",
Base,
ProcessAttrsMixin,
AnnotationMixin, // Object 组件需要
Model
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
🌙 3. 创建 View(React 组件)
创建 React 组件来渲染 UI:
import { inject, observer } from "mobx-react";
import { YourComponentView } from "../../components/YourComponent/YourComponentView";
// 使用 inject 注入 store,observer 使其响应式
const HtxYourComponent = inject("store")(observer(({ item }) => {
return <YourComponentView item={item} />;
}));
1
2
3
4
5
6
7
2
3
4
5
6
7
或者直接内联实现:
import { inject, observer } from "mobx-react";
const HtxYourComponent = inject("store")(observer(({ item }) => {
return (
<div className="your-component">
{/* 组件 UI */}
</div>
);
}));
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
🌙 4. 注册组件
使用 Registry 注册组件:
import Registry from "../../core/Registry";
// 注册标签
Registry.addTag("your-component", YourComponentModel, HtxYourComponent);
// 如果是 Object 组件,还需要注册对象类型
Registry.addObjectType(YourComponentModel);
1
2
3
4
5
6
7
2
3
4
5
6
7
🌙 5. 导出组件(可选)
如果组件需要在其他地方使用,导出它:
export { HtxYourComponent, YourComponentModel };
1
🌙 完整示例:Markdown 组件
参考 src/tags/object/Markdown.jsx 的实现:
import { inject } from "mobx-react";
import { types } from "mobx-state-tree";
import Registry from "../../core/Registry";
import { AnnotationMixin } from "../../mixins/AnnotationMixin";
import ProcessAttrsMixin from "../../mixins/ProcessAttrs";
import Base from "./Base";
import { parseValue } from "../../utils/data";
import { MarkdownPreview } from "../../components/MarkdownPreview/MarkdownPreview";
// 1. 定义 Model
const Model = types
.model({
type: "markdown",
value: types.maybeNull(types.string),
valuetype: types.optional(types.enumeration(["text", "url"]), "text"),
encoding: types.optional(
types.enumeration(["none", "base64unicode"]),
"none"
),
_content: types.maybeNull(types.string),
})
.actions((self) => ({
updateValue(store) {
const parsed = parseValue(self.value, store.task.dataObj);
// 处理数据逻辑...
self._content = parsed;
},
}));
// 2. 组合 Mixins
const MarkdownModel = types.compose(
"MarkdownModel",
Base,
ProcessAttrsMixin,
AnnotationMixin,
Model
);
// 3. 创建 View
const HtxMarkdown = inject("store")(MarkdownPreview);
// 4. 注册组件
Registry.addTag("markdown", MarkdownModel, HtxMarkdown);
Registry.addObjectType(MarkdownModel);
export { HtxMarkdown, MarkdownModel };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
🌙 常用 Mixins
Base/ControlBase:基础功能AnnotationMixin:标注相关功能(Object 组件必需)ProcessAttrsMixin:处理属性值解析PerRegionMixin:支持按区域标注RequiredMixin:必填验证ReadOnlyControlMixin:只读模式
🌙 组件开发注意事项
🌙 1. 命名规范
- Model 名称:
YourComponentModel - View 组件名称:
HtxYourComponent(Htx 前缀表示 HyperText) - 文件命名:使用 PascalCase(如
YourComponent.jsx)
🌙 2. 类型定义
- 使用
types.maybeNull()处理可选值 - 使用
types.optional()设置默认值 - 使用
types.enumeration()限制枚举值
🌙 3. 数据解析
- 使用
parseValue()解析配置中的$variable引用 - 在
updateValue()方法中处理数据更新
🌙 4. 响应式更新
- View 组件必须使用
observer()包装 - 使用
inject("store")注入 store - Model 中的属性变化会自动触发 View 更新
🌙 5. 样式处理
- 创建对应的
.scss文件 - 使用 CSS Modules 或 BEM 命名规范
- 注意 CSS 前缀(默认
lsf-)
🌙 测试自定义组件
🌙 1. 在配置中使用
<View>
<YourComponent name="comp-1" value="$data" />
</View>
1
2
3
2
3
🌙 2. 运行开发服务器
yarn lsf:serve
1
🌙 3. 编写单元测试
在 src/tags/object/__tests__/ 或 src/tags/control/__tests__/ 下创建测试文件
🌙 参考资源
🌙 现有组件示例
- Object 组件:
src/tags/object/Markdown.jsx- Markdown 显示组件 - Control 组件:
src/tags/control/Number.jsx- 数字输入组件 - 简单组件:
src/tags/control/Rating.jsx- 评分组件
🌙 核心文件
src/core/Registry.ts- 组件注册机制src/tags/object/Base.js- Object 组件基类src/tags/control/Base.js- Control 组件基类src/mixins/- 各种功能 Mixins
🌙 Mixins 详细说明
AnnotationMixin(src/mixins/AnnotationMixin.js) - 提供标注相关功能ProcessAttrsMixin(src/mixins/ProcessAttrs.js) - 处理属性值解析和数据解析PerRegionMixin(src/mixins/PerRegion.js) - 支持按区域标注RequiredMixin(src/mixins/Required.js) - 必填验证ReadOnlyControlMixin(src/mixins/ReadOnlyMixin.js) - 只读模式支持
🌙 常见问题
🌙 Q: 组件注册后无法使用?
A: 确保:
- 组件文件已被正确导入(在
src/tags/object/index.js或相应索引文件中) - 使用
Registry.addTag()注册了组件 - Object 组件还需要调用
Registry.addObjectType()
🌙 Q: 组件样式不生效?
A: 检查:
- 样式文件是否正确导入
- CSS 类名是否正确使用
- 是否有 CSS 前缀冲突(默认
lsf-)
🌙 Q: 组件数据不更新?
A: 确保:
- View 组件使用了
observer()包装 - Model 中的属性变化是通过 MST actions 进行的
- 使用了
inject("store")注入 store
🌙 Q: 如何调试组件?
A:
- 使用浏览器开发者工具查看组件状态
- 在 Model 的 actions 中添加
console.log调试 - 使用 React DevTools 查看组件树
- 检查 MobX State Tree 的 snapshots
🌙 最佳实践
- 保持组件简单:每个组件只负责一个功能
- 复用现有组件:查看是否有类似的组件可以复用或扩展
- 遵循命名规范:保持与现有组件一致的命名风格
- 编写测试:为新组件编写单元测试和集成测试
- 文档化:在组件文件中添加 JSDoc 注释,说明组件的用途和参数