JavaScript:像wx.cloud.database一样封装前端数据库操作
前天呢去公司见了一下未来的同事~看了一下现在的业务代码,发现了其中一个有趣的操作。即在前端向统一一个接口做数据库操作,我当时蛮震惊(我本以为的直接字符串SQL语句这样)。后来看了一下发现还是通过request请求的方式完成,不过现有封装是在一个函数中一次条件一次请求,回想小程序的云开发数据库请求,这不是现成的封装例子么!搞起来!
项目使用了click house作为数据库,封装上要和mongoDB有些不同;
1 抽象
要做数据库操作,逃不开“增删改查”四大密字。
我们先定义一个DataBase接口
- DataBase 包含大部分方法的基类
interface DataBaseInterface {
form: null | DBForm;
collection(name: string): DataBase;
clear(): void;
where(condition: WhereCondition): DataBase;
limit(val: number): DataBase;
orderBy(name: string, sort: OrderBySort): DataBase;
skip(val: number): DataBase;
groupBy(name: string): DataBase;
get(): Promise<DataBaseRes>;
}
出于安全性(和简便)考虑,这里只做查询封装。
这其中类似where(condition: WhereCondition): DataBase;
每次调用后会返回本身,实现链式调用。
可以看到,上方还定义了DBForm,WhereCondition,OrderBySort,DataBaseRes
这些接口,这些接口的实现如下:
- DBForm 定义了最终发送请求的payload中的数据项
interface DBForm {
orderBy?: Array<OrderBy>;
where?: WhereCondition;
collection: string;
limit?: number;
groupBy: Array<string>;
skip?: number;
}
interface OrderBy {
name: string;
order: OrderBySort;
}
a new interface——OrderBy
这里定义collection是必选项
- WhereCondition 条件接口
interface WhereCondition {
[key: string]: any;
}
由于还没有写到,先用any代替;
- OrderBySort
OrderBySort实际上是TS的枚举对象
enum OrderBySort {
asc,
desc,
}
- DataBaseRes 返回结果
enum DBcode {
success,
fail,
}
interface DataBaseRes {
code: DBcode;
source: any;
}
2 基本实现
2.1 构造与析构
db的构造是简便的,目前在开始的时候创建form=null即可
class DataBase implements DataBaseInterface {
form: DBForm | null;
constructor() {
this.form = null;
}
clear() {
this.form = null;
}
collection(name: string) {
this.form = Object.create(null);
this.form!.collection = name;
return this;
}
}
2.2 基本功能实现
2.2.1 where
按照接口约定,where函数应该返回一个DataBase,并接收一个WhereCondition;
where(condition: WhereCondition) {
if (this.form?.where) {
throw new Error("already existing[where]");
}
this.form!.where = condition;
return this;
}
2.2.2 limit&skip
这两应该是最简单滴,limit和skip需要接收一个整数。
limit(val: number) {
if (this.form?.limit) {
throw new Error("already existing[limit]");
}
this.form!.limit = val;
return this;
}
skip(val: number) {
if (this.form?.skip) {
throw new Error("already existing[skip]");
}
this.form!.skip = val;
return this;
}
2.2.3 orderBy
orderBy的话,应该按照先后顺序进行排序
如此的话还是使用了数组,虽然对象也是有序的
orderBy(name: string, order: OrderBySort) {
if (!this.form) throw new Error("none connect");
if (!this.form.orderBy) {
this.form.orderBy = [];
}
this.form.orderBy.push({ name, order } as OrderBy);
return this;
}
和上边不同的是,orderBy可以进行叠加,多次调用增加排序条件
2.2.4 groupBy
groupBy(name: string): DataBase {
if (!this.form) throw new Error("none connect");
if (!this.form.groupBy) {
this.form.groupBy = [];
}
this.form.groupBy.push(name);
return this;
}
groupBy可以进行叠加
2.2.5 get
当我们调用get,才真正发送请求,我这里仅把结果返回进行查看
const sleep = async (ms: number) =>
new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, ms);
});
async get() {
await sleep(500);
const form = { ...this.form };
this.clear();
return { code: DBcode.success, source: form } as DataBaseRes;
}
3 测试
我们调用上边的get的话:
const demo = async () => {
const db = new DataBase();
const res = await db
.collection("UserInfo")
.where({
name: "BugYalu",
})
.limit(1)
.get();
console.log(res);
};
打印结果如下
当然我们也可以真的把get封装一下,那它就会像这样
补一份总代码
import { axiosBase } from "./apis";
const sleep = async (ms: number) =>
new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, ms);
});
enum DBcode {
success,
fail,
}
interface DataBaseRes {
code: DBcode;
source: any;
}
interface DataBaseInterface {
form: null | DBForm;
collection(name: string): DataBase;
clear(): void;
where(condition: WhereCondition): DataBase;
limit(val: number): DataBase;
orderBy(name: string, sort: OrderBySort): DataBase;
skip(val: number): DataBase;
groupBy(name: string): DataBase;
get(): Promise<DataBaseRes>;
}
interface WhereCondition {
[key: string]: any;
}
enum OrderBySort {
asc,
desc,
}
interface OrderBy {
name: string;
order: OrderBySort;
}
interface DBForm {
orderBy?: Array<OrderBy>;
where?: WhereCondition;
collection: string;
limit?: number;
groupBy: Array<string>;
skip?: number;
}
class DataBase implements DataBaseInterface {
form: DBForm | null;
constructor() {
this.form = null;
}
clear() {
this.form = null;
}
collection(name: string) {
this.form = Object.create(null);
this.form!.collection = name;
return this;
}
where(condition: WhereCondition) {
if (this.form?.where) {
throw new Error("already existing[where]");
}
this.form!.where = condition;
return this;
}
limit(val: number) {
if (this.form?.limit) {
throw new Error("already existing[limit]");
}
this.form!.limit = val;
return this;
}
skip(val: number) {
if (this.form?.skip) {
throw new Error("already existing[skip]");
}
this.form!.skip = val;
return this;
}
orderBy(name: string, order: OrderBySort) {
if (!this.form) throw new Error("none connect");
if (!this.form.orderBy) {
this.form.orderBy = [];
}
this.form.orderBy.push({ name, order } as OrderBy);
return this;
}
groupBy(name: string): DataBase {
if (!this.form) throw new Error("none connect");
if (!this.form.groupBy) {
this.form.groupBy = [];
}
this.form.groupBy.push(name);
return this;
}
async get() {
await sleep(500);
const form = { ...this.form };
this.clear();
axiosBase.post("/hhh", { ...form });
return { code: DBcode.success, source: form } as DataBaseRes;
}
}
export { DataBase };