Next2D Framework 是一个用于构建 Next2D Player 应用程序的 MVVM 框架。它提供单页应用程序(SPA)的路由、View/ViewModel 管理和配置管理。
本项目实现了 Clean Architecture 和 MVVM 模式的组合。
graph TB
subgraph ViewLayer["视图层"]
View["View"]
ViewModel["ViewModel"]
UI["UI 组件"]
end
subgraph InterfaceLayer["接口层"]
IDraggable["IDraggable"]
ITextField["ITextField"]
IResponse["IResponse"]
end
subgraph ApplicationLayer["应用层"]
UseCase["UseCase"]
end
subgraph DomainLayer["领域层"]
DomainLogic["领域逻辑"]
DomainService["Service"]
end
subgraph InfraLayer["基础设施层"]
Repository["Repository"]
ExternalAPI["外部 API"]
end
ViewLayer -.->|通过接口| InterfaceLayer
ViewLayer -.->|调用| ApplicationLayer
ApplicationLayer -.->|通过接口| InterfaceLayer
ApplicationLayer -.->|使用| DomainLayer
ApplicationLayer -.->|调用| InfraLayer
InfraLayer -.->|访问| ExternalAPI
| 层 | 路径 | 角色 |
|---|---|---|
| View | view/*, ui/* | 处理画面结构和显示 |
| ViewModel | view/* | View 和 Model 之间的桥梁,事件处理 |
| Interface | interface/* | 抽象层,类型定义 |
| Application | model/application/*/usecase/* | 业务逻辑实现(UseCase) |
| Domain | model/domain/* | 核心业务规则 |
| Infrastructure | model/infrastructure/repository/* | 数据访问,外部 API 集成 |
遵循 Clean Architecture 原则,依赖始终指向内部(领域层)。
my-app/
├── src/
│ ├── config/ # 配置文件
│ │ ├── stage.json # 舞台设置
│ │ ├── config.json # 环境设置
│ │ ├── routing.json # 路由设置
│ │ └── Config.ts # 配置类型定义和导出
│ │
│ ├── interface/ # 接口定义
│ │ ├── IDraggable.ts # 可拖动对象
│ │ ├── ITextField.ts # 文本字段
│ │ ├── IHomeTextResponse.ts # API 响应类型
│ │ └── IViewName.ts # 视图名称类型定义
│ │
│ ├── view/ # View 和 ViewModel
│ │ ├── top/
│ │ │ ├── TopView.ts # 画面结构定义
│ │ │ └── TopViewModel.ts # 业务逻辑桥梁
│ │ └── home/
│ │ ├── HomeView.ts
│ │ └── HomeViewModel.ts
│ │
│ ├── model/
│ │ ├── application/ # 应用层
│ │ │ ├── top/
│ │ │ │ └── usecase/
│ │ │ │ └── NavigateToViewUseCase.ts
│ │ │ └── home/
│ │ │ └── usecase/
│ │ │ ├── StartDragUseCase.ts
│ │ │ ├── StopDragUseCase.ts
│ │ │ └── CenterTextFieldUseCase.ts
│ │ │
│ │ ├── domain/ # 领域层
│ │ │ └── callback/
│ │ │ ├── Background.ts
│ │ │ └── Background/
│ │ │ └── service/
│ │ │ ├── BackgroundDrawService.ts
│ │ │ └── BackgroundChangeScaleService.ts
│ │ │
│ │ └── infrastructure/ # 基础设施层
│ │ └── repository/
│ │ └── HomeTextRepository.ts
│ │
│ ├── ui/ # UI 组件
│ │ ├── animation/ # 动画定义
│ │ │ └── top/
│ │ │ └── TopBtnShowAnimation.ts
│ │ │
│ │ ├── component/ # 原子设计
│ │ │ ├── atom/ # 最小单元组件
│ │ │ │ ├── ButtonAtom.ts
│ │ │ │ └── TextAtom.ts
│ │ │ ├── molecule/ # 组合的 Atom 组件
│ │ │ │ ├── HomeBtnMolecule.ts
│ │ │ │ └── TopBtnMolecule.ts
│ │ │ ├── organism/ # 多个 Molecule 组合
│ │ │ ├── template/ # 页面模板
│ │ │ └── page/ # 页面组件
│ │ │ ├── top/
│ │ │ │ └── TopPage.ts
│ │ │ └── home/
│ │ │ └── HomePage.ts
│ │ │
│ │ └── content/ # Animation Tool 生成的内容
│ │ ├── TopContent.ts
│ │ └── HomeContent.ts
│ │
│ ├── assets/ # 静态资源
│ │
│ ├── Packages.ts # 包导出
│ └── index.ts # 入口点
│
├── file/ # Animation Tool 输出文件
│ └── sample.n2d
│
├── mock/ # 模拟数据
│ ├── api/ # API 模拟
│ ├── content/ # 内容模拟
│ └── img/ # 图像模拟
│
└── package.json
使用 gotoView 函数的画面转换详细流程。
graph TD
User([用户]) -->|请求| GotoView[gotoView Path]
GotoView --> LoadingCheck{使用加载?<br/>默认: true}
LoadingCheck -->|是| ScreenOverlay[画面遮罩]
LoadingCheck -->|否| RemoveResponse
ScreenOverlay --> LoadingStart[开始加载]
LoadingStart --> RemoveResponse
RemoveResponse[移除前一个响应数据] --> ParseQuery[解析查询字符串]
ParseQuery --> UpdateHistory{SPA 模式?}
UpdateHistory -->|是| PushState[推送历史状态]
UpdateHistory -->|否| RequestType
PushState --> RequestType
RequestType[请求类型]
RequestType --> JSON[JSON: 获取外部 JSON 数据]
RequestType --> CONTENT[CONTENT: 获取 Animation Tool JSON]
RequestType --> CUSTOM[CUSTOM: 请求外部 API]
JSON --> CacheCheck{使用缓存?<br/>默认: false}
CONTENT --> CacheCheck
CUSTOM --> CacheCheck
CacheCheck -->|是| CacheData[(缓存)]
CacheCheck -->|否| GlobalData{{全球网络}}
CacheData --> Cached{已缓存?}
Cached -->|否| GlobalData
Cached -->|是| RegisterResponse
GlobalData --> RegisterResponse
RegisterResponse[注册响应数据] --> RequestCallback{请求回调?}
RequestCallback -->|是| ExecRequestCallback[执行请求回调]
RequestCallback -->|否| UnbindView
ExecRequestCallback --> UnbindView
UnbindView[前一个 View: onExit 和解绑] --> BindView[新 View/ViewModel: 绑定]
BindView --> ViewModelInit[ViewModel: initialize]
ViewModelInit --> ViewInit[View: initialize]
ViewInit --> AddToStage[将 View 添加到舞台]
AddToStage --> GotoViewCallback{gotoView 回调?}
GotoViewCallback -->|是| ExecGotoViewCallback[执行 gotoView 回调]
GotoViewCallback -->|否| LoadingEndCheck
ExecGotoViewCallback --> LoadingEndCheck
LoadingEndCheck{使用加载?<br/>默认: true}
LoadingEndCheck -->|是| LoadingEnd[结束加载]
LoadingEndCheck -->|否| OnEnter
LoadingEnd --> DisposeOverlay[释放画面遮罩]
DisposeOverlay --> OnEnter
OnEnter[View: onEnter] --> StartDrawing
StartDrawing[开始绘制] -->|响应| User
style User fill:#d5e8d4,stroke:#82b366
style StartDrawing fill:#dae8fc,stroke:#6c8ebf
style CacheData fill:#fff2cc,stroke:#d6b656
style GlobalData fill:#f5f5f5,stroke:#666666
| 步骤 | 说明 |
|---|---|
| gotoView | 画面转换的入口点 |
| Loading | 加载画面显示/隐藏控制 |
| Request Type | 三种请求类型:JSON、CONTENT、CUSTOM |
| Cache | 响应数据缓存控制 |
| View/ViewModel Bind | 新 View/ViewModel 的绑定过程 |
| onEnter | 画面显示完成后的回调 |
为每个用户操作创建专用的 UseCase 类:
export class StartDragUseCase
{
execute(target: IDraggable): void
{
target.startDrag();
}
}
依赖接口,而不是具体类:
// 好:依赖接口
import type { IDraggable } from "@/interface/IDraggable";
function startDrag(target: IDraggable): void
{
target.startDrag();
}
抽象数据访问并实现错误处理:
export class HomeTextRepository
{
static async get(): Promise<IHomeTextResponse>
{
try {
const response = await fetch(`${config.api.endPoint}api/home.json`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error("Failed to fetch:", error);
throw error;
}
}
}
npx create-next2d-app my-app
cd my-app
npm install
npm start
npm run generate
此命令解析 routing.json 中的顶级属性并生成相应的 View 和 ViewModel 类。
any 类型,使用显式类型定义