在vue项目中,我们拆分某些组件,然后在使用组件的页面import进来。这样就会导致一个问题。
import BaseButton from './BaseButton.vue'
import BaseIcon from './BaseIcon.vue'
import BaseInput from './BaseInput.vue'
import BaseInput from './BaseWrap.vue'
import BaseInput from './BaseInfo.vue'
... 下面引入大量的组件
问题显而易见,引入很多组件不仅看上去不美观,同时也增加了一定的维护成本。
这个时候我们就可以利用require.context对于这些组件进行引入注册。
(其实在vue官网已经给出了例子https://cn.vuejs.org/v2/guide/components-registration.html#%E5%9F%BA%E7%A1%80%E7%BB%84%E4%BB%B6%E7%9A%84%E8%87%AA%E5%8A%A8%E5%8C%96%E5%85%A8%E5%B1%80%E6%B3%A8%E5%86%8C
)
简单的来说就是可以给这个函数传入三个参数:一个要搜索的目录,一个标记表示是否还搜索其子目录, 以及一个匹配文件的正则表达式。
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'
const requireComponent = require.context(
// 其组件目录的相对路径
'./components',
// 是否查询其子目录
false,
// 匹配基础组件文件名的正则表达式
/Base[A-Z]\w+\.(vue|js)$/
)
requireComponent.keys().forEach(fileName => {
// 获取组件配置
const componentConfig = requireComponent(fileName)
// 这个地方直接传入filename其实就是内部会调用了resolve方法,会返回对应的文件内容(不理解可以console一下看看)
// 获取组件的 PascalCase 命名
const componentName = upperFirst(
camelCase(
// 获取和目录深度无关的文件名
fileName
.split('/')
.pop()
.replace(/\.\w+$/, '')
)
)
// 全局注册组件
Vue.component(
componentName,
// 如果这个组件选项是通过 `export default` 导出的,
// 那么就会优先使用 `.default`,
// 否则回退到使用模块的根。
componentConfig.default || componentConfig
)
})
webpack官网上面的解释:
如果你的 request 含有表达式(expressions),就会创建一个上下文(context),因为在编译时(compile time)并不清楚 具体 导入哪个模块。会生成一个 context module(上下文模块)。会生成一个 context module(上下文模块)。它包含 目录下的所有模块 的引用,如果一个 request 符合正则表达式,就能 require 进来。该context module包含一个map(映射)对象,会把requests翻译成对应的模块id。
wepack大概就是会解析成下面这样
var map = { "./approvals/AAdditionalBudgetApprove.vue": "./src/components/approvals/AAdditionalBudgetApprove.vue",
"./approvals/AMonthlyBudgetApprove.vue": "./src/components/approvals/AMonthlyBudgetApprove.vue",
"./approvals/ASpecialInMonthlyApprove.vue": "./src/components/approvals/ASpecialInMonthlyApprove.vue",
"./approvals/ASpecialOutsideMonthlyApprove.vue": "./src/components/approvals/ASpecialOutsideMonthlyApprove.vue",
"./approvals/BAdditionalBudgetApprove.vue": "./src/components/approvals/BAdditionalBudgetApprove.vue",
"./approvals/BMonthlyBudgetApprove.vue": "./src/components/approvals/BMonthlyBudgetApprove.vue",
"./approvals/BSpecialInMonthlyApprove.vue": "./src/components/approvals/BSpecialInMonthlyApprove.vue",
"./details/AAdditionalBudgetDetail.vue": "./src/components/details/AAdditionalBudgetDetail.vue",
"./details/AMonthlyBudgetDetail.vue": "./src/components/details/AMonthlyBudgetDetail.vue",
"./details/ASpecialInMonthlyDetail.vue": "./src/components/details/ASpecialInMonthlyDetail.vue",
"./details/ASpecialOutsideMonthlyDetail.vue": "./src/components/details/ASpecialOutsideMonthlyDetail.vue",
"./details/BAdditionalBudgetDetail.vue": "./src/components/details/BAdditionalBudgetDetail.vue",
"./details/BMonthlyBudgetDetail.vue": "./src/components/details/BMonthlyBudgetDetail.vue",
"./details/BSpecialInMonthlyDetail.vue": "./src/components/details/BSpecialInMonthlyDetail.vue"
};
function webpackContext(req) {
var id = webpackContextResolve(req);
return __webpack_require__(id);
}
function webpackContextResolve(req) {
var id = map[req];
if(!(id + 1)) { // check for number or string
var e = new Error("Cannot find module '" + req + "'");
e.code = 'MODULE_NOT_FOUND';
throw e;
}
return id;
}
webpackContext.keys = function webpackContextKeys() {
return Object.keys(map);
};
webpackContext.resolve = webpackContextResolve;
module.exports = webpackContext;
webpackContext.id= "./src/components sync recursive (Detail|Approve)\\.vue$";
怎么样是不是明白一些了。
我们再来require.context函数会返回三个属性:resolve, keys, id。
resolve 是一个函数,它返回 request 被解析后得到的模块 id。
keys 也是一个函数,它返回一个数组,由所有可能被此 context module 处理的请组成。
对照上面的代码再来看这些参数:
keys 方法其实就是对map映射做了一个获取key的操作
然后resolve的时候需要传递相应的map 映射对象的key获取对应的文件
id 是 context module 的模块 id. (感觉像是解析了一下正则然后拼接的路径,这个感觉没啥大用)
来看webpack官网的解释:
id 是 context module 的模块 id. 它可能在你使用 module.hot.accept 时会用到。