人人都该具有封装思惟:Taro+Es6+Promise+Ts简易构建微信小程序的全局请求方法

不管是app仍是小程序或者其余端,交互中请求无处不在。node

一个优秀的封装类,能让你的开发效率事半功倍,因此封装逻辑相当重要,固然我也是个小菜鸟,跟着本身的思路写过一些封装方法,一方面是想不足之处还望路过的大神给予指正,两者是为新手打开一个善于封装思惟的大门,下面进入到前置知识。react

Api:cnodejs.org/api/v1es6

Ts: 简易的类型声明、接口等小程序

Es6:结构赋值、扩展运算、promise、export等api

Taro:类react,以及小程序基础知识等promise


1.梳理Taro的请求 Taro.request,实现最简易的请求方法bash

import Taro from '@tarojs/taro'
//回调调用
function doRequestAction(){
  Taro.request({
    url: '',
    data: {},
    header: {},
    success: res => {},
    fail: err => {},
    complete: info => {}
  })
}
// promise调用
function doRequestAction(){
  Taro.request({
    url: '',
    data: {},
    header: {}
  })
  .then(res=>{})
  .catch(err=>{})
  .finally(()=>{})
}
复制代码

其中利弊很少作介绍,咱们将会用到promise的请求方式。网络

2.梳理会用到的请求结构,以及定义的文件分类:app

  • 接口地址,也就是咱们常说的url,存放于api.ts文件中
  • 请求方法,也就是咱们封装的方法,咱们存放于request.ts文件中
  • 接口类型,也就是咱们声明数据类型的文件,咱们存放于inter.d.ts文件中
  • 配置文件,经常使用的全局请求域名,其余不动参数等,咱们这里只是简单的示例所以不须要,若是有须要请你们自行配置
文件名 做用
api.ts 存放接口地址、以及统一请求域名前缀
request.ts 封装公共请求方法、以及调用方法集合
inter.d.ts ts的声明文件,主要存放返回值类型,请求参数类型等

3.接入promise声明本身的方法并返回本身的promise请求对象ide

我这里尽可能写es6写法让你们在从此的项目开发中更加顺畅的使用,固然在本身的项目中请结合实际状况使用,不要盲目的接入一些新的写法。下面进入知识点梳理:

  • 请求是否loading,真 ? 结束隐藏loading : ' '
  • loading层是否开启mask
  • loading文字参数可自定义
  • 请求失败是否弹出咱们的报错信息
  • url在不以http/https的状况下使用自定义接口
import Taro from '@tarojs/taro'
// 暂时考虑 req的属性都会传入
const doRequestAction = (req) => {
  return new Promise((resolve, reject) => {
    if (req.loading) Taro.showLoading({ title: req.title ? req.title : '数据加载中...' })
    Taro.request({
      url: /^http(s?):\/\//.test(req.url) ? req.url : '', //暂时留空
      method: 'POST',
      data: {},
      header: { 'content-type': 'application/x-www-form-urlencoded' }
    })
    .then(res => {})
    .catch(err => { 
        //报错提示 
    })
    .finally(() => {
      if (req.loading) Taro.hideLoading()
    })
  })
}
复制代码

4.分离请求参数并使用ts声明传入值与类型

1.将使用到的参数进行分离

2.每一个参数给出默认值,若是不传人将用默认值代替

3.使用ts声明参数类型

export interface RequestBase {
  url: string, //字符串
  method: 'POST' | 'GET', //常规请求方式,根据项目要求添加
  data: any, // 每次的参数都是不固定的,所以咱们暂时不声明数据类型
  header: RequestHeader, // 下面的requestheader类型,
  loading: boolean, // 请求是否开启loading层
  mask: boolean, //开启loading层的状况下是否不能点击,全屏遮罩
  title: string, //开启loading层的提示内容
  failToast: boolean //若是请求失败,我是否直接弹出个人提示
}

export interface RequestHeader {
  'content-type': string // 表示content-type类型必须声明
}
复制代码

上面的header,我从新定义了一个接口来声明类型,是为了更方便的去管理这个数据,试想若是咱们平时须要将用户的token带入到header里面,那么咱们就会在RequestHeader中在声明一个token字段。

所谓的接口也就至关于咱们这个数据里面有什么字段,字段是什么类型。

因此,咱们在header中在加入token字段,实际项目中,可能还会带入加密串,时间,以及其余的辅助验证字段,这里只为了你们方便开发作出示例,实际还需看项目声明。

特殊提醒:ts是能够不加逗号的

export interface RequestHeader {
  'content-type': string // 表示content-type类型必须声明
  token: string
}
复制代码

声明咱们的默认参数,在用户没有参数传入的状况下,将会使用咱们的默认参数来辅助请求。

// 使用默认参数,当数据不传入指定字段时替代
const NormalRquestData: RequestBase = {
  url: api.DOMAIN, // 默认请求地址
  method: 'GET', // 默认get请求
  header : { // 默认使用的header头
    "content-type": 'application/x-www-form-urlencoded',
    token: ''
  },
  data: {}, // 默认没有参数,传入空对象
  loading: true, //默认开启loading层
  mask: true, //请求时不须要点击
  title: '数据加载中', //loading提示文字
  failToast: false // 通常咱们会处理相应业务逻辑,就不直接提示阻断流程
}
复制代码

思考问题,咱们若是每次动态 带上咱们token? 用户刚开始进入小程序,这时多是没有受权,后面受权了咱们要及时更新token来达到用户交互目的 这时咱们能够将header提出,固然我通常会在个人状态管理中操做,这里作个例子为你们提供思路。

  • 将token做为可选字段
  • 封装方法每次请求动态提取token
  • 方法返回类型为咱们定义的RequestHeader类型
// inter.d.ts
export interface RequestHeader {
  'content-type': string // 表示content-type类型必须声明
  token?: string // token可能不存在,若是存在就是字符串类型
}
//request.ts 获取header头 返回值是RequestHeader类型
const getRequestHeader = (): RequestHeader => {
  let token: string = Taro.getStorageSync('token')
  return token ? {
    "content-type": 'application/x-www-form-urlencoded',
    token: token
  } : {
    "content-type": 'application/x-www-form-urlencoded'
  }
}
复制代码

5.在参数某些字段不传入的状况下,咱们使用本身的参数字段进行默认填充

思考问题:RequestBase参数都是必传,可是咱们的请求的时候参数都是可传,若是咱们将其都改成可选参数,这个时候若是咱们使用req.title(loading标题)、req.url('请求api地址') 在ts的检测下,这些参数都是可能不存在的,这样咱们会写大量判断,那么咱们的代码就会变得至关糟糕~!所以咱们再声明一个接口用来可选参数的规范。

// 遍历RequestBase中全部key,都改成可选参,这样咱们就只管维护RequestBase
type Request = {
  [K in keyof RequestBase]?: RequestBase[K]
}
复制代码

改造请求方法,并声明各个类型,使ts更加规范, 将接口类使用inter导入,也将以前的api改为Api统一首字母大写

import Taro from '@tarojs/taro'
import * as Api from './api'
import * as Inter from './inter.d'


// 请求传入reqData参数   返回promise对象 由于全局请求我每次返回的类型都是不同的,因此我直接any
const doRequestAction = (reqData: Inter.Request): Promise<any> => {
  // 将不存在的参数字段使用默认值进行替换
  let req: Inter.RequestBase = { ...NormalRquestData, ...reqData }
  return new Promise((resolve, reject) => {
    //检测是否开启loading层 是否打开msak
    if (req.loading) Taro.showLoading({ title: req.title, mask: req.mask })
    Taro.request({
      url: req.url, //引入个人接口是特殊声明的,因此我就不检测http/https了
      method: req.method,
      data: req.data,
      header: req.header
    })
    .then(res => {
      // 大多数请求中 success并不表明成功,须要咱们本身检测statusCode来确保
      if (res.statusCode === 200) {
        resolve(res.data) // 成功
      } else {
        // 若是失败 检测是否直接提示信息
        if(req.failToast) Taro.showToast({ title: '网络很差,请求失败!' })
        reject(res) // 失败
      }
    })
    .catch(err => {
      // 若是失败 检测是否直接提示信息
      if (req.failToast) Taro.showToast({ title: '网络很差,请求失败!' })
      reject(err)
    })
    .finally(() => {
      // 请求结束 关闭loading层
      if (req.loading) Taro.hideLoading()
    })
  })
}
复制代码

ok,请求方法写到这里咱们暂时只能告一段落了

6.完善api.ts,声明全局域名以及每一个接口的定义.

//定义全局请求地址,由于咱们用到的地址是https://cnodejs.org/api/v1
export const DOMAIN: string = 'https://cnodejs.org/'
// 声明获取主题首页接口地址并导出
export const topics: string = DOMAIN + 'api/v1/topics'
复制代码

7.观察api,声明请求data文件,以及使用请求并返回promise以及返回类型

返回类型:cnodejs.org/api/v1/topi… 本身观察,我就不截图了。

// 请求主题接口参数类型
export interface TOPICSDATA {
  page: number,
  tab: 'ask' | 'share' | 'job' | 'good',
  limit: number,
  mdrender?: boolean
}
// 获取主题的接口
export interface TOPICS {
  id: string,
  author_id: string,
  tab: string,
  content: string,
  title: string,
  last_reply_at: string,
  good: boolean,
  top: boolean,
  reply_count: number,
  visit_count: number,
  create_at: string,
  author: TOPICSAUTHOR
}
// 做者的类型
export interface TOPICSAUTHOR {
  loginname: string,
  avatar_url: string
}
复制代码

8.自定义获取方法,结合本身的请求方法返回新的promise对象

// 调用封装方法 返回promise对象 获得获取到的数据
const getTopics = (data: Inter.TOPICSDATA): Promise<Inter.TOPICS> => {
  return doRequestAction({
    url: Api.topics,
    data: data
  })
}
复制代码

9. 页面内能够调用getTopics方法拿到咱们的数据

import { getTopics } from '../../utils/request/request'
import { TOPICSDATA } from '../../utils/request/inter'

useEffect(()=>{
    let data: TOPICSDATA= {
      page: 1,
      tab: 'ask',
      limit: 10
    }
    getTopics(data).then(res=>console.log(res))
  },[])
复制代码

topics数据.png

至此,一个简易的封装就完美的结束了,可是在实际开发中为了本身的便利,咱们会封装不少常用到的参数,这里只是提供一个封装思惟,具体还须要在你们的项目中去思考怎么才能去优化代码。

10.总结

知识点

  • es6语法: ...展开运算, 解构赋值, promise
  • ts:类型声明, 可选项, type , any类型 ,单个类型定义
  • 封装思惟,文件拆分

代码链接:pan.baidu.com/s/1DZ9u7U1f… 密码:t7rc

最后若是这篇文章对您有用,麻烦您点亮一个赞,感激涕零,若是代码中有好的意见或建议,还望不吝赐教。

相关文章
相关标签/搜索