传送地址:
前端设计模式之工厂模式
前端设计模式之代理模式
前端设计模式之策略模式
设计模式已经连载到了第 3 期,由于下一期的观察者模式要配合团队的埋点性能博客一起上要暂缓一段时间,所以这一篇算彩蛋篇,推出一个开箱即用型的 fetch 项目实战。
未封装之前的 fecth 如下使用
fetch('https://www.baidu.com/search/error.html') // 返回一个Promise对象 .then((res) => { return res.text() // res.text()是一个Promise对象 }) .then((res) => { console.log(res) // res是最终的结果 }) 复制代码
如上是直接使用 fecth 的方法,但在项目中直接引用会有很多不便的地方,所以我们先简单封装一下,比如跨域配置、超时、各种请求等等的配置。
import qs from 'qs' class Fetch { constructor(config = {}) { this.config = { cache: 'no-cache', // * default, no-cache, reload, force-cache, only-if-cached credentials: 'same-origin', // include, same-origin, *omit headers: {}, mode: 'cors', // no-cors, cors, *same-origin redirect: 'follow', // manual, *follow, error referrer: 'no-referrer', // *client, no-referrer timeOut: 3000, BASE_URL: '', ...config } } send({ url, params, method = "GET", headers }) { // 发送 ajax 请求 const { BASE_URL } = this.config const ajax = new Promise((resolve) => { fetch(BASE_URL ? `${BASE_URL}/${url}` : url, { ...this.config, body: params, headers, method, }).then((response) => { return response.json() }).then((data) => { resolve(data) }) }) // 设置超时时间 const time = new Promise((reject) => { console.log(this.config.timeOut) setTimeout(() => { reject('time out') }, this.config.timeOut); }) return Promise.race([ajax, time]) } // 封装请求 get({ url, query, headers }) { return this.send({ url: `${url}?${qs.stringify(query)}`, headers, method: 'GET' }) } post({ url, params, headers }) { return this.send({ url, params, headers, method: 'POST' }) } } const newFetch = new Fetch(); newFetch.get({ url: 'https://api.github.com/users/octocat', params: { test: 1 } }).then(data => { console.log(data) }).catch(err => { console.log(err) }) 复制代码
如上我们简单的封装了一个可以发送 get、 post 请求的 fetch 类,加入了超时跟网关,简单的项目可以随便用起来了,但我们既然要做到开箱即用,那就根据实际的项目发生的情况,再进一步的定制。
this.dataOperation = { JSON: { headers: { 'Content-Type': 'application/json', // 告诉服务器,我们提交的数据类型为 json 格式 }, formatting(params) { return JSON.stringify(params) } }, FormData: { headers: { 'Content-Type': 'application/x-www-form-urlencoded' // 告诉服务器,我们提交的数据类型为 FormData 格式 }, formatting(params) { let _formData = new FormData(); Object.keys(params).forEach(key => { _formData.append(key, params[key]); }) return _formData } } } preSend({ url, params, headers, method }) { const { requestType } = this.config const FetchConfig = { ...this.FetchConfig, method, headers: { ...this.dataOperation[requestType].headers, ...headers }, }; if (!!params) FetchConfig.body = this.dataOperation[requestType].formatting(params); return this.send({ url, FetchConfig }) } post({ url, query, params = {}, headers }) { return this.preSend({ url: query ? `${url}?${qs.stringify(query)}` : url, params, headers, method: 'POST' }) } 复制代码
如上,我们根据策略模式 + 代理模式将发送请求报多包了一层,这样我们可以在初始化的时候,选择项目请求参数类型,一般来说一个项目并不会使用多种请求类型,所以我们暂不提供请求参数类型的方法传参配置,简化我们请求方法的参数数量。
一般来说,正常的 get 请求可以配置 304 缓存等,但是从网络通信请求质量跟减少请求数量来说,当数据数据过大或者请求数量过多的时候,从本地直接读取数据的性能提升会更加有效果,所以我们可以将之前代理模式里面的缓存实战例子结合 get 请求封装起来,一起搭配使用
get({ url, query, headers }) { // 优化 get 请求,添加缓存处理 const key = query ? `${url}?${qs.stringify(query)}` : url if (this.cacheStorage) { if (this.cacheStorage.getItem(key)) { return Promise.resolve(this.cacheStorage.getItem(key)) } else { return this.preSend({ url: key, headers, method: 'GET' }).then(data => { this.cacheStorage.setItem(key, data) return data }) } } else { return this.preSend({ url: key, headers, method: 'GET' }) } } 复制代码
这边的缓存封装,是在初始化工具类的时候就已经把缓存类型跟缓存时间一起初始化完成了,这样比较方便后续业务方使用,如果全部放在参数里面的话,灵活性提高,但是方法使用成本会提高。
根据之前的项目经验总结一下业务侧的使用:
如上已经简单的封装了一个开箱即用的 fetch 工具类,完整的 demo 地址:项目实战demo,喜欢的朋友可以 star 一下,但是具体项目实战涉及的内容还是差的比较多,比如上传功能,数据缓存的内存溢出、请求发送前后的拦截器等,后续会根据设计模式博文的推出,逐步的将此项目继续拓展出来,仅供学习参考。
如对文章内容以及实例有任何疑问、见解可随时与我沟通。你不点个赞吗O(∩_∩)O~
前端早早聊大会目标成为用得上、听得懂、抄得走的技术大会,计划 2020 年办 >= 15 期,由前端早早聊与掘金联合举办,前端早早聊大会行程动态、录播视频/PPT/讲稿资料下载请关注 「前端早早聊」 公众号跟进。
你的支持,是早早聊办下去的唯一动力!
点击保存下图,转发朋友圈即可,越多人参与,越多讲师愿意来分享!
还想听哪方面的分享,直接加 Scott 微信: codingdreamer 提需求吧!
7 月 18 日举办第十二届 - 前端搞可视化,报名请戳:www.huodongxing.com/go/tl12 ,海报及讲师行程如下: