mirror of
https://github.com/motajs/template.git
synced 2026-05-02 20:31:11 +08:00
81 lines
4.8 KiB
Markdown
81 lines
4.8 KiB
Markdown
# 魔塔样板开发说明
|
||
|
||
## 项目结构
|
||
|
||
`public`: mota-js 样板所在目录。
|
||
`packages`: 核心引擎代码 monorepo。
|
||
`packages-user`: 用户代码 monorepo。
|
||
`src`: 游戏入口代码。
|
||
|
||
`packages` `packages-user` 可以单独打包为库模式,`src` 单向引用 `packages-user`,`packages-user` 单向引用 `packages`,`src` 为游戏的入口代码。
|
||
|
||
## 开发环境
|
||
|
||
- `node.js ^20.0.0 || >=22.0.0`
|
||
- `pnpm >= 10.0.0`
|
||
- 任意支持 `ESNext` 特性的浏览器
|
||
|
||
**建议使用 `vscode`,搭配 `prettier` `eslint` 插件**
|
||
|
||
## 开发说明
|
||
|
||
1. 将项目拉取到本地。
|
||
2. 运行 `pnpm i` 安装所有依赖,如有要求运行 `pnpm approve-builds`,请允许全部。
|
||
3. 运行 `pnpm dev` 进入开发环境。
|
||
|
||
## 构建说明
|
||
|
||
- `pnpm build:packages`: 构建所有 `packages` 文件夹下的内容,使用库模式。
|
||
- `pnpm build:game`: 构建为可以直接部署的构建包。
|
||
- `pnpm build:lib`: 构建所有 `packages` `packages-user` 文件夹下的内容,使用库模式。
|
||
- `pnpm type`: 对仓库执行类型检查
|
||
- `pnpm check:circular`: 对仓库执行循环引用检查
|
||
|
||
## 开发原则
|
||
|
||
- 模块原则:
|
||
- 无副作用原则:所有模块不包含副作用内容,全部由函数、类、常量的声明组成,不出现导出的变量声明、代码执行内容,允许但不建议编写类的静态块。
|
||
- 如果需要模块初始化,编写一个 `createXxx` 函数,然后在 `index.ts` 中整合,再逐级向上传递,直至遇到包含 `create` 函数的 `index.ts`,所有初始化将会统一在顶层模块中执行。
|
||
- 不允许一个文件导出不属于当前 `monorepo` 或当前文件夹的内容。
|
||
- 不允许出现循环引用,如果不得不进行循环引用,应当首先考虑接口设计是否有问题。
|
||
- 命名规则:
|
||
- 变量、成员、一般常量、方法、函数使用小驼峰。
|
||
- 类、接口、类型别名、命名空间、泛型、枚举、组件使用大驼峰。
|
||
- 不变常量使用全大写命名法,单词之间使用下划线连接。
|
||
- 专有名词缩写如 `HTTP`, `URI` 全部大写。
|
||
- 会被 `implements` 的接口使用大写 `I` 开头。
|
||
- `id`, `class` 等 `HTML/CSS` 内容使用连字符命名法。
|
||
- 不使用下划线命名法。
|
||
- 注释:
|
||
- 常用属性成员、方法、接口、类型必须添加 `jsDoc` 注释。
|
||
- 长文件可使用 `#region` 分段,可以写上 `#endretion` 允许折叠。
|
||
- TODO 使用 `// TODO:` 或 `// todo:` 格式。
|
||
- 单行注释的双斜杠与注释内容之间添加一个空格,多行注释只允许出现 `jsDoc` 注释,如果需要多行非 `jsDoc` 注释,使用多个单行注释。
|
||
- 注释进行合理换行,考虑到中文字符较宽,建议 40-60 个字符进行换行。不允许在句中换行,必须在标点符号后换行。参数注释换行后保持对齐。
|
||
- 单行注释结尾不添加句号,对于多行长注释,可以在结尾添加句号。
|
||
- 类型:
|
||
- 不允许出现非必要的 `any` 类型。
|
||
- 所有类的成员必须显式声明类型。
|
||
- 如果有无法避免出现类型错误的地方,使用 `// @ts-expect-error` 标记,并填写原因。
|
||
- 没用到的变量、方法使用下划线开头。
|
||
- 合理运用 `readonly` `protected` `private` 关键字。
|
||
- 函数不建议使用过多可选参数,如果可选参数过多,可以考虑换用对象。
|
||
- 尽量少地使用 `as` 关键字进行类型断言,一般情况下不建议进行任何 `as` 类型断言,除非必要。
|
||
- 其他要求:
|
||
- 严格遵循 `eslint` 配置,不允许出现 `eslint` 报错。
|
||
- 尽量不使用 `?.` 运算符,一般建议仅在副作用函数调用(如 `this.obj?.func()`,`this.obj.func?.()`),或对象 `Required` 化(如 `{ value: obj?.value ?? 0 }`)中使用 `?.` 运算符。
|
||
- 只进行必要的非空判断,不必要的非空判断直接使用非空断言 `!` 实现。
|
||
- 除非参数要求传入函数等情况,不建议在函数内写任何局部函数。
|
||
- 语句尽量不换行,除非必要,尤其注意三元运算符与 `private readonly` 类成员。
|
||
|
||
## 双端分离
|
||
|
||
样板将渲染端与数据端彻底分离,数据端可以单独在 `node` 环境运行,可以直接用于录像验证。渲染端仅负责向数据端发送消息,不负责任何逻辑运算。
|
||
|
||
- `@user/data-base`: 数据端的系统层,负责核心系统。
|
||
- `@user/data-state`: 数据端的实现层,依靠系统层实现完整的游戏实例。
|
||
- `@user/client-base`: 渲染端的系统层,负责渲染端的核心系统。
|
||
- `@user/client-modules`: 渲染端的实现层,依靠系统层实现客户端的渲染与用户交互。
|
||
|
||
数据端允许运行渲染端代码,但需要使用全局接口 `Mota.r(() => {})` 包裹。除非必要,否则不建议在数据端调用渲染端代码。
|