统一类型
模型层概念
模型层是项目的核心,它定义了项目中的数据结构和业务逻辑。模型层的设计是项目的基础,它决定了项目的扩展性和可维护性。
传统前端项目的问题
在传统的前端项目中,由于采用函数式编程,对于模型的定义本就模糊不清。而在使用 TypeScript 等类型系统的项目中,由于对于后端模型类型的缺失,导致了代码的可读性和可维护性下降。
全栈统一类型的解决方案
在SSR项目中,我们可以通过定义统一的模型层来解决这个问题。统一的模型层可以定义项目中的数据结构和业务逻辑,从而提高代码的可读性和可维护性。
统一类型的优点
统一类型的优点主要有以下几个方面:
- 提高代码的可读性和可维护性
- 提高代码的可扩展性
- 提高代码的可测试性
统一类型的实现
统一类型的实现主要分为以下几个步骤:
- 定义统一的模型层
- 定义统一的模型层的类型
- 定义统一的模型层的接口
- 定义统一的模型层的方法
- 定义统一的模型层的实例
技术细节
基于 Type Script 对于 class
的支持,借鉴OOB开发的思想基础,将文章类型及其各种变种类型抽象成类,通过 class
同时定义类型和初始值。
具体表现为:
- 通过
extends
继承的方式,实现了类型的复用,从而提高了代码的可维护性。 - 通过
interface
接口的方式,实现了API的参数和返回值约束,抹平前后端类型差异,从而保证了数据的一致性。 - 通过
type
类型的方式,在前端获取类型,提供完善的补全和校验的开发体验。 - 通过
new class
类的方式,实现了数据的初始化,提高项目的统一性和鲁棒性。
具体的类型定义位于 models
目录下,例如:
typescript
// models/article.ts
// 响应体
export interface ResOptions<T> {
data: T;
code: number;
message: string;
}
// 访客三元组:访问量、点赞、评论
export class VisitorTernary {
protected views: number = 0;
protected likes: number = 0;
protected comments: number = 0;
}
// Posts
export class Post extends VisitorTernary {
protected id: number = 0;
protected classify_id: number = 1;
protected title: string = "";
protected date: string = "";
}
// 文章
export class Article extends Post {
article_id: number = this.id;
article_classify_id: number = this.classify_id;
article_title: string = this.title;
article_date: string = this.date;
article_views: number = this.views;
article_likes: number = this.likes;
article_comments: number = this.comments;
article_title_image: string = "";
article_text: string = "";
article_copyright: string = "";
article_keywords: string = "";
article_private: number = 0;
article_password: string = "";
has_password: boolean = false;
}
// 文章列表
export class ArticleList {
list: Article[];
total: number = 0;
constructor() {
this.list = [new Article()];
}
}
// 获取文章列表接口
export interface ApiIndex {
params: {
page_numer?: number;
page_size?: number;
classify_path?: string | null;
};
result: ResOptions<ArticleList>;
}
在Nuxt3的 server
后端目录中使用:
typescript
// server/api/article/index/index.post.ts
import { article } from "@@/models";
type ApiIndexModelType = article.ApiIndex;
type Article = article.Article;
export default defineEventHandler(async (event) => {
// 具体的接口实现......
// 假设从数据库中获取的数据为 dbResults,包装或处理时,指定item的类型为 Article
dbResults.data.forEach((item: Article) => {
if (item.article_text.length > 60) {
item.article_text = item.article_text.substring(0, 60) + "...";
}
});
// API返回的数据类型为 ApiIndexModelType["result"]
return setJson(
{ data: { list: dbResults.data, total: total } },
dbResults,
) as ApiIndexModelType["result"];
});
在Nuxt3的 app
前端目录中使用:
typescript
// server/api/article/index/index.post.ts
import { options } from "@@/models";
import { article } from "@@/models";
// 使用type关键字获取class的类型
type ResOptions<T> = options.ResOptions<T>;
type ResOptionsModelType<T> = ResOptions<T>;
type ArticleListModelType = article.ArticleList;
// 使用时,指定result的类型为 ArticleListModelType,并通过new关键字初始化,设置初始值
const articleListData = ref<ArticleListModelType>(new article.ArticleList());
const showArticleList = (
result: ResOptionsModelType<ArticleListModelType>,
) => {
articleListData.value = result.data;
};