vue請求介面封裝
阿新 • • 發佈:2021-09-13
1. 使用require.context()
動態載入檔案
是什麼?
require.context()
是實現前端工程化 動態匯入檔案的方法
為什麼?
- 隨著專案業務越來越多,專案的層級目錄越來越多,需要引入的檔案越來越多時(幾十個、幾百個),通過
import
分別引入會導致程式碼重複了很大。
import a11 from '@/components/test/a11' import b11 from '@/components/test/b11' import c11 from '@/components/test/c11' import d11 from '@/components/test/d11' components:{ a11, b11, c11, d11, }
怎麼做?
入參
-
directory { String } - 讀取檔案的路徑
-
useSubdirectories { Boolean } - 是否遍歷檔案的子目錄
-
regExp { RegExp } - 匹配檔案的正則
require.context函式執行後返回一個函式,函式的三個屬性:
-
resolve { Function } - 接受一個引數request,request為test資料夾下面匹配檔案的相對路徑,返回這個匹配檔案相對於整個工程的相對路徑
-
keys { Function } - 返回匹配成功模組的名字組成的陣列
-
id { String } - 執行環境的id,返回的是一個字串
const path = require('path'); // files 是一個函式 const files = require.context('@/components/test', false, /\.vue$/) const modules = {} // files.keys() -->['./a11.vue','./b11.vue','./c11.vue'] files.keys().forEach( key => { // 提取出用'/'隔開的path的最後一部分 // path.basename(p, [ext]) p--要處理的path,ext要過濾的字元 const name = path.basename(key, '.vue'); modules[name] = files(key).default || files(key) components: modules })
應用
1 介面封裝時,實現自動引入同一資料夾下多個檔案(替代import)
/*
* 統一所有module的api
* 獲取module目錄下所有js檔案,注意:不獲取子目錄
* 把js檔案的名字作為key,檔案中export default的東西作為value
* apis[key] = value
* 輸出apis
* */
/*
* require.context(directory, useSub, reg)
* 引數: 讀取檔案的路徑 是否遍歷檔案的子目錄 匹配檔案的正則表示式
* 返回: 函式;屬性:resolve keys
* resolve: 是一個函式,他返回的是被解析模組的id ,接受一個引數request
* keys: 也是一個函式,他返回的是一個數組,該陣列是由所有可能被上下文模組解析的請求物件組成
* */
const modulesFiles = require.context('./modules', false, /\.js$/); // 第二個argument為false,不處理子目錄
/*
* reduce((total, currentValue, currentIndex, arr)=>{}, initVal)
* 引數: 初始值|計算結束後的返回值 當前元素
* */
const apis = modulesFiles.keys().reduce((modules, modulePath) => {
// 執行modulesFiles 函式,返回一個物件{default: {//檔案內容}, _esModule:true}
const value = modulesFiles(modulePath);
// set './app.js' => 'app'
const moduleName = value.moduleName || modulePath.replace(/^\.\/(.*)\.\w+$/, '$1');
modules[moduleName] = value.default;
return modules;
}, {});
export default apis;
2 使用 axios interceptors 攔截器統一處理表頭 --- 【可省略】
/*
* axios的基本配置及攔截器
* */
import axios from 'axios';
import baseUrl from './baseUrl';
import { getCookie } from "../utils/cookie";
let request = axios.create({
baseURL: baseUrl
});
// interceptors 攔截器
/*
* 每個請求都需要攜帶 token ,所以我們可以使用 axios request 攔截器統一處理
* */
request.interceptors.request.use(config => {
// 鑑權 ticket
const ticket = getCookie('ticket');
if (ticket) config.headers.ticket = ticket;
return config;
}, err => {
return Promise.reject(err);
});
/*
* token 失效問題,當我們token 失效,我們服務端會返回一個特定的錯誤表示,用 axios response 攔截器統一處理
* */
request.interceptors.response.use(response => {
return response;
}, err => {
return Promise.reject(err);
});
export default request;
3 封裝請求
/*
* service = {
* [moduleName]: {
* [name]: function (data, callback, config) {
* axios().then(res => callback(res)).catch(err => callback(err))
* }
* }
* }
*
* 使用示例:
* service.login.in(data, callback, {}) 即可呼叫api/module/login.js中的in的介面,並使用data作為引數,呼叫後會執行callback
*
* */
import api from './api';
import request from './request';
// 獲取url上的rest引數,返回引數列表 /{userId}/{roleId} => ['userId', 'roleId']
function getUrlParams(url) {
return (url.match(/\{(\w+)\}/g) || []).map(param => /\{(\w+)\}/.exec(param)[1]);
}
/*
* 建立一個請求函式
* 閉包,用於儲存isProcessing變數
* 通過isProcessing控制介面不發生多次請求
* */
function createRequest(url, method = 'post') {
let isProcessing = false;
let timeOut = true;
const urlParams = getUrlParams(url); // 獲取url上的rest引數 用於後續處理
//encryResquest 控制引數是否集體加密
//encryResponse 控制回參是否需要解密
return function (data, callback, config = {}, force = false) {
// force 傳true跳過不同時多次請求限制
if (isProcessing && !force) return;
isProcessing = true;
let headerIn = {
headers:{}
}
if(sessionStorage.getItem("userTk"))headerIn.headers.Authorization = sessionStorage.getItem("userTk")
config = Object.assign(headerIn,config)
console.info("工程頭部",config)
let option = Object.assign({
url: url,
method: method,
}, config);
// 處理rest引數, 把url上的{param}替換,並且把data對應的key刪掉,防止引起可能的400錯誤
urlParams.forEach(param => {
option.url = option.url.replace(`{${param}}`, data[param]);
Reflect.deleteProperty(data, param);
});
if (method === 'post') option.data = data;
if (method === 'get') option.params = data;
request(option)
.then(res => {
console.info("終極引數",res)
isProcessing = false;
var goLogin = false;
if(res.data.statusCode === "403"){
goLogin = true
}else{
var resList = res.data.toString().split('statusCode=')
if(resList.length>1){
var tiplist = resList[1].split(', ')
var code = tiplist.length>0?tiplist[0]:''
if(code === '403'){
goLogin = true
}
}
}
if(goLogin){
alert("超時登入,請重新登入")
setTimeout(()=>{
location.href="/"
},500)
return;
}
callback(res.data,res.headers);
})
.catch(err => {
isProcessing = false;
if (err.response) {
const code = err.response.status;
if(timeOut && code === 401){
timeOut = false
alert("超時登入,請重新登入")
setTimeout(()=>{
timeOut = true
location.href="/"
},500)
return;
}
callback({statusCode: code, message: `服務出錯了:${code},請稍後再試!`});
} else {
console.info(err)
console.error('回撥中程式碼邏輯出問題了!', err);
}
});
}
}
let service = {};
Object.keys(api).forEach(module => {
let moduleReq = service[module] = {};
Object.keys(api[module]).forEach(name => {
const apiConfig = api[module][name];
// 獲取url 是一個string時預設使用post請求,是一個物件時,通過物件的method來呼叫物件的url
if (typeof apiConfig === 'string') {
moduleReq[name] = createRequest(apiConfig);
} else {
moduleReq[name] = createRequest(apiConfig.url, apiConfig.method);
}
});
});
export default service;
4 main.js引入
import request from './service/request';
import service from './service';
Vue.prototype.$http = request;
Vue.prototype.$service = service;