前文传送地址:
前端设计模式之工厂模式
前端设计模式之代理模式
前端设计模式之策略模式
前端设计模式之装饰模式
前端设计模式|封装fetch(1)
连续四篇设计模式都是前端经常使用到的,相信大家参考博文中的项目实战之后再去琢磨自己的项目代码、或者看一些优秀的开源代码对比后,能更深刻的体会到设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结这句话的含义。
(小声BB,某人的埋点博客终于快到尾声了)
在开发的过程中,同一种功能采用不同的或者组合的设计模式实现,可以将代码质量提升。这里要 copy 第一篇博文的话重申一下为什么前端需要了解设计模式
在日常开发中大部分前端都在开发的中,进行组件、方法等封装、提炼的时候或多或少已经使用了一些设计模式的理念, 但是由于对设计模式的概念模糊,理解不够,从而导致设计整体架构的时候,会有各种局限性,拓展性、可读性、维护性变差,不得不多次重构甚至重写。在 ts 在前端开发中加速推进的同时,合理的设计模式使得项目从架构、设计、迭代、维护都有一定质量的保障。
接下来我们通过使用设计模式中的工厂、代理模式来继续改造我们的 fetch 工程
业务层的封装,我们在上一篇的文末已经提到过,这边再结合代码展示一下
一般来说我们的工程会有多个模块,这边我们先根据各个模块封装一层 service 层,方便我们业务侧调用。
import Fetch from './util/fetch'; const prefix = 'https://api.github.com/users' const fetch = new Fetch({ requestType: "JSON", cacheType: 'local', BASE_URL: prefix }); const getUser = (params) => { return new Promise((resolve, reject) => { fetch.get({ url: '/octocat', params }).then(response => { const { data, code, errMessage } = response if (code) { resolve(data) } else { reject(errMessage) } }) }) } const setUser = (params) => { return new Promise((resolve, reject) => { fetch.post({ url: '/octocat', params }).then(response => { const { data, code, errMessage } = response if (code) { resolve(data) } else { reject(errMessage) } }) }) } export { getUser, setUser }; export default { getUser, setUser } 复制代码
如上我们封装了一个用户 service 层,一般来说,除了正常的 http 的请求状态异常之外,会有业务处理的异常。所以在 service 层调用的时候,可以预先处理掉错误的异常,返回给业务侧正常的数据,业务侧在调用的时候,可以直接使用 try/catch 去承接数据。同时在多个业务侧都需要调用相同的接口的时候,可以在用户 service 层处理、过滤一些后台返回的参数,这样可以使得业务侧调用到方便前端展示的数据(比如组装列表数据,日期、金额格式化等)。
但是当业务过多,都要处理统一的业务错误的时候,会显得非常麻烦,造成冗余代码跟维护困难,所以在这之上,我们可以在针对 service 层再做一层业务报错封装。
import Fetch from '../util/fetch'; const prefix = 'https://api.github.com' const fetch = new Fetch({ requestType: "JSON", cacheType: 'local', BASE_URL: prefix }); const get = (url, params) => { return new Promise((resolve, reject) => { fetch.get({ url, params }).then(response => { const { data, code, errMessage } = response if (code) { resolve(data) } else { reject(errMessage) } }) }) } export { get }; export default { get } 复制代码
如上可以将业务层的统一处理跟单独的业务接口数据处理分开,用户的 service 层改造如下
import { get, post } from './baseFetch'; const prefix = 'users' const getUser = async (params) => { try { const data = get({ url: `${prefix}/octocat`, params }) return data } } const setUser = (params) => { try { const data = post({ url: `${prefix}/octocat`, params }) return data } } export { getUser, setUser }; export default { getUser, setUser } 复制代码
有些后台接口是按照 restful 风格封装的接口,如果还是按照上面的封装,会显得比较累赘,我们可以如下封装一下
import baseFetch from './baseFetch'; const prefix = 'users' const methods = ['get', 'post', 'put', 'delete'] const userUrl = { users: 'octocats', user: 'octocat' } const user = {} Object.keys(userUrl).forEach(key => { user[key] = {} methods.forEach(method => { user[key][method] = (params) => { return baseFetch[method](`${prefix}/${userUrl[key]}`, params) } }) }) export { user }; export default { user } user.user.get({ test: 1 }) // 业务侧调用 user.user.post({ test: 1 }) // 业务侧调用 复制代码
如上我们将 url 跟请求方法组装起来,业务侧调用会简便很多,但是中间的业务层数据处理似乎就没了,我们可以将 userUrl 拓展成 userObj,把数据处理方法也放入对象里面,改造如下
const userObj = { users: { url: 'octocats', get(data) { return data } }, user: { url: 'octocats', post(data) { return data } } } const user = {} Object.keys(userObj).forEach(key => { user[key] = {} methods.forEach(method => { user[key][method] = (params) => { return new Promise((resolve) => { baseFetch[method](`${prefix}/${userObj[key].url}`, params).then(data => { if (userObj[key][method]) resolve(userObj[key][method](data)) resolve(data) }) }) } }) }) 复制代码
完整的 demo 地址:项目实战demo,喜欢的朋友可以 star 一下,后续会根据设计模式博文的推出,逐步的将此项目继续拓展出来,仅供学习参考。
另外有个疑问,由于后期的项目实战想引入真实的案列,不知道语言偏向于 vue 还是 react,采用哪种来写更好?留言或者加微信都行。
如对文章内容以及实例有任何疑问、见解可随时与我沟通。你不点个赞吗O(∩_∩)O~
前端早早聊大会目标成为用得上、听得懂、抄得走的技术大会,计划 2020 年办 >= 15 期,由前端早早聊与掘金联合举办,前端早早聊大会行程动态、录播视频/PPT/讲稿资料下载请关注 「前端早早聊」 公众号跟进。
你的支持,是早早聊办下去的唯一动力!
点击保存下图,转发朋友圈即可,越多人参与,越多讲师愿意来分享!
还想听哪方面的分享,直接加 Scott 微信: codingdreamer 提需求吧!
7 月 18 日举办第十二届 - 前端搞可视化,报名请戳:www.huodongxing.com/go/tl12 ,海报及讲师行程如下: