一开始网上查资料看到mpvue中封装wx.request返回promise对象,感觉这样就够用了,也就没有用axios、flyio等库,后面需要结合token管理的时候就比较苦逼了,接下来的内容跟axios、flyio等内容完全不搭边,flyio等如果想管理token的话,需要做请求拦截,具体可以自行百度。
一、封装wx.request
在src/utils目录下新建requestMethod.js文件进行请求封装,可以新建config.js文件作为配置文件,存放请求基地址。
config.js:
const baseUrl = 'https://www.zwl.com/api/v1'
export default{
baseUrl
}
在使用的时候调用get方法,会在return处执行request(),request会返回一个Promise实例,在Promise实例化的时候发送了请求,requestMethod.js:
import Config from './config'
function request (url, method, data, header = {}) {
wx.showLoading({
title: '加载中' // 数据请求前loading
})
return new Promise((resolve, reject) => {
wx.request({
url: Config.baseUrl + url,
method: method,
data: data,
header: {
'content-type': 'application/json'
},
success: function (res) {
wx.hideLoading()
resolve(res.data)
},
fail: function (error) {
wx.hideLoading()
reject(error)
},
complete: function () {
wx.hideLoading()
}
})
})
}
function get (obj) {
return request(obj.url, 'GET', obj.data)
}
function post (obj) {
return request(obj.url, 'POST', obj.data)
}
export default {
request,
get,
post
}
使用前需要将封装的请求挂在Vue原型上,被所有vue实例共享(相当于定义了全局变量,在.vue文件中可以直接使用)。src/main.js如下:
import Vue from 'vue'
import App from './App'
import request from './utils/requestMethod'
// 将request赋值给Vue原型链上的$http属性($http属性将被创建)
Vue.prototype.$http = request
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue(App)
app.$mount()
在src/pages/index/index.vue中使用如下:
<template>
<div></div>
</template>
<script>
export default {
data () {
return {
dataList: []
}
},
methods: {
// 初始化数据
initData () {
this.$http.post({
url: '/collection/is_collect/',
data: {'id': 7}
}).then((res) => {
// 请求成功执行回调,开始初始化数据
this.dataList = res.data
})
}
},
created () {
// 在vue实例创建时执行数据初始化
this.initData()
}
}
</script>
<style lang="stylus" rel="stylesheet/stylus">
</style>
二、vuex管理token
在src下新建目录store,store目录如下:
把要管理的数据放入state.js文件:
const state = {
token: ''
}
export default state
在mutation-types.js中定义类型(想不到词形容这个文件的作用,直接看操作吧):
// 设置token
export const SET_TOKEN = 'SET_TOKEN'
// 更新token
export const UPDATE_TOKEN = 'UPDATE_TOKEN'
在mutations.js文件中对数据进行操作,这里我们同时把token存入本地缓存:
import * as types from './mutation-types'
const matations = {
/**
* state: 当前状态树
* v: 提交matations时传的参数
* 设置token时执行的操作
*/
[types.SET_TOKEN] (state, v) {
state.token = v
// 同步存入本地缓存
wx.setStorageSync('token', v)
},
// 更新token时执行的操作
[types.UPDATE_TOKEN] (state, v) {
state.token = v
wx.setStorageSync('token', v)
}
}
export default matations
在src/store/index.js中进行组装:
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import getters from './getters'
import actions from './actions'
import mutations from './mutations'
Vue.use(Vuex)
export default new Vuex.Store({
state,
getters,
actions,
mutations
})
在src/main.js中将store挂在原型上:
import store from './store/index'
Vue.prototype.$store = store
使用示例:
// 注意‘UPDATE_TOKEN’将执行mutations下的[types.UPDATE_TOKEN]对应的操作
this.$store.commit('UPDATE_TOKEN', token的新值)
三、携带token访问API
以下是根据个人开发的小程序里设计的token管理流程
根据流程图,主要是在访问API后对返回的状态进行判断,是否出现token过期或者token为空的状态,然后重新获取token,再重发当前请求。首先,封装一个方法用作从服务器获取token,src/utils/token.js:
import Config from './config'
// 从服务器获取token
function getTokenFromServer () {
return new Promise((resolve, reject) => {
wx.login({
success: function (res) {
wx.request({
url: Config.baseUrl + '/token/user',
method: 'POST',
data: {
code: res.code
},
success: function (res) {
// 取得token后需要对现在的token进行更新
this.$store.commit('UPDATE_TOKEN', res.data.token)
resolve(res.data)
}
})
}
})
})
}
这里可能会出现错误说$store undefined;
这里可能原因是this的指向问题,这里的this指向是当前js文件,所以需要想办法引入vue实例,vue实例可以使用$store变量:
之后需要重写之前封装的请求,requestMethod.js:
四、全部代码:
requestMethod.js:
import Config from './config'
import Token from './token'
function request (url, method, data, header = {}, noRefetch = false) {
wx.showLoading({
title: '加载中' // 数据请求前loading
})
return new Promise((resolve, reject) => {
wx.request({
url: Config.baseUrl + url,
method: method,
data: data,
header: {
'token': wx.getStorageSync('token'),
'content-type': 'application/json'
},
success: function (res) {
wx.hideLoading()
let code = res.statusCode.toString()
let startChar = code.charAt(0)
if (startChar === '2') {
resolve(res.data)
} else {
if (code === '401') {
if (!noRefetch) {
Token.getTokenFromServer().then((token) => {
// 获取token之后重新发送请求
request(url, method, data, {}, true).then((res) => {
resolve(res)
})
})
}
}
}
},
fail: function (error) {
wx.hideLoading()
reject(error)
},
complete: function () {
wx.hideLoading()
}
})
})
}
function get (obj) {
return request(obj.url, 'GET', obj.data)
}
function post (obj) {
return request(obj.url, 'POST', obj.data)
}
export default {
request,
get,
post
}
token.js:
import Config from './config'
import Vue from 'vue'
let vue = new Vue()
function verify () {
let token = wx.getStorageSync('token')
if (!token) {
// 不存在token缓存,获取token
getTokenFromServer()
} else {
// 对缓存中的token进行验证
verifyFromServer(token)
}
}
// 从服务器获取token
function getTokenFromServer () {
return new Promise((resolve, reject) => {
wx.login({
success: function (res) {
wx.request({
url: Config.baseUrl + '/token/user',
method: 'POST',
data: {
code: res.code
},
success: function (res) {
vue.$store.commit('UPDATE_TOKEN', res.data.token)
resolve(res.data.token)
}
})
}
})
})
}
// 携带令牌去服务器校验
function verifyFromServer (token) {
wx.request({
url: Config.baseUrl + '/token/user',
method: 'POST',
data: {
token: token
},
success: function (res) {
let valid = res.data.isValid
if (!valid) {
getTokenFromServer()
}
}
})
}
export default{
verify,
getTokenFromServer,
verifyFromServer
}
main.js:
import Vue from 'vue'
import App from './App'
import request from './utils/requestMethod'
import store from './store/index'
Vue.prototype.$store = store
Vue.prototype.$http = request
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue(App)
app.$mount()
使用时,如果当前token无效可以在调试的时候看到当前网络请求发送了两次,第一次为401,第二次为成功状态:
<template>
<div></div>
</template>
<script>
export default {
data () {
return {
dataList: []
}
},
methods: {
// 初始化数据
initData () {
this.$http.post({
url: '/collection/is_collect/',
data: {'id': 7}
}).then((res) => {
// 请求成功执行回调,开始初始化数据
this.dataList = res.data
})
}
},
created () {
// 在vue实例创建时执行数据初始化
this.initData()
}
}
</script>
<style lang="stylus" rel="stylesheet/stylus">
</style>