如何优雅处理「快应用」数据请求

查阅快应用开发文档,可以知道官方提供了数据请求接口;对于如何使用,文档中也给出了简单的说明和代码示例,但很显然,这在实际项目中,不够科学优雅、且更不高效,所以需要对其进行再封装,使得可以大幅提升开发效率,同时也令整个代码优雅,以便于维护。所以在此篇文章的存在,旨在于讨论下如何优雅处理「快应用」数据请求

如何优雅处理快应用数据请求

备注: 本文最先发布于,基于 Ghost 构建的最新博客: 静轩之别苑

为保证文中的代码,是直接拷贝就可以运行的,特有引用网络开源接口;需要备注说明的是,在快应用使用接口,需要在 manifest.json 中,对所使用的接口进行声明;使用数据请求,就需要注入下面的声明:

{ "name": "system.fetch" }

默认的低效率写法

import $fetch from '@system.fetch'
$fetch.fetch({
  url: 'https://api.apiopen.top/singlePoetry',
  responseType: 'text',
  success: function (response) {
    const result = response.data
    console.log(`success response, code: ${result.code}, data: ${result.data}`)
  },
  fail: function (data, code) {
    console.log(`fetch handling fail, code = ${code}`)
  },
  complete: funtion (data, code) {
    console.log(`fetch handling complete, code = ${code}`)
  }
})

以上就是官方文档提供的上古 jQuery 时代的写法,冗长的代码,古老的回调式操作,以及对请求没有做任何必要的处理,如果这在项目中使用,对代码复用度无疑是零,从长期维护角度看,这样的代码就是导致痛苦的根源。虽然,示例代码这样写道,也无可厚非;但先入为主的模范作用,将对于诸多经验不够充足的开发者,起到错误的误导性引领。

改进后的用法

import $fetch from '@system.fetch'
$fetch.fetch({
  url: 'https://api.apiopen.top/singlePoetry',
  method: 'GET'
}).then(response => {
    const result = response.data
    console.log(`success response, code: ${result.code}, data: ${result.data}`)
}).catch(error => {
  console.log(`Something Error: ${error}`)
})

其实,快应用对部分接口方法调用,返回了 Promise 优化,其中就包括数据请求接口;所以,使用如上这种方式,至少代码显得不那么冗长而古老。但,数据请求相关代码,却依然没有得到复用,这就需要对其进行封装处理。

优化处理数据请求

如果每次调用接口,都需要 import,外加指定 url,method 等参数,这无异是变相的浪费生命;大道至简,优秀的开发流程,一定是便于编写和维护!所以有必要将其统一封装,如下面所封装的代码(可放置在 helpera/ajax.js 路径下):

function requestHandle(params) {
  return new Promise((resolve, reject) => {
    $fetch.fetch({
      url: params.url,
      method: params.method,
      data: params.data
    }).then(response => {
      const result = response.data
      const content = JSON.parse(result.data)
      /* @desc: 可跟具体不同业务接口数据,返回你所需要的部分,使得使用尽可能便捷 */
      content.code === 200 ? resolve(content.result) : resolve(content.message)
    }).catch((error, code) => {
      console.log(`🐛 request fail, code = ${code}`)
      reject(error)
    })
  })
}

/* 此处只是处于示例代码的可运行性,实际项目中,此方法最好予以封装 & 提取 */
function queryString(url, query) {
  let str = []
  for (let key in query) {
    if (typeof query[key] === 'object') {
      query[key] = JSON.stringify(query[key])
    }
    str.push(key + '=' + query[key])
  }
  let paramStr = str.join('&')
  return paramStr ? `${url}?${paramStr}` : url
}

export default {
  post: function(url, params) {
    return requestHandle({
      method: 'post',
      url: url,
      data: params
    })
  },
  get: function(url, params) {
    return requestHandle({
      method: 'get',
      url: queryString(url, params)
    })
  }
}

这 $fetch.fetch 返回本就是一个 Promise,这里为何额外要包裹的一层处理呢?这样做的好处不仅在于使其依旧可以链式调用,同时对返回数据统一处理,精简返回内容,使得在获取到最终结果处,可以尽可能简单,更加有利于后期做维护,调用方式就可以成为如下这样(涉及某类模块,可统一在相应模块文件下,如:helper/apis/poetry.js ):

import $ajax from './../ajax'
const baseUrl = 'https://api.apiopen.top/'

export default {
  getSinglePoetry(data) {
    return $ajax.get(`${baseUrl}singlePoetry`, data)
  },
  getOtherApi(data) {
    // other api .... 
  }
}

另外,开发者将接口,按照功能模块规划,分门别类以存放至统一文件夹下,如 helper/apis;如此清晰明了,方便调用,且对于多人协作开发,又不相互响应,减少不必要的冲突。类似善用配置,以表驱动法的编程手法,应该活学活用,贯穿始终。这些理念,早在更优雅的处理-Http-请求 | 开箱即用的 Vue Webpack 脚手架模版中就有阐述。

至此,就对接口进行了完美封装处理;在业务层便捷调用,也是需要优化;在快应用,最为高效的办法,就是将上面封装暴露给 global,如此就可以:

import { $apis } from './helper'
const hook2global = global.__proto__ || global
hook2global.$apis = $apis

// 在任何其他页面、组件、js 文件,接可以像如下调用
const params = {}
$apis.poetry.getSinglePoetry(params).then(result => {
    // 处理正常逻辑
}).catch(error => {
    // 处理请求异常逻辑
})

更近一步体验优化

在业务逻辑中,发起数据请求时候,都需要添加 Loading,提示用户请求正在进行中,以免响应缓慢给用户带来不必要的疑惑;而,在请求成功或失败情形下,都需要对 Loading 进行消除,如此一来就会有下面的逻辑:

$apis.poetry.getSinglePoetry(params).then(result => {
    this.isLoading = false
    // 处理正常逻辑
}).catch(error => {
    // 处理请求异常逻辑
    this.isLoading = false
})

很明显,像类似不管请求成功或失败,都需要执行的业务逻辑是存在的,如果分别在对应链式后做处理,谈何优雅呢?在 ES2018 有引入 finally 标准,跟快应用中请求后 complete 回调是一样的作用:不管最后状态如何,都会执行的操作。所以上面的调用,就可以优化成如下代码:

$apis.poetry.getSinglePoetry(params).then(result => {
    // 处理正常逻辑
}).catch(error => {
    // 处理请求异常逻辑
}).finally(() => {
    this.isLoading = false
})

当按照预期这样写的时候,你会发现 finally 链式并未得到调用。查阅一番,兴许你也会得到一个答案,快应用规范没有 finally,如此一来,就不得不打补丁 (polyfill) 来予以解决了,可以有的途径不少,下面介绍一种简单无依赖的法子,注入以下代码即可;那么上面关于 ajax.js 的封装则可以优化成如下代码:

Promise.prototype.finally = function (callback) {
  const P = this.constructor
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  )
}

function requestHandle(params) {
  return new Promise((resolve, reject) => {
    $fetch.fetch({
      url: params.url,
      method: params.method,
      data: params.data
    }).then(response => {
      const result = response.data
      const content = JSON.parse(result.data)
      /* @desc: 可跟具体不同业务接口数据,返回你所需要的部分,使得使用尽可能便捷 */
      content.code === 200 ? resolve(content.result) : resolve(content.message)
    }).catch((error, code) => {
      console.log(`🐛 request fail, code = ${code}`)
      reject(error)
    }).finally(() => {
      console.log(`✔️ request @${params.url} has been completed.`)
      resolve()
    })
  })
}

需要补充说明的是,finally 方法指定的回调函数,用于指定不管 Promise 对象最后状态如何,都会执行的操作;它是与状态无关的,不依赖于 Promise 的执行结果,所以此处 polyfill 返回跟标准一致,回调函数不接受任何参数。

关于快应用数据请求的整体代码优化设计,具体可参见 Github 项目:quickapp-boilerplate-template: 🔨致力于构建更为优雅的「快应用」开发脚手架模板。

至此,对处理「快应用」数据请求,相比开发文档中所写到的示例,是不是优雅很多呢?如果你的项目中,涉及数据请求有超过 3 个的可能,那么你就应该做像着样。当然,这不仅仅限于此接口,其他如数据存储(storage),上传下载(request),你都应该予以封装。这也不仅仅限于快应用,其他如开发 VueReact 等项目,亦是同理。

@2019-01-17 于深圳.福田 Last Modify:2018-01-19


您可能会感兴趣的文章:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容