注:文章所写内容在星云中得到了实践
项目结构
通过 monorepo 架构来组织,Monorepo 架构是一种将多个项目(通常是相互关联的项目)存放在一个代码库(Repository)中的方法,我们可以借助 monorepo 实现
- 共享代码和依赖:所有项目在同一个仓库中,因此可以轻松共享和复用代码,例如公共的工具库、组件等,避免了重复开发。
- 一致性和版本控制:所有项目的代码版本一致,可以统一管理依赖的版本并快速查看整个项目的版本变动。 我们考虑使用 pnpm 的 workspace 来实现,当然也可以使用 Lerna 等其他
关注点分离
- 将逻辑层和视图层分离
考虑到视图层期望支持不同的UI框架(例如react/vue),以及视图层有较强的定制性(每个公司都希望自己的产品不要一眼就看出是使用了某某框架)
故而,我们将逻辑层单独抽成npm 包,而可复用的UI层则抽成npm包里相对独立的小组件,大的面板则交由客户自己开发。 - 通过不同的业务类型分离
我们将,表单/大屏/门户/流程等等不同的业务的核心逻辑,抽取到不同的npm包中,这样用户用到哪个模块,只需要安装对应的包即可
核心对象
核心的对象,我们放在了 core 包中,我们抽取了 project 、document、node、props, children 等各个业务都需要的通用对象,如果不同业务需要对其扩展,我们可以通过继承这个类并注册新的类来实现
如何实现较高的扩展性
常见的扩展方式:
1.插件的扩展思想
function middleware1() {}
function middleware2() {}
function middleware3() {}
const plugins = [middleware1, middleware2, middleware3]
run() // (run函数会依次执行所以注册的插件), 通过添加自定义的插件,来实现对默认逻辑的扩展
2.面向对象的继承思想
class Animal {
eat() {}
}
class Person extend Animal {
work() {}
}
// 通过继承来实现对 Animal 的扩展
这两种基础的方式,一类是函数式的思想,一类是面向对象的思想
我们采取了两者的结合,在面向对象的基础上,增加许多hook,hook本身则是插件的容器,可以插入自定义的逻辑。
// Project 本身是一个对象,提供基础的能力,可以继承它来添加自定义的方法或属性
export class Project<T extends Services = Services> {
// 这个 hooks 是一个插件容器,我们可以通过加入不同的插件来对某些流程插入自定义的工作
hooks = projectHooks as ProjectHooks<T>
constructor() {
makeObservable(this)
}
@action
createDocument(schema?: DocumentSchema, index = -1): T['Document'] {
const doc = this.documentFactory(this, newSchema)
if (index === -1) {
this.documents.push(doc)
} else {
this.documents.splice(index, 0, doc)
}
// 执行 onDocumentsChange 这个hooks里所有注册的插件
// 意味着可以在 创建doc 之后加入自定义的逻辑
this.hooks.onDocumentsChange.call({ documents: this.documents })
return doc
}
}
状态驱动UI
我们使用mobx来作为状态库,mobx是围绕响应式编程创建的库,更加关注状态的自动同步和响应式特性。mobx和vue很像,而且是框架无关的,这意味着在任意框架中都可以接入。
MobX 会自动追踪 observable 数据和它们的依赖关系,确保当数据变化时,UI 自动更新,而无需手动设置监听或通知。
export class Project<T extends Services = Services> {
// 通过 mobx 的语法 监听documents的数组变化
@obx.shallow documents: T['Document'][] = []
constructor(
) {
makeObservable(this)
}
@action
removeDocument(index: number) {
// 直接移除指定的document
this.documents.splice(index, 1)
// a after hook
this.hooks.onDocumentsChange.call({ documents: this.documents })
}
}
import React from "react";
import { observer } from "mobx-react";
import { project } from "mobx-react";
// 通过包裹 observer 监听所有组件内部用到的依赖
const Counter = observer(() => (
<div>
// 此处可以实时渲染 doc 的数量
{project.documents.length}
</div>
))