封裝axios模塊
背景
使用axios發起一個請求是比較簡單的事情,但是axios沒有進行封裝復用,專案越來越大,會引起越來越多的冗余代碼,讓代碼變的越來越難維護,所以我們對axios進行二次封裝,使專案中各個組件能夠復用請求,讓代碼變得更容易維護,
要點
統一url配置,
統一api請求
request請求攔截
response攔截器
根據需求,結合vuex做全域的loading影片,或者錯誤處理
將axios封裝成vue插件使用
檔案結構
在src目錄下新建http檔案夾,用來存放http互動api代碼,
api.js、axios.js、config.js、index.js、modules目錄

api.js
/*
* 介面統一集成模塊
*/
import * as login from './modules/login'
import * as user from './modules/user'
import * as dept from './modules/dept'
import * as role from './modules/role'
import * as menu from './modules/menu'
import * as dict from './modules/dict'
import * as config from './modules/config'
import * as log from './modules/log'
import * as loginlog from './modules/loginlog'
// 默認全部匯出
export default {
login,
user,
dept,
role,
menu,
dict,
config,
log,
loginlog
}
axios.js
1、這里匯入類組態檔的資訊(如baseURL、headers、withCredentials等設定)到axios物件
2、發送請求的時候獲取token,如果token不存在,說明未登錄,就重定向到系統登錄,否則帶token繼續發送請求
3、如果有需要,可以在這里通過response回應攔截器對回傳結果進行統一處理后再回傳
import axios from 'axios';
import config from './config';
import Cookies from "js-cookie";
import router from '@/router'
export default function $axios(options) {
return new Promise((resolve, reject) => {
const instance = axios.create({
baseURL: config.baseUrl,
headers: config.headers,
timeout: config.timeout,
withCredentials: config.withCredentials
})
// request 請求攔截器
instance.interceptors.request.use(
config => {
let token = Cookies.get('token')
// 發送請求時攜帶token
if (token) {
config.headers.token = token
} else {
// 重定向到登錄頁面
router.push('/login')
}
return config
},
error => {
// 請求發生錯誤時
console.log('request:', error)
// 判斷請求超時
if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {
console.log('timeout請求超時')
}
// 需要重定向到錯誤頁面
const errorInfo = error.response
console.log(errorInfo)
if (errorInfo) {
error = errorInfo.data // 頁面那邊catch的時候就能拿到詳細的錯誤資訊,看最下邊的Promise.reject
const errorStatus = errorInfo.status; // 404 403 500 ...
router.push({
path: `/error/${errorStatus}`
})
}
return Promise.reject(error) // 在呼叫的那邊可以拿到(catch)你想回傳的錯誤資訊
}
)
// response 回應攔截器
instance.interceptors.response.use(
response => {
return response.data
},
err => {
if (err && err.response) {
switch (err.response.status) {
case 400:
err.message = '請求錯誤'
break
case 401:
err.message = '未授權,請登錄'
break
case 403:
err.message = '拒絕訪問'
break
case 404:
err.message = `請求地址出錯: ${err.response.config.url}`
break
case 408:
err.message = '請求超時'
break
case 500:
err.message = '服務器內部錯誤'
break
case 501:
err.message = '服務未實作'
break
case 502:
err.message = '網關錯誤'
break
case 503:
err.message = '服務不可用'
break
case 504:
err.message = '網關超時'
break
case 505:
err.message = 'HTTP版本不受支持'
break
default:
}
}
console.error(err)
return Promise.reject(err) // 回傳介面回傳的錯誤資訊
}
)
// 請求處理
instance(options).then(res => {
resolve(res)
return false
}).catch(error => {
reject(error)
})
})
}
config.js
import { baseUrl } from '@/utils/global'
export default {
method: 'get',
// 基礎url前綴
baseUrl: baseUrl,
// 請求頭資訊
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
// 引數
data: {},
// 設定超時時間
timeout: 10000,
// 攜帶憑證
withCredentials: true,
// 回傳資料型別
responseType: 'json'
}
index.js
這里把axios注冊為Vue插件使用,并將api模塊掛載在Vue原型的$api物件上,這樣在能獲取this參考的地方就可以通過“this.$api.子模塊.方法” 的方式呼叫api了
// 匯入所有介面
import api from './api'
const install = Vue => {
if (install.installed)
return;
install.installed = true;
Object.defineProperties(Vue.prototype, {
// 注意,此處掛載在 Vue 原型的 $api 物件上
$api: {
get() {
return api
}
}
})
}
export default install
modules
modules模塊下子模塊比較多,不方便全貼,這里就以用戶管理模塊為例,
import axios from '../axios'
/*
* 用戶管理模塊
*/
// 保存
export const save = (data) => {
return axios({
url: '/user/save',
method: 'post',
data
})
}
// 洗掉
export const batchDelete = (data) => {
return axios({
url: '/user/delete',
method: 'post',
data
})
}
// 分頁查詢
export const findPage = (data) => {
return axios({
url: '/user/findPage',
method: 'post',
data
})
}
// 查找用戶的選單權限標識集合
export const findPermissions = (params) => {
return axios({
url: '/user/findPermissions',
method: 'get',
params
})
}
global.js
上面我們組態檔中引入了global.js,我們把一些全域的配置、常量和方法放到此檔案中,
/**
* 全域常量、方法封裝模塊
* 通過原型掛載到Vue屬性
* 通過 this.Global 呼叫
*/
// 后臺管理系統服務器地址
// export const baseUrl = 'http://139.196.87.48:8001'
export const baseUrl = 'http://localhost:8001'
// 系統資料備份還原服務器地址
// export const backupBaseUrl = 'http://139.196.87.48:8002'
export const backupBaseUrl = 'http://localhost:8002'
export default {
baseUrl,
backupBaseUrl
}
main.js
修改main.js,匯入api模塊,并通過vue.use(api)陳述句進行使用注冊,這樣就可以通過“this.$api.子模塊.子方法”的方式來呼叫后臺介面了,
引入global模塊,并通過配置
Vue.prototype.global = global
陳述句進行掛載,這樣就可以通過this.global.xx 來獲取全域配置了
import Vue from 'vue'
import App from './App'
import router from './router'
import api from './http'
import global from '@/utils/global'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI) // 引入Element
Vue.use(api) // 引入API模塊
Vue.prototype.global = global // 掛載全域配置模塊
new Vue({
el: '#app',
router,
render: h => h(App)
})
安裝js-cookie
在上面的axios.js中會用到cookie獲取token,所以需要把相關依賴安裝一下,執行以下命令安裝
npm add js-cookie
測驗
在登錄界面login.vue 中,添加一個登錄按鈕,單擊處理函式通過axios呼叫login介面回傳資料
回傳成功后,彈出提示,顯示token資訊,然后將token放入cookie并跳轉主頁,
Login.Vue
<template>
<div class="page">
<h2>Login Page</h2>
<el-button type="primary" @click="login()">登錄</el-button>
</div>
</template>
<script>
import mock from '@/mock/index.js'
import Cookies from "js-cookie"
import router from '@/router'
export default {
name: 'Login',
methods: {
login() {
this.$api.login.login().then(function(res) {
alert(res.token)
Cookies.set('token', res.token) // 放置token到Cookie
router.push('/') // 登錄成功,跳轉到主頁
}).catch(function(res) {
alert(res);
});
}
}
}
</script>
mock介面
在mock.js中添加login介面進行攔截,回傳一個token,
import Mock from 'mockjs'
Mock.mock('http://localhost:8080/user', {
'name': '@name', // 隨機生成姓名
'name': '@email', // 隨機生成郵箱
'age|1-12': 7, // 年齡1-12之間
})
Mock.mock('http://localhost:8001/login', {
'token': 'dsfagda5312adf1a56', //令牌
})
Mock.mock('http://localhost:8080/menu', {
'id': '@increment', // id自增
'name': 'menu', // 名稱為menu
'order|1-10': 6, // 排序1-10之間
})
配置完我們就可以啟動專案在瀏覽器進行測驗
在瀏覽訪問:http://localhost:8080/#/Login

點擊登錄

點擊確定,跳轉到主頁面

封裝mock模塊
背景:為了可以統一管理和集中控制資料模擬介面,我們對mock模塊進行封裝,可以方便定制模擬介面的統一開關和個體開關,
目錄結構如下:

一、在mock目錄下新建index.js,新建子目錄modules并在目錄里面創建介面*.js,
在index.js中統一引入所有介面并通過mock進行資料模擬,
index.js
import Mock from 'mockjs'
import { baseUrl } from '@/utils/global'
import * as login from './modules/login'
import * as user from './modules/user'
import * as role from './modules/role'
import * as dept from './modules/dept'
import * as menu from './modules/menu'
import * as dict from './modules/dict'
import * as config from './modules/config'
import * as log from './modules/log'
import * as loginlog from './modules/loginlog'
// 1. 開啟/關閉[所有模塊]攔截, 通過調[openMock引數]設定.
// 2. 開啟/關閉[業務模塊]攔截, 通過呼叫fnCreate方法[isOpen引數]設定.
// 3. 開啟/關閉[業務模塊中某個請求]攔截, 通過函式回傳物件中的[isOpen屬性]設定.
let openMock = true
// let openMock = false
fnCreate(login, openMock)
fnCreate(user, openMock)
fnCreate(role, openMock)
fnCreate(dept, openMock)
fnCreate(menu, openMock)
fnCreate(dict, openMock)
fnCreate(config, openMock)
fnCreate(log, openMock)
fnCreate(loginlog, openMock)
/**
* 創建mock模擬資料
* @param {*} mod 模塊
* @param {*} isOpen 是否開啟?
*/
function fnCreate (mod, isOpen = true) {
if (isOpen) {
for (var key in mod) {
((res) => {
if (res.isOpen !== false) {
let url = baseUrl
if(!url.endsWith("/")) {
url = url + "/"
}
url = url + res.url
Mock.mock(new RegExp(url), res.type, (opts) => {
opts['data'] = opts.body ? JSON.parse(opts.body) : null
delete opts.body
console.log('\n')
console.log('%cmock攔截, 請求: ', 'color:blue', opts)
console.log('%cmock攔截, 回應: ', 'color:blue', res.data)
return res.data
})
}
})(mod[key]() || {})
}
}
}
二、modules下介面比較多,代碼不方便粘貼,我們以用戶管理模塊為例,有需要的請私信博主
user.js
/*
* 用戶管理模塊
*/
// 保存
export function save() {
return {
url: 'user/save',
type: 'post',
data: {
"code": 200,
"msg": null,
"data": 1
}
}
}
// 批量洗掉
export function batchDelete() {
return {
url: 'user/delete',
type: 'post',
data: {
"code": 200,
"msg": null,
"data": 1
}
}
}
// 分頁查詢
export function findPage(params) {
let findPageData = {
"code": 200,
"msg": null,
"data": {}
}
let pageNum = 1
let pageSize = 8
if(params !== null) {
// pageNum = params.pageNum
}
if(params !== null) {
// pageSize = params.pageSize
}
let content = this.getContent(pageNum, pageSize)
findPageData.data.pageNum = pageNum
findPageData.data.pageSize = pageSize
findPageData.data.totalSize = 50
findPageData.data.content = content
return {
url: 'user/findPage',
type: 'post',
data: findPageData
}
}
export function getContent(pageNum, pageSize) {
let content = []
for(let i=0; i<pageSize; i++) {
let obj = {}
let index = ((pageNum - 1) * pageSize) + i + 1
obj.id = index
obj.name = 'mango' + index
obj.password = '9ec9750e709431dad22365cabc5c625482e574c74adaebba7dd02f1129e4ce1d'
obj.salt = 'YzcmCZNvbXocrsz9dm8e'
obj.email = 'mango' + index +'@qq.com'
obj.mobile = '18688982323'
obj.status = 1
obj.deptId = 12
obj.deptName = '技術部'
obj.status = 1
if(i % 2 === 0) {
obj.deptId = 13
obj.deptName = '市場部'
}
obj.createBy= 'admin'
obj.createTime= '2018-08-14 11:11:11'
obj.createBy= 'admin'
obj.createTime= '2018-09-14 12:12:12'
content.push(obj)
}
return content
}
// 查找用戶的選單權限標識集合
export function findPermissions() {
let permsData = {
"code": 200,
"msg": null,
"data": [
null,
"sys:user:view",
"sys:menu:delete",
"sys:dept:edit",
"sys:dict:edit",
"sys:dict:delete",
"sys:menu:add",
"sys:user:add",
"sys:log:view",
"sys:dept:delete",
"sys:role:edit",
"sys:role:view",
"sys:dict:view",
"sys:user:edit",
"sys:user:delete",
"sys:dept:view",
"sys:dept:add",
"sys:role:delete",
"sys:menu:view",
"sys:menu:edit",
"sys:dict:add",
"sys:role:add"
]
}
return {
url: 'user/findPermissions',
type: 'get',
data: permsData
}
}
三、然后我們修改一下登錄界面,包括匯入陳述句和回傳資料格式的獲取,修改后代碼如下:
login.vue
<template>
<div class="page">
<h2>Login Page</h2>
<el-button type="primary" @click="login()">登錄</el-button>
</div>
</template>
<script>
import mock from '@/mock/index.js'
import Cookies from "js-cookie"
import router from '@/router'
export default {
name: 'Login',
methods: {
login() {
this.$api.login.login().then(function(res) {
alert(JSON.stringify(res.data))
Cookies.set('token', res.token) // 放置token到Cookie
router.push('/') // 登錄成功,跳轉到主頁
}).catch(function(res) {
alert(res);
});
}
}
}
</script>
四、修改主界面,替換匯入mock檔案陳述句,修改后代碼如下:
home.vue
<template>
<div class="page">
<h2>Home Page</h2>
<el-button type="primary" @click="testAxios()">測驗Axios呼叫</el-button>
<el-button type="primary" @click="getUser()">獲取用戶資訊</el-button>
<el-button type="primary" @click="getMenu()">獲取選單資訊</el-button>
</div>
</template>
<script>
import axios from 'axios'
import mock from '@/mock/index.js'
export default {
name: 'Home',
methods: {
testAxios() {
axios.get('http://localhost:8080').then(res => { alert(res.data) })
},
getUser() {
axios.get('http://localhost:8080/user').then(res => { alert(JSON.stringify(res.data)) })
},
getMenu() {
axios.get('http://localhost:8080/menu').then(res => { alert(JSON.stringify(res.data)) })
}
}
}
</script>
到此,我們就完成了模塊封裝,下面我們進行測驗
啟動專案后,在瀏覽器訪問http://localhost:8080/#/login
可以看到我們登錄按鈕

點擊登錄可以看到我們介面中設定的回傳資料

然后跳轉到主頁面

到此axios和mock就都封裝好了
看完記得點贊哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/287165.html
標籤:其他
