代码不分层会是怎么样的?
相信不少小伙伴都有做过离线功能。那位各位看官的代码是否是如下这样的呢?如果是这样,这么恭喜你!请务必往下看,一定会有所收获的。
import Dexie from 'dexie'; // 这个是indexDB库
const createTodo = async (todo: { text: string }) => {
if (isOnline) {
// 在线,走http请求
await axios.post('/todo', { todo })
// ...
} else {
// 离线代码
const db = new Dexie('database')
db.version(1).stores({
todos: 'text'
});
await db.table('todos').add({ todo });
}
}
如上代码,这样写代码问题就在于,每次调接口的时候都需要判断是否离线,然后针对不同的情况写不同的代码。而且在业务复杂的情况下这会使得createTodo
十分臃肿,进而导致整个代码文件十分臃肿。
如何分层
那么如何写才是更好的呢?有nodejs服务端开发经验的小伙伴一定对以下代码比较熟悉:
import Router from 'koa-router'
import Koa from 'koa'
const app = new Koa()
const router = new Router()
router.post('/todo', async (ctx) => {
const { todo } = ctx.request.body
await todoService.createTodo(todo)
ctx.body = 'ok'
})
如上代码所示,这是nodejs服务端开发十分常见的代码。
那么试想一下,如果在前端中也能够实现router
对象,拥有get/post等方法;同时再实现一个request
对象,拥有向这个router
发送消息的能力(比如发布订阅),那么传统前端代码就能与离线服务代码进行分层处理了。
说到这里就需要插播一条广告了[手动狗头]。offline-request是我开发的一个完整的离线服务代码分层解决方案的工具(希望觉得不错的小伙伴可以给个star[再次手动狗头])。如何实现就请看源码吧,下面说一下具体如何使用:
- 首先创建offlineRequest对象
// offline-request.ts
import OfflineRequest from 'offline-request'
import Axios from 'axios'
// 第二个参数接受一个函数,要求函数返回boolean,true代表走在线模式,false代表走离线模式
// 也可以是异步函数
export const offlineRequest = new OfflineRequest(Axios.create(), () => {
return false;
});
- 然后写离线服务代码
// server.ts
import { offlineRequest } from './offline-request';
import { todoController } from './todos';
export const runServer = () => {
const db = new Dexie("test_database");
todoController(db, offlineRequest.server);
}
// todos.ts
import Dexie from 'dexie';
import { OfflineRequestServer } from 'offline-request';
export const todoController = (db: Dexie, router: OfflineRequestServer) => {
db.version(1).stores({
todos: 'text'
});
router.put('/todos', async (ctx) => {
const { data } = ctx.request;
await db.table('todos').add(data);
ctx.body = { // 存储成功后给前端一个返回值
data: 'ok',
status: 200,
statusText: 'ok'
};
});
}
- 接着写前端代码
// TodoList.tsx
import { FC, useState, ChangeEvent, useRef, useEffect } from 'react'
import { offlineRequest } from './offline-request'
export const TodoList: FC = () => {
const createTodo = async (todo: { text: string }) => {
// offlineRequest会根据创建的时候传入的第二个参数(函数)的返回值判断是走离线还是在线
// 走离线模式就会向offlineRequest.server(也就是router)对象发送消息,并把todo参数传递过去
// 离线服务端可以通过ctx.request.data拿到todo对象然后进行存储
// 走在线模式就相当于执行了 await axios.post('/todos', todo)
await offlineRequest.put('/todos', todo)
}
return <>
...
</>
}
- 最后,让在离线服务代码跑起来就ok了
// index.tsx
import ReactDOM from 'react-dom'
import App from './App'
import { runServer } from './server'
ReactDOM.render(<App />, document.getElementById('root'))
runServer()
这样离线服务代码的分层就完成了。在简单的业务场景下这看起来会更麻烦一点,但是当业务逻辑足够复杂时分层的作用将大大的显现出来。不仅能让代码看起来没那么臃肿,可读性也会好很多,可维护性也更高。同时也不需要在每个调接口的地方判断是走离线还是在线(当判断逻辑比较复杂的情况下,每次做这个判断也是比较繁琐的)。
另外当离线先服务的业务逻辑足够复杂的时候,你还可以在离线服务代码内部进行分层,类似于后端的services/controller等。
总结
offline-request实现的核心概念就是发布订阅模式,通过发布订阅模式模拟了类似http通讯。