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;