一開始網上查資料看到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'
const tokenUrl = 'https://www.zwl.com/api/v1/token/user'
export default{
baseUrl,
tokenUrl
}
在使用的時候調用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.tokenUrl,
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.tokenUrl,
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.tokenUrl,
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>