前言
这是一个过程曲折、结局温暖的动人故事。
知识点:ES6 import()动态加载模块和webpack的require.context。
先看下重构前的代码
// mock/index.js
import getProvinceCity from './data/getProvinceCity.js'
import getAcListInfo from './data/getAcListInfo.js'
import sendFreezeApply from './data/sendFreezeApply.js'
import getFreezeList from './data/getFreezeList.js'
import getProjectInfo from './data/getProjectInfo.js'
import getProjectList from './data/getProjectList.js'
import getGuaranteeType from './data/getGuaranteeType.js'
import getToken from './data/getToken.js'
import checkProjectUser from './data/checkProjectUser.js'
import checkAiBinding from './data/checkAiBinding.js'
import getAcctType from './data/getAcctType.js'
import getVeriCode from './data/getVeriCode.js'
import getJsApi from './data/getJsApi.js'
import getWeixinUser from './data/getWeixinUser.js'
import getDeductionList from './data/getDeductionList.js'
import getGuaranteeFreezeInfo from './data/getGuaranteeFreezeInfo.js'
import checkCard from './data/checkCard.js'
import approvePay from './data/approvePay.js'
import checkPwd from './data/checkPwd.js'
Mock.mock(/getProvinceCity$/, getProvinceCity)
Mock.mock(/getAcListInfo$/, getAcListInfo)
Mock.mock(/sendFreezeApply$/, sendFreezeApply)
Mock.mock(/getFreezeList$/, getFreezeList)
Mock.mock(/getProjectInfo$/, getProjectInfo)
Mock.mock(/getProjectList$/, getProjectList)
Mock.mock(/getGuaranteeType$/, getGuaranteeType)
Mock.mock(/getToken$/, getToken)
Mock.mock(/checkProjectUser$/, checkProjectUser)
Mock.mock(/checkAiBinding$/, checkAiBinding)
Mock.mock(/getAcctType$/, getAcctType)
Mock.mock(/getVeriCode$/, getVeriCode)
Mock.mock(/getJsApi$/, getJsApi)
Mock.mock(/getWeixinUser$/, getWeixinUser)
Mock.mock(/getDeductionList$/, getDeductionList)
Mock.mock(/getGuaranteeFreezeInfo$/, getGuaranteeFreezeInfo)
Mock.mock(/checkCard$/, checkCard)
Mock.mock(/approvePay$/, approvePay)
Mock.mock(/checkPwd$/, checkPwd)
就是对mockjs最基础的应用:引入静态数据、mock匹配请求路径、返回对应数据。
没一点毛病!除了太普通显得有点low。
low不是错,让我不能忍的是每次新增操作都要CV好几次(最好情况需要复制3次粘贴6次),很累,感觉身体被掏空。
重构势在必行!
for循环走起
不难看出,import和mock都是重复的,先无脑走个for循环。
下面是改造后的代码:
// mock/index.js
const urls = [
'getProvinceCity',
'getAcListInfo',
'sendFreezeApply',
'getFreezeList',
'getProjectInfo',
'getProjectList',
'getGuaranteeType',
'getToken',
'checkProjectUser',
'checkAiBinding',
'getAcctType',
'getVeriCode',
'getJsApi',
'getWeixinUser',
'getDeductionList',
'getGuaranteeFreezeInfo',
'checkCard',
'approvePay',
'checkPwd'
]
for (let i = 0; i < urls.length; i++) {
const url = urls[i]
import data from `./data/${url}.js` // eslint 报错
Mock.mock(new RegExp(`${url}$`), data)
}
这样以后再有新增,CV一次就好!!!舒服……
可惜,还没试运行,eslint就给报警了:
Parsing error: 'import' and 'export' may only appear at the top level
于是特意回去翻了ECMAScript 6 入门 Module那一章,人说的很明白:
由于import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。
引擎处理import语句是在编译时,这时不会去分析或执行if语句,所以import语句放在if代码块之中毫无意义,因此会报句法错误,而不是执行时错误。也就是说,import和export命令只能在模块的顶层,不能在代码块之中(比如,在if代码块之中,或在函数之中)
此路不通,好在后面有解决方案:
ES2020提案 引入import()函数,支持动态加载模块。
使用import函数动态加载模块
这个简单,动态加载模块走起。
// mock/index.js
for (let i = 0; i < urls.length; i++) {
const url = urls[i]
// 动态加载模块
import(`./data/${url}.js`).then(data => {
Mock.mock(new RegExp(`${url}$`), data.default)
})
}
完美!!!不报错了……
欢天喜地的打开页面一看……一直转圈圈。点开network,果然是没拦截住,有两个接口被发送出去了。
又点了其他页面,好的?!……除了这两个接口,其他都是能正常被mock的……
第一时间怀疑是拼写错误,再三比对确认后,拼写无误!
随后在页面又试了几次,发现个规律:只有这两个接口没被mock,且它们都是在created中被调用的。
此时‘js执行机制’、‘异步逻辑处理’的知识点纷至沓来,确定是异步问题无疑了。
赶紧看下main.js:
// main.js
import './mock/index.js'
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
// mock/index.js
// 动态加载模块
import(`./data/${url}.js`).then(data => {
Mock.mock(new RegExp(`${url}$`), data.default)
})
不难看出,mock/index.js里面动态加载模块是promise,mock会进异步队列,然后就被排在vue实例化之后执行了。
真相大白!vue实例化过程中(created)调用接口时,mock还没执行,也就自然不会拦截请求了……
解决动态加载模块造成的异步问题
两个方案:Vue实例化延迟执行、mock/index.js模块Promise化。我选择了前者……
// main.js
const useMock = true // 是否使用MOCK
if (useMock && process.env.NODE_ENV === 'development') {
import('./mock/index.js')
}
// 由于mock中是动态加载的模块,所以当开启mock时需要延迟实例化
setTimeout(() => {
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
}, useMock ? 100 : 0)
使用require.context实现免配置
经评论区蛋蛋大神的指点,使用webpack的require.context读取文件列表,达成了免配置效果!
以后有新增的话只在data目录下添加js文件就好……
最终代码:
// mock/index.js
import Mock from 'mockjs'
const dataFilesContexts = require.context('./data', false, /\.js$/)
const urls = dataFilesContexts.keys().map(item => {
//从uri中匹配出文件名
const fileNameArr = item.match(/(?<=\/).*(?=.js)/) || []
return fileNameArr[0]
})
for (let i = 0; i < urls.length; i++) {
const url = urls[i]
// 动态加载模块
import(`./data/${url}.js`).then(data => {
Mock.mock(new RegExp(`${url}$`), data.default)
})
}
写在最后
把需要“复制3次粘贴6次”的操作降到了零次,这一刻,心中的某种情感被唤醒了。
好似一阵风,吹得头上“CV程序员”的帽子晃了一晃。
我伸手按住,抬起头,开心的像个孩子……