本文将详细讲解封装思路,通过对接口请求进行封装,实现接口引入即用,减少了请求复杂的属性参数,更加关注数据与业务本身。
在 config 文件夹中配置好动态域名,对不同环境进行配置,方便前端开发时的调试与部署。
配置开发环境:有多个开发环境时,可以根据开发的需要切换到不同的接口进行调试,定义完后抛出。
const ENV_TYPE = {
// baseUrl: 'https://120.120.120.1/', // 调试环境1
// baseUrl: 'https://120.120.120.2/', // 调试环境2
// baseUrl: 'https://120.120.120.3/', // 调试环境3
baseUrl: 'https://120.120.120.4/', // 调试环境4
}
module.exports = ENV_TYPE
配置体验版服务器的地址,小程序上必须先发布体验版再发布测试版
const ENV_TYPE = {
baseUrl: 'https://test.com/', // 体验服务器地址
}
module.exports = ENV_TYPE
配置正式服务器地址
const ENV_TYPE = {
baseUrl: 'https://formal.com/',
}
module.exports = ENV_TYPE
通过对环境的检测,判断当前所属的是什么环境,动态切入请求地址,减少上传时的操作步骤。
现在我们需要了解的是如何知道当前所属的环境。由下文可知,我们可以基于
process.env.ENV_PATH
和uni.getAccountInfoSync()
获取当前所属的环境动态配置
uni-app
可通过process.env.NODE_ENV
判断当前环境是开发环境还是生产环境。一般用于连接测试服务器或生产服务器的动态切换。
- 在 HBuilderX 中,点击“运行”编译出来的代码是开发环境,点击“发行”编译出来的代码是生产环境
- cli 模式下,是通行的编译环境处理方式。
uni.getAccountInfoSync()
获取当前帐号信息,可以返回小程序的Appid。如果使用了微信小程序的云端插件,还可以反馈插件的id和版本
miniProgram 的结构:
属性 类型 说明 appId string 小程序 appId envVersion string 小程序 当前环境版本: develop
、trial
、release
version string 版本号 动态添加成功后,抛出
baseUrl
给接口文件引入
/**
* 配置动态化域名,外部js引用此文件process.env.NODE_ENV
*/
let path = process.env.ENV_PATH == undefined ? require('../config/develop') : require(process.env.ENV_PATH)
// #ifdef MP-WEIXIN
// ---------------- 根据微信开发环境配置请求地址 --------------------
// 获取当前帐号信息
const accountInfo = uni.getAccountInfoSync();
console.log(accountInfo)
// env类型 develop:开发版、trial:体验版、release:正式版
const envWx = accountInfo.miniProgram.envVersion;
if (envWx === 'release') {
path = require('../config/release.js')
} else if(envWx === 'trial') {
path = require('../config/trial.js')
} else if(envWx === 'develop') {
path = require('../config/develop.js')
}
// #endif
const baseUrl = path.baseUrl
module.exports = {
baseUrl
}
在该文件夹内我们对接口请求进行封装,对请求方式简化,专注于数据和业务。
对uniapp请求进行封装,设置登录请求和上传图片等公共请求接口。
/**
* 网络请求管理,二次封装
*/
/** 请求信息格式
{
apiName: 'url',
params: {
'key1': 'value1',
'key1': 'value2',
},
success: e=> {
},
fail: e=> {
}
} */
// 状态码
import hsCode from '../config/statusCode.js'
// 导入当前域名地址
import urlConfig from '@/config/urlConfig.js'
// 导入缓存
import storage from "@/config/storage.js"
// 调用超时的是时候,保存调取接口传入参数的参数
let requestInfoData = ''
let requestMethod = ''
/**
* 请求数据
* @url: 请求路径
* @params:请求参数(数据类型,对象)
* method: post get
* success:成功回调函数
* fail:失败回调函数
*/
function request(apiName, params = {}, method, success, fail) {
const token = storage.getToken()
var headersParam = {
'X-Requested-With': 'XMLHttpRequest',
"Accept": "application/json",
"Content-Type": "application/json; charset=UTF-8;",
"Accept-Encoding": ''
}
// token为空
if (token != '' || token != undefined || token != null) {
headersParam['Authorization'] = 'Bearer ' + token
}
console.log(urlConfig.baseUrl + apiName)
console.log('请求参数---->' + JSON.stringify(params))
// 发送请求
uni.request({
url: urlConfig.baseUrl + apiName,
data: params,
header: headersParam,
method: method,
success: function(res) {
if (res.data.code === hsCode.SUCCESS) {
success && success(res.data)
// token过期
} else if (res.data.code === hsCode.DENY) {
} else {
if (fail) {
fail(res.data.msg)
} else {
uni.showToast({
title: res.data.msg,
icon: 'none'
})
}
}
},
fail: function(res) {
fail && fail('请检查网络')
},
})
}
// 微信登录获取 sessionKey
/**
* 重新登录
* @success: 成功回调函数
* @params: 额外参数
* @isAgin:超时重新请求当前数据(false不请求,true请求)
*/
function onLogin({
success,
params = {},
isAgin = false
} = {}) {
// 判定对内或对外
uni.login({
success(res) {
params['js_code'] = res.code
console.log(res)
uni.request({
url: urlConfig.baseUrl + 'login',
data: params,
header: {
'content-type': 'application/json' // 默认值
},
success(res) {
// 成功
if (res.statusCode === 200) {
if (res.data.code === hsCode.successCode) {
const data = res.data.data
if (isAgin) {
if (requestMethod === 'GET') {
requestWithGet(requestInfoData)
} else if (requestMethod ===
'POST') {
requestWithPost(requestInfoData)
}
}
success && success(res)
} else {
console.log('wx.login:fail ' + res.data.msg)
}
} else {
console.log('wx.login:fail ' + res.errMsg)
}
},
fail(res) {
console.log('wx.login:fail ' + res.errMsg)
wx.showToast({
title: '请检查网络',
icon: 'none'
})
}
})
},
fail(res) {
// 失败
console.log(res)
uni.showToast({
title: '请检查网络状态',
icon: 'none'
})
}
})
}
// 深拷贝对象
function deepCopy(obj) {
var result = Array.isArray(obj) ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'object') {
result[key] = deepCopy(obj[key]); //递归复制
} else {
result[key] = obj[key];
}
}
}
return result;
}
/**
* DELETE请求
*/
export function requestWithDelete(requestInfo) {
requestInfoData = deepCopy(requestInfo)
requestMethod = 'DELETE'
request(requestInfo.apiName, requestInfo.params, "DELETE", requestInfo.success, requestInfo.fail);
}
/**
* put请求
*/
export function requestWithPut(requestInfo) {
requestInfoData = deepCopy(requestInfo)
requestMethod = 'PUT'
request(requestInfo.apiName, requestInfo.params, "PUT", requestInfo.success, requestInfo.fail);
}
/**
* post请求
*/
export function requestWithPost(requestInfo) {
requestInfoData = deepCopy(requestInfo)
requestMethod = 'POST'
request(requestInfo.apiName, requestInfo.params, "POST", requestInfo.success, requestInfo.fail);
}
/**
* get请求
*/
export function requestWithGet(requestInfo) {
requestInfoData = deepCopy(requestInfo)
requestMethod = 'GET'
request(requestInfo.apiName, requestInfo.params, "GET", requestInfo.success, requestInfo.fail);
}
// 上传图片
/**
* 上传图片传参
* options: {
* image: [], 必须,图片数据 string[]
* params: {}, 必须,接口参数
* success: ()=>{}, 成功回调
* fail: ()=>{}, 失败回调
* }
*/
export function uploadImage(options) {
console.log('请求参数---->' + JSON.stringify(options.params))
let url = urlConfig.baseUrl + 'api/upload'
if (typeof options.image === 'undefined' || options.image.length === 0) {
requestWithPost({
url,
params: options.params,
success: data => {
wx.hideLoading()
options.success && options.success(data)
},
fail(res) {
wx.hideLoading()
options.fail && options.fail(res)
}
})
return
}
let callUploadApi = 0
for (let i = 0; i < options.image.length; i++) {
if (typeof options.params.sortNum === 'undefined') options.params.sortNum = 0
options.params.Sort = options.params.sortNum + (i + 1)
// setTimeout(() => {
wx.uploadFile({
url: url + arugment, //仅为示例,非真实的接口地址
filePath: options.image[i],
name: 'file',
header: {
'content-type': 'multipart/form-data',
"Cookie": "ASP.NET_SessionId=" + wx.getStorageSync("sessionId")
},
formData: options.params,
success(res) {
wx.hideLoading()
const data = JSON.parse(res.data)
callUploadApi++
if (callUploadApi === options.image.length) {
options.success && options.success(data)
}
},
fail(res) {
wx.hideLoading()
options.fail && options.fail(res)
}
})
}
}
module.exports = {
requestWithGet,
requestWithPost,
requestWithPut,
requestWithDelete,
}
进行模块化的接口管理,你当前接口属于哪个模块,你就放在哪个模块里面。这样更方便对接口进行管理,后期也更好维护。该文件内的接口为基本模拟,实际开发按需添加接口即可。
// 公共接口调用
// 调用封装接口的文件,下面的所有请求都使用封装好的接口
import hs from './networkRequestManage'
import hsCode from '../config/statusCode.js'
import storage from '../config/storage'
import urlConfig from '@/config/urlConfig.js'
/**
* GET接口请求
*/
function getTranslation(key) {
return new Promise((resolve, reject) => {
hs.requestWithPost({
apiName: 'api/common/GetTranslation',
params: { // 没有参数可以直接不传参
key
},
success: data => {
resolve(data)
},
fail: res => {
reject(res)
}
})
})
}
/**
* POST接口请求
*/
function postTranslation(params) {
return new Promise((resolve, reject) => {
hs.requestWithGet({
apiName: 'api/common/PostTranslation',
params,
success: data => {
resolve(data)
},
fail: res => {
reject(res)
}
})
})
}
/**
* PUT接口请求
*/
function putTranslation(key) {
return new Promise((resolve, reject) => {
hs.requestWithPut({
apiName: 'api/common/PutTranslation',
params: {
key:key,
},
success: data => {
resolve(data)
},
fail: res => {
reject(res)
}
})
})
}
/**
* DELETE接口请求
*/
function getTranslation() {
return new Promise((resolve, reject) => {
hs.requestWithDelete({
apiName: 'api/common/DeleteTranslation',
success: data => {
resolve(data)
},
fail: res => {
reject(res)
}
})
})
}
// 导出接口
module.exports = {
getTranslation,
postTranslation,
putTranslation,
getTranslation,
}
在main.js中对配置文件全局导入,我们调用接口的时候就可以直接调用,而不用每次都import导入相关文件。
import * as publicInterface from './networking/publicInferface.js'
导入接口 js 为下面的原型定义做准备
Vue.prototype.$publicInterface = publicInterface
在vue项目main.js文件中通过上述方法,可以原型上定义它们使其在每个 Vue 的实例中可用,为后面我们在页面中的导入做准备
import App from './App'
// 公共接口
import * as publicInterface from './networking/publicInferface.js'
import storage from 'config/storage.js' // 缓存文件
import tools from 'utils/tools.js' // 工具文件
// 定义全局
Vue.prototype.$publicInterface = publicInterface
Vue.prototype.$storage = storage
Vue.prototype.$tools = tools
// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import {
createSSRApp
} from 'vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
// #endif
接下来介绍页面中如何引用,在 main.js 中定义后,导入就变得很简单了。
根据在接口文档定义的参数,需要调用对应功能模块下对应函数即可。只要你在接口页面处理好了请求类型与参数传递格式,就只需要关注传递的参数。
/**
* get请求:
* $publicInferface:注意你在 main.js 定义的名称
* getTranslation:注意你在 publicInferface.js 接口页面定义的函数名,一定不能重复
*/
this.$publicInferface.getTranslation(key)
.then(e => {
// 最后在回调里对返回数据进行处理即可
console.log(e)
})
.catch(e => {
console.log(e)
})
/**
* post请求:
* @params Object
* 格式和get请求基本都是一样的,只有参数改变
*/
this.$publicInferface.postTranslation(params)
.then(e => {
console.log(e)
})
.catch(e => {
console.log(e)
})