【小程序】请求与封装

【原文:请求与封装】

微信小程序进行网络通信,只能和指定的域名进行通信,微信小程序包括四种类型的网络请求。

请求方法
普通HTTPS请求wx.request
上传文件wx.uploadFile
下载文件wx.downloadFile
WebSocket通信wx.connectSocket

这里主要对普通HTTPS请求做一次详细介绍。

普通HTTPS请求

要微信小程序进行网络通信,必须先设置域名,不然会提示不合法。设置域名的信息可以在开发者工具-详情-域名信息中看到。

使用wx.request可以发起一个http请求

wx.request({
  url: 'test.php', // 开发者服务器接口地址
  data: { // 请求的参数
    x: '',
    y: ''
  },
  method: 'GET', // HTTP 请求方法, 默认GET
  header: { // 设置请求的 header
    'content-type': 'application/json', // 默认值
    'cookie': 'token=' + token
  },
  dataType: 'json', // 返回的数据格式, 默认json
  responseType: 'text', // 响应的数据类型, 默认text
  success (res) { // 接口调用成功的回调函数
    console.log(res.data)
  },
  fail () {
    // 接口调用失败的回调函数
  },
  complete () {
    // 接口调用结束的回调函数(调用成功、失败都会执行)
  } 
})

请求封装

但在平时项目使用中,我们往往会对HTTP请求做一层封装。这里介绍两种方法:1. 回调函数 2. promise

第一种 回调函数

假设项目中有一个文件为http.js, 里面存在如下代码

// http.js
var api_base_url = 'pay.tairanmall.com'
var token = 'some code as token'
/**
 * 传入params对象,url, data, method, failBack, successBack
 **/
export default function (params){
    if(!params.method){
        params.method = "GET"
    }
    wx.request({
        url: api_base_url + params.url,
        method: params.method,
        data: params.data,
        header:{
            'content-type':'application/json',
            'cookie': 'token=' + token
        },
        success:function(res){
            let code = res.statusCode.toString()
            if (code.startsWith('2')){
                params.successBack && params.successBack(res.data)
            }
            else{
                params.failBack && params.failBack(res.data)
                wx.showToast({
                  title: res.data.error.description,
                  icon: 'none',
                  duration: 2000
                })
            }
        },
        fail:function(err){
          // some fail code
        }
    })

}

你在api.js文件中,引入http.js文件,代码如下

// api.js
import http from 'http.js'

function getOrderDetailData(params,successBack,failBack) {
  http({
    method: 'POST',
    url: '/some/url',
    data: params,
    successBack: successBack,
    failBack: failBack
  })
}

export { getOrderDetailData } 

之所以不在页面中直接引入引入http.js,而加了一层api.js文件,是为了项目的规范性和可维护性等方面考虑的。把所有的http请求地址集中在一个文件中,能够更加直观看到项目使用了哪些接口,使得后续的项目开发维护以及项目优化迭代更加容易。(举例:接口更换)

最后在某个页面中使用 home/home.js

// home/home.js
import { getOrderDetailData } from 'api.js'
Page({
  /**
   * 页面的初始数据
   */
  data: {
    payId: '', // 支付订单ID
    orderDetail: {}, // 订单详情
  },
  /**
   * 页面显示的时候
   */
  onShow: function () {
    // 请求获取订单信息
    this.getOrderDetail({ payId: this.data.payId})
  },

  /**
   * 获取订单信息
   */
  getOrderDetail: function (params) {  
    let _this = this
    getOrderDetailData(params, function (data) { 
      _this.setData({orderDetail: data})
    })
  }
})

上述的代码还是比较清晰的,但是当项目变得臃肿且业务代码繁杂的时候,过多的回调函数常常会使得代码难以理解和维护。 代码中每一层的回调函数都需要依赖上一层的回调执行完,形成层层嵌套的关系,就会产生回调地域。类似如下代码

import { getA, getB, getC, getD } from 'api.js'

function callback () {  
  getA({id: 'A'}, function (Adata) { 
    getB({id: Adata.id}, function(Bdata){
      getC({id: Bdata.id}, function(Cdata) {
        getD({id: Cdata.id}, function() {
          ...
        })
      })
    })
  })
}

上述代码无疑是不利于我们阅读与维护的,接下来介绍使用PROMISE。

第二种 PROMISE

首先先介绍一下Promise,以下摘自阮一峰老师的《ES6入门》

它是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。 简单说它就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。 Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。 有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

假设项目中有一个文件为http.js, 里面存在如下代码

// http.js
var api_base_url = 'pay.tairanmall.com'
var token = 'some code as token'

export default function ({url, data={},method='GET'}) {
        return new Promise((resolve, reject)=>{
          wx.request({
            url: api_base_url + url,
            method: method,
            data: data,
            header:{
                'content-type':'application/json',
                'cookie': 'token='+token
            },
            success:(res)=>{
                const code = res.statusCode.toString()
                if (code.startsWith('2')){
                    resolve(res.data)
                }
                else{
                    reject(res.data)
                    wx.showToast({
                      title: res.data.error.description,
                      icon: 'none',
                      duration: 2000
                    })
                }
            },
            fail:(err)=>{
            }
          })
        })
    }

你在api.js文件中,引入http.js文件,代码如下

// api.js
import http from 'http.js'

function getOrderDetailData(params) {
  return http({
    method: 'POST',
    url: '/some/url',
    data: params
  })
}

export { getOrderDetailData } 

最后在某个页面中使用 home/home.js

// home/home.js
import { getOrderDetailData } from 'api.js'
Page({
  /**
   * 页面的初始数据
   */
  data: {
    payId: '', // 支付订单ID
    orderDetail: {}, // 订单详情
  },
  /**
   * 页面显示的时候
   */
  onShow: function () {
    // 请求获取订单信息
    this.getOrderDetail({ payId: this.data.payId})
  },

  /**
   * 获取订单信息
   */
  getOrderDetail: function (params) {  
    getOrderDetailData(params).then(data=> {
      this.setData({orderDetail: data})
    })
  }
})

import { getA, getB, getC, getD } from 'api.js'

function callback () {  

  getA({id: 'A'}).then(Adata => {
    return getB({id: Adata.id})
  }).then(Bdata => {
    return getC({id: Bdata.id})
  }).then(Cdata => {s
    return getD({id: Cdata.id})
  }).then(Ddata => {
    ...
  })
}

使用function定义的函数,this的指向随着调用环境的变化而变化的,而箭头函数中的this指向是固定不变的,一直指向的是定义函数的环境。

----------------------------------------

小程序系列:

前言

项目结构

生命周期

请求与封装

数据绑定

事件

基础使用: 缓存

前端架构浅谈

----------------------------------------