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;