小程序中对API promise化以及对数据进行共享

819 阅读6分钟

1.基于Promise的API

1.1 基于回调函数API的缺点

  • 默认情况下,小程序官方提供的API都是基于回调函数实现的。 缺点:会造成回调地狱的情况,比如:使用原生的小程序API发送请求
clickSendRequest(){
    wx.request({
      url: 'url',
      method:'', //请求方式
      data: {}, //请求携带的参数
      success: () => {}, //请求成功后的回调
      fail: () => {}, // 请求失败后的回调
      complete: () => {} //请求完成后的回调
    })
  }

1.2 什么是API promise化?

  • API的promise化,指的是通过额外的配置,将官方提供的、基于回调函数的异步 API,升级改造为基于 Promise 的异步 API,从而提高代码的可读性、维护性,避免回调地狱的问题

1.3 实现小程序API的promise化

  • 通过安装 miniprogram-api-promise实现promise化,安装命令 npm install --save miniprogram-api-promise@版本号
  • 在app.json文件中引入 miniprogram-api-promise
// app.json
import { promisifyAll } from "miniprogram-api-promise"
// 定义空对象用来接收wx实例对象
const wxp = wx.p = {}
// promisifyAll()接受两个参数,第一个参数页面的实例wx,第二个参数为空对象接收页面的实例wxp
promisifyAll(wx,wxp)

  • 在方法中发送请求测试
// 点击发送请求
    async btnSendRequest(){
        // 使用await等待请求结果的返回,async修饰的函数返回的结果为promise
        const { data: res } =  await wx.p.request({
            method: 'GET',
            url: '', //请求的路径
            success: () => {}, //请求成功的回调函数
            fail: () => {}, //请求失败的回调函数
            complate: () => {} //请求完成后的请求
        })
        console.log(res)
    }

2. 小程序中支持es7的async语法

  • async和await是解决回调的最终方案
  1. 在小程序中使用facebook的regenerator库的步骤
  • (1) 首先需要在小程序开发工具中勾选es6转es5语法
    image.png
  • (2) 接着下载facebook的regenerator库中的runtime/runtime.js文件
  • 可以去这里下载github.com/facebook/re…
  • (3) 在小程序项目根目录下新建lib/runtime/runtime.js文件目录结构,把facebook中的runtime.js代码复制到小程序runtime.js中
  • (4) 在小程序项目中,那个页面需要使用async语法,引入runtime.js文件(不能全局引入)
  • (5) 使用案例
      1. 封装的请求接口
        // 封装的请求接口 request.js
        export const request = (params) => {
            // 定义接口的基础路径
            const baseUrl = "https://www.baidu.com/api/v1"
            return new Promise((resolve,reject)=>{
                wx.request({
                    ...params
                    url: parmas.url + baseUrl,
                    success: (res) => {
                        resolve(res)
                    },
                    fail: (error) => {
                        console.log(error)
                    }
                })
                
            })
        }
    
    • 2.页面中使用封装的接口,发送请求,获取数据
    // 引入封装的接口请求
    import { request } from '../../api/request.js'
    import { regeneratorRuntime }  from '../../lib/runtime'
    // 接口发送请求获取数据
    Page({
        data:{
            username: '', //定义存储用户名变量
        },
        onLoad(){
            this.getData()
        },
        async getData(){
            // 定义发送请求,await等待接口返回数据的响应
            const res = await wx.request({url:'/username', method: 'GET'})
            if(res.code === 200){
                this.setData({
                    username: res.data.username
                })
            }
        }
    })
    

3.全局数据共享Mobx

3.1 安装npm包

  • mobx-miniprogram:相当于数据共享仓库,用于存放各种数据,方便用户存取操作。
  • mobx-miniprogram-bindings:介于数据共享仓库和组件之间的桥梁,使二者能够相互联系。
  • cmd: npm i --save mobx-miniprogram mobx-miniprogram-bindings
  • 安装完成后删除项目原有的miniprogram_npm ,小程序开发工具中重新npm构建项目

3.2 在页面中使用store mobx

  • 新建根文件夹store并创建store.js文件存放mobx实例
// 创建store实例对象
import { observable } from "mobx-miniprogram"
export const store = observable({
    // 全局数据字段
    numA: 1,
    numB: 2,
    // 计算属性
    get sum(){
        return this.numA + this.numB
    },
    // action函数修改数据
    updateNumberA: action(function(val){
        this.numA += val
    }),
    updataNuberB: action(function(val){
        this.numB += val
    })
})

  • 注意:考虑到数据的管理和安全,mobx store里的数据不允许外界直接修改,必须通过store对象中的action函数修改

2.3 在index.js中引入全局仓库中的数据

// pages/index/index.js
// 在当前js中引入数据仓库
import { store } from "../../store/store"
// 使用**mobx-miniprogram-bindings**搭建页面与数据仓库之间的桥梁
import { createStoreBindings } form "mobx-miniprogram-binds"
Page({
    onLoad(){
        // createStoreBindings方法接收两个参数,参数1:将数据绑定到的地方,this指的是当前页面
        // 第二个参数:为数据仓库中绑定的数据、方法
        // this.storeBindings自定义添加一个方法,来接收数据仓库中的数据
        this.storeBindings = createStoreBindings(this,{
            store,
            fields:{
                numA: 'numA',
                numB: (store)=> store.numB
                sum: () => sum
            },
            actions: ['updateNumberA','updateNumberB']
        })
      }
})

3.4 在index.wxml中使用 index.js中接收仓库的数据

<!-- pages/index/index.wxml -->
<!-- 使用mobx定义的全局数据 -->
<view>{{numA}} + {{numB}} = {{sum}}</view>
<van-button type="info" bindtap="btnadd" data-step="{{1}}">numA+1</van-button>
<van-button type="warning" bindtap="btnminues" data-step="{{-1}}">numB-1</van-button>

3.5 在index.js页面中修改仓库中数据字段的值

    // pages/index/index.js
    Page({
        onLoad(){
            this.storeBindings = createStoreBindings(this,{
            ...
            })
        },
        // numA+1
        btnaaa(e){
            // e.target.dataset.step 获取事件绑定时传递的参数
            this.updateNumberA(e.target.dataset.step)
        },
        // 修改numB-1
        btnminues(e){
            this.updateNumberB(e.target.dataset.step)
        }
        
    })

4.在组件中使用store mobx

4.1 新建组件test,配置为全局的组件,在pages/index/index.wxml中使用test组件

<!--components/test/test.wxml-->
<view>
  <view>{{numA}} + {{numB}} = {{sum}}</view>
  <van-button type="info" bindtap="btnadd" data-step="{{1}}">numA+1</van-button>
  <van-button type="warning" bindtap="btnminues" data-step="{{-1}}">numB-1</van-button>
</view>

<!-- pages/index/index.wxml -->
<!-- 在index页面使用全局组件test -->
<test></test>

4.2 在components/test/test.js文件中使用全局数据

// components/test/test.js
// 引入全局的数据仓库
import { store } from "../../store/store";
// 使用storeBindingBehavior搭建页面与数据之间的桥梁
import { storeBindingsBehavior } from "mobx-miniprogram-bindings";
Component({
  // 在behavior添加storeBindingBehavior来做绑定功能
  behaviors: [storeBindingsBehavior],
  // 配置对象
  storeBindings:{
    store:store,
    fields: {
      numA: 'numA',
      numB: 'numB',
      sum: 'sum'
    },
    actions: [ "updateNumberA","updateNumberB" ]
  },

  /**
   * 组件的方法列表
   */
  methods: {
    // 修改numA值加1的方法
    btnadd(e){
      this.updateNumberA(e.target.dataset.step)
    },
    // 修改numB值减1的方法
    btnminues(e){
      this.updateNumberB(e.target.dataset.step)
    }
  }
})

5. 分包

5.1 分包加载

  • 某些情况下,开发者需要将小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载。
  • 在构建小程序分包项目时,构建会输出一个或多个分包。每个使用分包小程序必定含有一个主包。所谓的主包,即放置默认启动页面/TabBar 页面,以及一些所有分包都需用到公共资源/JS 脚本;而分包则是根据开发者的配置进行划分
  • 在小程序启动时,默认会下载主包并启动主包内页面,当用户进入分包内某个页面时,客户端会把对应分包下载下来,下载完成后再进行展示。目前小程序分包大小有以下限制
  • 整个小程序所有分包不能超过20M
  • 单个分包/ 主包不能超过2M
  • 对小程序进行分包,可以优化小程序首次启动的下载时间,

5.2 使用分包

  • 在app.json中的subpackages字段声明分包的结构
"subPackages": [
    {
      "root": "packageA",
      "name": "pckA",
      "pages": [
        "pages/cat/cat",
        "pages/pig/pig"
      ]
    },
    {
      "root": "packageB",
      "name": "pckB",
      "pages": [
        "pages/apple/apple",
        "pages/grape/grape"
      ]
    }
  ],
  • subpackages中,每个分包的配置有以下几项
  1. root: 分包的目录
  2. name: 分包别名,分包预下载时可以使用
  3. pages:分包页面路径,相对于分包根路径
  4. independent: 分包是否时独立分包

5.3 打包原则

  • 声明subpackages后,将按照subpackages配置路径进行打包,subpackages配置路径外的目录将被打包到主包中
  • subpackages 的根路径不能是另外一个subpackages内的子目录
  • tabBar 必须在主包内

5.4 引用原则

  • packageA无法 require packageB JS文件,但可以require主包、packageA内的JS文件;使用分包异步化时不受此条限制。

  • packageA 无法 import packageB 的 template,但可以 require 主包、packageA 内的 template

  • packageA 无法使用 packageB 的资源,但可以使用主包、packageA 内的资源

5.5 独立分包

  • 独立分包属于分包的一种,普通分包的所有限制都对独立分包有效。
  • app.jsonsubpackages字段中对应的分包配置项中定义 independent字段声明对应分包为独立分包。
 "subPackages": [
   {
     "root": "packageA",
     "name": "pckA",
     "pages": [
       "pages/cat/cat",
       "pages/pig/pig"
     ]
   },
   {
     "root": "packageB",
     "name": "pckB",
     "pages": [
       "pages/apple/apple",
       "pages/grape/grape"
     ],
     "independent": true
   }
 ],

5.6 分包预下载

  • 配置方法:预下载分包行为在进入某个页面时触发,通过在app.json增加preloadRule配置来控制
"preloadRule":{
    "pages/index/index":{
        "network": "all",
        "packages": "pckA"
    }
}
  • preloadRule中,key是页面路径,value是进入此页面的预下载配置,每个配置有以下几项 1.packages 进入页面后预下载分包的root或name 2.network 在指定网络下预下载,可选值为 all | wife

    frailscholar