装箱百万奖金,第六届全国工业互联网数据创新应用大赛火热报名中! 了解详情
写点什么

前端快速建⽴Mock App

  • 2020-04-04
  • 本文字数:2931 字

    阅读完需:约 10 分钟

前端快速建⽴Mock App

引言

作为前端开发者,有些时候我们在后端服务还未 ready 的时候就接到了紧急开发需求,面对数据接口的缺失和数据持久化的支持,开发举步维艰。当然,加班也许也是一种解决问题的方法,但如果我们能够自己动动手指头去解决这两个问题,那么前端开发者们不仅增进了对业务的了解还掌握了对数据接口定义的主动权,后期的联调时间成本也可以大大缩小。


本文适合前端开发者阅读,阅读时长 10 分钟。


开始

首先,我选用 Vue 全家桶来做这个 Mock App 的讲解,因为代码少、效率高。前端做数据持久化需要一个存数据的地方,有的读者可能对 localstorage 和 sessionstorage 比较熟悉,但它们的缺点如下:


  1. 缺少对结构化数据存储的优化,存取都需要调用 JSON.stringify 和 JSON.parse。

  2. 缺少对数据系统化的管理

  3. 缺少对数据查询的支持


由此可得 localstorage 和 sessionstorage 都不适合拿来解决我们的问题,而 WebSQL 已经寿终正寝,所以只有 IndexedDB 可选了。


INDEXEDDB 是一个嵌入在浏览器中的事务数据库。该数据库的管理围绕 JSON 对象集合的概念,这类似 NOSQL 数据库 MONGODB 与 COUCHDB。其中每个对象使用插入时生成的键标识。而索引系统优化对存储对象的访问。Wikipedia


决定了使用 IndexedD 做数据持久化方案,我推荐使用 Dexie.js 对 IndexedDB 进行操作。实际上,如果没有 Dexie.js 这层封装,我也不会想在前端做数据 mock。


所有的工具已经 ready,我们将使用经典的 message board 来作为业务模型,做一个只关注数据层面的 mock。


业务剖析

先上一张图, Message board 的业务逻辑很简单,用户先创建 Board(帖子),然后此用户或其它用户在 Board(帖子)里创建 Message(回复)。按照正常 BBS 的逻辑,用户未登录的时候也可以看到帖子,只是不能回复,这个特性也会在 Mock App 中体现。



由于 IndexedDB 和 MongoDB 很相似,所以我们可以直接将 User 直接保存在 Message 和 Board 的 author 中,而 Message 则使用 parentId+parentType 来建立和 Board 的关系。


Coding

定义数据库

使用 Vue cli 新建一个干净的项目,安装 dexie,在 src 文件夹下新建一个 db.ts 文件,放入数据库的 Schema


import Dexie from "dexie";
interface DBObject {[key: string]: any;}
const db: DBObject = new Dexie("myDb");db.version(1).stores({ users: `++id, name`, boards: `++id, topic, description, author`, messages: `++id, content, author, createdAt, parentId, parentType`})
复制代码


这里传入 stores 方法的 object 就是数据库的 schema 了。 keys 代表了数据库的表, value 中是以逗号分隔的 columns。一个比较特殊的是++id,它的意思是自增整形,并且它是作为表的主键存在的。其他的 columns 和业务剖析中的图一致,就不展开了。


定义 Mock API

我们只实现最小可用的 Mock App,只需实现以下一个 API:


  • createUser: 创建用户,用以登录应用

  • getUsers: 获取用户列表,方便我们切换用户

  • createDiscussion: 创建 Board,类似于创建一个帖子的概念

  • getDiscussions: 获取 Board 列表,类似于获取帖子列表的概念

  • getDisucssionMessage: 获取 Board 下的 Message 列表,类似于获取一个帖子下面所有回复的概念

  • createDiscussionMessage: 创建 Board 下的 Message,类似于在帖子下创建回复的回复的概念


在本文最后给出的项目代码中有它们的具体实现。这里只对 getUsers、 createUser 和 getDiscussionMessage 方法做讲解:


api.prototype.getUsers = function() {  return db.transaction("r", db.users, function() {    return db.users.toArray();  });};
复制代码


这个方法返回了一个 Promise, db.transaction 中第一个参数是”r”,熟悉 linux 权限系统的同学肯定知道了,这是 read 权限的意思,因为这个 getUsers 方法涉及到读数据库操作,所以这个 transaction 需要 read access。 第一个传入的参数是 db.users,它声明了 transaction 将建立和 Users table 的连接,而 function 中 return 的 db.users.toArray()则返回了 Users table 中所有的数据。


api.prototype.createUser = function(name: string) {  return db.transaction("rw", db.users, function() {    return db.users.add({ name: name });  });};
复制代码


这个方法同样返回了一个 Promise, db.transaction 中第一个参数是”rw”,也就是 read & write 的意思,因为这个 createUser 方法涉及到读数据库操作,所以它需要 write access。 db.users.add({name:name})则新建了一行用户数据,用户的 id 被自动补全。


api.prototype.getDisucssionMessage = function(discussionId:number) {  return db.transaction("r", db.messages, function() {    return db.messages.where({      parentId: discussionId,      parentType: "discussion"    }).toArray();  });};
复制代码


这个方法也返回了一个 Promise, function 中的 db.message.where 方法有点 SQL 的味道,它的作用你也猜到了,就是通过提供的过滤器(object)在 Messages table 中查询数据,然后返回所有符合过滤器筛选的结果。


定义 Vuex Store

我们会用 Vuex actions 把 API 管理起来,在 Angular 中也可以用 Service 达到同样的效果,管理起来的目的是当后端 API 开发完成的时候,我们可以很方便地迁移到新的 API 上,而不需要大量地变更已经写好的业务代码。


我们在 Vuex 的 actions 中建立和上文 Mock API 中相对应的方法,同时为了方便获取到用户登录的状态,我们可以在 getters 中加入 loginStatus,因为我们的多个层面需要判断用户是否登录,只有登录的用户才可以发言,未登录的用户只能查看讨论。


完成业务

Mock API 和 Vuex Store 都写好之后我们可以开始着手实现我们的业务逻辑。由于本身的业务实在太简单,我直接放源码了,这里不做展开。


假设我们完成了 Mock App 的编写,如何利用已完成的代码,尽量少地改动业务逻辑来适配后端的 API 呢?在 Vuex(或服务)中抽象出来的 API 就功不可没了,理想情况下我们只需要改变引入的 API 源(Angular 服务注入的时候可以用 UseClass)就能做到切换 API 的工作,因为我们的业务逻辑和 API 用什么技术方案实现的完全没关系!


事实上,基于这样的流程编写的 App 也能降低 Code Smell,促进应用与 API 的解耦,为更健壮、更具拓展性、更具可测性的 Code Base 打好基础。


总结

IndexedDB 赋予了开发者们即使没有后端的时候仍可以继续前端开发的能力,使用 Dexie 的 API 去管理 IndexDB 无疑减小了 IndexedDB 的使用门槛。同时,使用 Vuex、 Angular 这类具有服务注入能力的库/框架可以有效降低 API 源切换的时间成本和风险。


在前端给出需要的数据接口格式后,后端只需按要求提供相应数据即可。如果有扩展需求,只需在前端给出的接口格式上持续演进,而不用为了对接接口格式而扯皮。


在业务逻辑与 API 实现解耦之后,前端开发者可以有更多的时间去思考用户体验、编写单元测试,从而提高整个应用的鲁棒性、可用性和易用性。


最后需要强调的是,此文并不是说后端没用,因为后端提供的一些能力仍是前端远远不及的。本文的观点是,在前端做 Mock App 的这段时间内,后端可以有更充足的时间、空间去思考后端架构和实现、以更稳定的状态承载更大的总业务容量,从而为产品、公司和客户带来价值。


项目源码


2020-04-04 16:591267

评论

发布
暂无评论
发现更多内容

模块2作业

miliving

腾讯新闻基于 Flink PipeLine 模式的实践

腾讯云大数据

flink pipeline 流计算 Oceanus

和12岁小同志搞创客开发:手撕代码,做一款数字骰子

不脱发的程序猿

少儿编程 DIY 智能硬件 创客开发 Arduino

Thanos 架构剖析(一)Thanos 架构总览

耳东@Erdong

架构 Thanos 签约计划第二季

《PyTorch 深度学习实战》复习9

IT蜗壳-Tango

28天写作 12月日更

神工鬼斧惟肖惟妙,M1 mac系统深度学习框架Pytorch的二次元动漫动画风格迁移滤镜AnimeGANv2+Ffmpeg(图片+视频)快速实践

刘悦的技术博客

人工智能 机器学习 深度学习 PyTorch 图像处理

【Spring Boot 快速入门】六、Spring Boot集成Redis

小阿杰

redis SpringBoot 2 内容合集 签约计划第二季

Python Qt GUI设计:窗口之间数据传递(拓展篇—5)

不脱发的程序猿

Python qt PyQt GUI设计 窗口之间数据传递

Go 语言快速入门指南:第五篇 与数据为舞之切片

宇宙之一粟

golang slices 切片 签约计划第二季 12月日更

底层逻辑的生命力

卢卡多多

28天写作 12月日更

Thanos 架构剖析(五)历史数据怎么办

耳东@Erdong

Thanos 签约计划第二季 compactor

Volatile 原理七:volatile都不保证原子性,为啥我们还要用它

悟空聊架构

volatile 原子性 28天写作 悟空聊架构 12月日更

拆解&组合

圣迪

数据 创新 组合 拆解 要素

Go 语言快速入门指南:第四篇 与数据为舞之数组

宇宙之一粟

数组 签约计划第二季 12月日更

对上管理

张老蔫

28天写作

1-10聚合架构第十一讲:不服?那得治!

钰湚—付晓岩

Thanos 架构剖析(三)如何选择 Sidecar 和 Receiver

耳东@Erdong

Thanos 签约计划第二季 Sidecar Receiver

Go 语言快速入门指南:第三篇 流程控制

宇宙之一粟

for 流程控制 swith 签约计划第二季 if语句

学习源码整体架构系列 | 前端

若川

内容合集 签约计划第二季 技术专题合集

创业研发团队的氛围营造

wood

创业 管理 28天写作

Thanos 架构剖析(六)告警规则如何管理

耳东@Erdong

Thanos 签约计划第二季 Rule

阿里云 FaaS 架构设计与创新实践

阿里巴巴云原生

阿里云 Serverless 云原生 Faas

重学计算机组成原理(4)-还记得纸带编程吗?

JavaEdge

12月日更

和12岁小同志搞创客开发:手撕代码,做一款遥控灯

不脱发的程序猿

少儿编程 DIY 智能硬件 创客开发 Arduino

Thanos 架构剖析(二)统一的查询入口

耳东@Erdong

Thanos query 签约计划第二季 Query Frontend

Thanos 架构剖析(四)数据存储和运维工具

耳东@Erdong

store tools Thanos 签约计划第二季

广州站 | 云原生 Serverless 技术实践营精彩回顾

阿里巴巴云原生

阿里云 Serverless 云原生 线下活动

45 K8S之系统扩展CRD/自定义API Server

穿过生命散发芬芳

k8s 28天写作 12月日更

2020年净利暴涨1288%,遨森电商携手DataPipeline构建实时数据融合体系跑出加速度!

DataPipeline数见科技

数据库 大数据 中间件 数据融合 数据管理

【Spring Boot 快速入门】七、Spring Boot集成RabbitMQ

小阿杰

RabbitMQ SpringBoot 2 内容合集 签约计划第二季

你还在一个挨一个的删除镜像文件吗?

liuzhen007

28天写作 12月日更

前端快速建⽴Mock App_前端_华为云开发者联盟_InfoQ精选文章