1. 程式人生 > 其它 >vue請求介面封裝

vue請求介面封裝

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,
}

怎麼做?

入參
  1. directory { String } - 讀取檔案的路徑

  2. useSubdirectories { Boolean } - 是否遍歷檔案的子目錄

  3. regExp { RegExp } - 匹配檔案的正則

require.context函式執行後返回一個函式,函式的三個屬性:
  1. resolve { Function } - 接受一個引數request,request為test資料夾下面匹配檔案的相對路徑,返回這個匹配檔案相對於整個工程的相對路徑

  2. keys { Function } - 返回匹配成功模組的名字組成的陣列

  3. 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;