02_微信小程序常用的方法封装
消息提示框封装:
//消息提示框封装 /** // 提示的内容 title: '消息提示框', // 提示的内容 icon: 'success', // 提示的图标,success(成功)、error(失败)、loading(加载)、none(不显示图标) duration: 2000, // 提示的延迟时间 mask: true // 是否显示透明蒙层,防止触摸穿透 */ const toast = ({ title = '数据加载中', icon = 'none', mask = true, duration = 3000 } = {}) => { wx.showToast({ title, icon, mask, duration }) } //挂载到全局 wx.toast=toast; //导出 export {toast}
调用
//import {toast} from './utils/extendApi' import './utils/extendApi' //toast() //toast({"title":"成功","icon":"success"}) //wx.toast(); wx.toast({"title":"成功","icon":"success"})
----------------------------------------------------------漂亮的分割线----------------------------------------------------------------------------------------
消息对话框封装:
wx.showModal({ title: '提示', // 提示的标题 content: '您确定执行该操作吗?', // 提示的内容 confirmColor: '#f3514f', // 接口调用结束的回调函数(调用成功、失败都会执行) complete({ confirm, cancel }) { confirm && console.log('点击了确定') cancel && console.log('点击了取消') } })
封装
const modal = (options = {}) => { // 使用 Promise 处理 wx.showModal 的返回结果 return new Promise((resolve) => { // 默认的参数 const defaultOpt = { title: '提示', content: '您确定执行该操作吗?', confirmColor: '#f3514f', } // 将传入的参数和默认的参数进行合并 const opts = Object.assign({}, defaultOpt, options) wx.showModal({ // 将合并的参数赋值传递给 showModal 方法 ...opts, complete({ confirm, cancel }) { // 如果用户点击了确定,通过 resolve 抛出 true // 如果用户点击了取消,通过 resolve 抛出 false confirm && resolve(true) cancel && resolve(false) } }) }) } wx.modal = modal //导出 export { modal }
如果全局使用:export const modal = (options = {}) => {
删除:export { toast,modal }
使用:
import {modal} from './utils/extendApi' //import './utils/extendApi' App({ async onShow(){ // 第一种调用方式:不传入任何参数 // 不使用任何参数,使用默认值 //const res = await modal() //console.log(res) // 第二种调用方式:更改默认配置 const res = await modal({ content: '鉴权失败,请重新登录', showCancel: false }) console.log(res) } })
----------------------------------------------------------漂亮的分割线----------------------------------------------------------------------------------------
封装本地存储
/** * @description 存储数据 * @param {*} key 本地缓存中指定的 key * @param {*} value 需要缓存的数据 */ export const setStorage = (key, value) => { try { wx.setStorageSync(key, value) } catch (e) { console.error(`存储指定 ${key} 数据发生错误:`, e) } } /** * @description 从本地读取对应 key 的数据 * @param {*} key */ export const getStorage = (key) => { try { const value = wx.getStorageSync(key) if (value) { return value } } catch (e) { console.error(`获取指定 ${key} 数据发生错误:`, e) } } /** * @description 从本地移除指定 key 数据 * @param {*} key */ export const removeStorage = (key) => { try { wx.removeStorageSync(key) } catch (e) { console.error(`移除指定 ${key} 数据发生错误:`, e) } } /** * @description 从本地清空全部的数据 */ export const clearStorage = () => { try { wx.clearStorageSync() } catch (e) { console.error("清空本地存储时发生错误:", e); } } /** * @description 将数据存储到本地 - 异步方法 * @param {*} key 本地缓存中指定的 key * @param {*} data 需要缓存的数据 */ export const asyncSetStorage = (key, data) => { return new Promise((resolve) => { wx.setStorage({ key, data, complete(res) { resolve(res) } }) }) } /** * @description 从本地读取指定 key 的数据 - 异步方法 * @param {*} key */ export const asyncGetStorage = (key) => { return new Promise((resolve) => { wx.getStorage({ key, complete(res) { resolve(res) } }) }) } /** * @description 从本地移除指定 key 的数据 - 异步方法 * @param {*} key */ export const asyncRemoveStorage = (key) => { return new Promise((resolve) => { wx.removeStorage({ key, complete(res) { resolve(res) } }) }) } /** * @description 从本地移除、清空全部的数据 - 异步方法 */ export const asyncClearStorage = () => { return new Promise((resolve) => { wx.clearStorage({ complete(res) { resolve(res) } }) }) }
调用:
import {setStorage,getStorage,removeStorage,clearStorage,asyncSetStorage,asyncGetStorage} from './utils/storage' App({ async onShow(){ setStorage('name','张三') let _name= getStorage('name') console.log(_name) asyncSetStorage('age',28).then((res)=>{ console.log(res.errMsg) }) asyncGetStorage('age').then((res)=>{ console.log(res.data) }) } })
----------------------------------------------------------漂亮的分割线----------------------------------------------------------------------------------------
网络请求封装
// 创建 WxRequest 类 // 通过类的方式来进行封装,会让代码更加具有复用性 // 也可以方便添加新的属性和方法 class WxRequest { // 定义实例属性,用来设置默认请求参数 defaults = { baseURL: '', // 请求基准地址 url: '', // 接口的请求路径 data: null, // 请求参数 method: 'GET', // 默认的请求方法 // 请求头 header: { 'Content-type': 'application/json' // 设置数据的交互格式 }, timeout: 60000, // 默认的超时时长,小程序默认的超时时长是 1 分钟 isLoading: true // 控制是否使用默认的 loading,默认值是 true 表示使用默认的 loading } // 定义拦截器对象 // 需要包含请求拦截器以及响应拦截器,方便在请求之前以及响应以后时进行逻辑处理 interceptors = { // 请求拦截器 // 在请求发送之前,对请求参数进行新增或者修改 request: (config) => config, // 响应拦截器 // 在服务器响应数据以后,对服务器响应的数据进行逻辑处理 response: (response) => response } // 定义数组队列 // 初始值需要是一个空数组,用来存储请求队列、存储请求标识 queue = [] // 用于创建和初始化类的属性以及方法 // 在实例化时传入的参数,会被 constructor 形参进行接收 constructor(params = {}) { // 通过 Object.assign 方法合并请求参数 // 注意:需要传入的参数,覆盖默认的参数,因此传入的参数需要放到最后 this.defaults = Object.assign({}, this.defaults, params) } // request 实例方法接收一个对象类型的参数 // 属性值和 wx.request 方法调用时传递的参数保持一致 request(options) { // 如果有新的请求,就清除上一次的定时器 this.timerId && clearTimeout(this.timerId) // 注意:需要先合并完整的请求地址 (baseURL + url) // https://gmall-prod.atguigu.cn/mall-api/index/findBanner options.url = this.defaults.baseURL + options.url // 合并请求参数 options = { ...this.defaults, ...options } // 在请求发送之前,添加 loading 效果 // wx.showLoading() if (options.isLoading && options.method !== 'UPLOAD') { // 判断 queue 队列是否为空,如果是空,就显示 loading // 如果不是空,就不显示 loading,不调用 wx.showLoading() this.queue.length === 0 && wx.showLoading() // 然后立即向 queue 数组队列中添加请求标识 // 每个标识代表是一个请求,标识是自定义的 this.queue.push('request') } // 在请求发送之前,调用请求拦截器,新增和修改请求参数 options = this.interceptors.request(options) // 需要使用 Promise 封装 wx.request,处理异步请求 return new Promise((resolve, reject) => { if (options.method === 'UPLOAD') { wx.uploadFile({ ...options, success: (res) => { // 需要将服务器返回的 JSON 字符串 通过 JSON.parse 转成对象 res.data = JSON.parse(res.data) // 合并参数 const mergeRes = Object.assign({}, res, { config: options, isSuccess: true }) resolve(this.interceptors.response(mergeRes)) }, fail: (err) => { // 合并参数 const mergeErr = Object.assign({}, err, { config: options, isSuccess: false }) reject(this.interceptors.response(mergeErr)) } }) } else { wx.request({ ...options, // 当接口调用成功时会触发 success 回调函数 success: (res) => { // 不管是成功响应还是失败响应,都需要调用响应拦截器 // 响应拦截器需要接收服务器响应的数据,然后对数据进行逻辑处理,处理好以后进行返回 // 然后在通过 resolve 将返回的数据抛出去 // 在给响应拦截器传递参数时,需要将请求参数也一起传递 // 方便进行代码的调试或者进行其他逻辑处理,需要先合并参数 // 然后将合并的参数传递给响应拦截器 // 不管是请求失败还是请求成功,都已经将响应的数据传递给了响应拦截器 // 这时候在合并参数的时候,追加一个属性:isSuccess // 如果属性值为 true,说明执行了 success 回调函数 // 如果属性值为 false,说明执行了 fail 回调函数 const mergeRes = Object.assign({}, res, { config: options, isSuccess: true }) resolve(this.interceptors.response(mergeRes)) }, // 当接口调用失败时会触发 fail 回调函数 fail: (err) => { // 不管是成功响应还是失败响应,都需要调用响应拦截器 const mergeErr = Object.assign({}, err, { config: options, isSuccess: false }) reject(this.interceptors.response(mergeErr)) }, // 接口调用结束的回调函数(调用成功、失败都会执行) complete: () => { if (options.isLoading) { // 在每一个请求结束以后,都会执行 complete 回调函数 // 每次从 queue 队列中删除一个标识 this.queue.pop() this.queue.length === 0 && this.queue.push('request') this.timerId = setTimeout(() => { this.queue.pop() // 在删除标识以后,需要判断目前 queue 数组是否为空 // 如果是空,说明并发请求完成了 // 就需要隐藏 loading,要调用 wx.hideLoading() this.queue.length === 0 && wx.hideLoading() clearTimeout(this.timerId) }, 1) // 不管请求时成功还是失败,都需要隐藏 loading // wx.hideLoading() } } }) } }) } // 封装 GET 实例方法 get(url, data = {}, config = {}) { // 需要调用 request 请求方法发送请求,只需要组织好参数,传递给 request 请求方法即可 // 当调用 get 方法时,需要将 request 方法的返回值 return 出去 return this.request(Object.assign({ url, data, method: 'GET' }, config)) } // 封装 DELETE 实例方法 delete(url, data = {}, config = {}) { return this.request(Object.assign({ url, data, method: 'DELETE' }, config)) } // 封装 POST 实例方法 post(url, data = {}, config = {}) { return this.request(Object.assign({ url, data, method: 'POST' }, config)) } // 封装 PUT 实例方法 put(url, data = {}, config = {}) { return this.request(Object.assign({ url, data, method: 'PUT' }, config)) } // 用来处理并发请求 all(...promise) { // 通过展开运算符接收传递的参数 // 那么展开运算符会将传入的参数转成数组 // console.log(promise) return Promise.all(promise) } /** * @description upload 实例方法,用来对 wx.uploadFile 进行封装 * @param {*} url 文件的上传地址、接口地址 * @param {*} filePath 要上传的文件资源路径 * @param {*} name 文件对应的 key * @param {*} config 其他配置项 */ upload(url, filePath, name = 'file', config = {}) { return this.request( Object.assign({ url, filePath, name, method: 'UPLOAD' }, config) ) } } export default WxRequest
----------------------------------------------------------漂亮的分割线----------------------------------------------------------------------------------------
mina-request的安装
网站:mina-request - npm (npmjs.com)
安装命令:npm install mina-request
工具——>构建npm
http.js
// 导入模块、包提供的类 import WxRequest from 'mina-request' // 导入封装的本地存储操作模块 import { getStorage, clearStorage } from './storage' // 导入封装的增强 API import { toast, modal } from './extendApi' // 对类进行实例化 const instance = new WxRequest({ baseURL: 'https://gmall-prod.atguigu.cn/mall-api', timeout: 15000 }) // 添加请求拦截器 (在请求发送之前对请求参数进行新增或者修改) instance.interceptors.request = (config) => { // 在实际开发中,有一些接口需要使用访问令牌 token // 访问令牌 token 通常是存储到本地 // 需要先从本地获取到存储的 token const token = getStorage('token') // 如果本地存在 token,这时候就需要在请求头中添加 token 字段 if (token) { config.header['token'] = token } // 在发送请求之前做些什么 return config } // 添加响应拦截器 (在服务器响应数据以后,对返回的数据进行逻辑处理) instance.interceptors.response = async (response) => { // 从 response 对象中解构两个数据 const { isSuccess, data } = response // response 服务器响应的数据,只不过数据被 wx.request 进行了一层包装 // console.log(response) // response.config 封装的包里面提供的 config 属性,是请求的参数信息 // 可以使用请求参数进行代码的调试 // response.data 服务器真正响应的数据 // response.isSuccess 判断代码执行了哪一个回调函数 // isSuccess = true,说明代码执行了 wx.request 方法的 success 回调函数 // isSuccess = false,说明代码执行了 wx.request 方法的 fail 回调函数 // 如果 isSuccess = false,说明网络出现了问题 if (!isSuccess) { toast({ title: '网络异常请重试', icon: 'error' }) return Promise.reject(response) } // 如果 isSuccess = true,说明代码执行到了 success 回调函数 // 需要开发者对返回的参数进行逻辑判断 // 需要对后端返回的业务状态码进行判断 // 业务状态码 === 200,接口调用成功,服务器成功返回了数据 // 业务状态码 === 208,没有 token 或者 token 失效,需要让用户重新进行登录 // 业务状态码既不等于 200,也不等于 208,说明出现了其他异常,需要给用户统一进行提示 switch (data.code) { case 200: // 接口调用成功,服务器成功返回了数据,只需要将数据简化以后返回即可 return data case 208: const res = await modal({ content: '鉴权失败,请重新登录', showCancel: false }) if (res) { // 既然用户需要重新进行登录,就需要把之前用户存储的信息(过期的 token) 进行清除 clearStorage() wx.navigateTo({ url: '/pages/login/login' }) } return Promise.reject(response) default: toast({ title: '程序出现异常,请联系客服或稍后重试!' }) return Promise.reject(response) } // return response } // 导出实例 export default instance
接口地址环境变量设置
添加env.js
// 获取 小程序帐号信息 const { miniProgram } = wx.getAccountInfoSync() // 获取小程序当前开发环境 // develop 开发版, trial 体验版, release 正式版 const { envVersion } = miniProgram let env = { baseURL: 'https://gmall-prod.atguigu.cn' } switch (envVersion) { //开发版 case 'develop': env.baseURL = 'https://gmall-prod.atguigu.cn' break //体验版 case 'trial': env.baseURL = 'https://gmall-prod.atguigu.cn' break //正式版 case 'release': env.baseURL = 'https://gmall-prod.atguigu.cn' break defaulr } export { env }
http.js
// 导入模块、包提供的类 import WxRequest from 'mina-request' // 导入封装的本地存储操作模块 import { getStorage, clearStorage } from './storage' // 导入封装的增强 API import { toast, modal } from './extendApi' import {env} from './env' // 对类进行实例化 const instance = new WxRequest({ //baseURL: 'https://gmall-prod.atguigu.cn/mall-api', baseURL:env.baseURL, timeout: 15000, isLoading:false })
模块API封装
index.js
// 导入封装的网络请求工具 http.js import http from '../utils/http' /** * @description 获取轮播图数据 * @returns Promise */ export const reqSwiperData = () => http.get('/mall-api/index/findBanner')
// 导入接口 API import { reqSwiperData } from '../../api/index' Page({ // 页面数据 data: { swiperList: [] }, // 小程序页面加载时执行 onLoad () { // 调用获取首页数据的方法 getHomeList() } // 获取首页数据 async getHomeList() { // 获取轮播图数据 const res = await reqSwiperData() console.log(res) } })