你越是认真生活,你的生活就会越美好
——弗兰克·劳埃德·莱特
《人生果实》经典语录
webpack官网 依赖管理
如果你的request 含有表达式(expressions)
,就会创建一个上下文(context)
,因为webpack
在编译时(compile time)并不清楚具体导入哪个模块。
示例,考虑到我们有包含 .ejs 文件
的如下目录结构:
example_directory │ └───template │ │ table.ejs │ │ table-row.ejs │ │ │ └───directory │ │ another.ejs
当下面的require()
调用被评估解析:
require('./template/' + name + '.ejs');
webpack
解析require()
调用,然后提取出如下一些信息:
Directory: ./template Regular expression: /^.*\.ejs$/
意思是: 在当前文件下的template文件下,匹配以.ejs为结尾的文件
不熟悉正则的同学,可以看看下面的解析
正则解析:
/^.*\.(jpg|gif|png|bmp)$/i
^
: 匹配字符串的开始位置.*
: .匹配任意字符,*匹配数量0到正无穷\.
: 斜杠用来转义,\.
匹配.(jpg|gif|png|bmp)
: 匹配jpg或gif或png或bmp$
: 匹配字符串的结束位置i
: 不区分大小写。合起来就是匹配以.jpg
或.GIF
或…结尾的任意字符串,不区分大小写
会生成一个context module(上下文模块)
。会生成一个context module(上下文模块)
。它包含目录下的所有模块
的引用,如果一个request
符合正则表达式,就能require
进来。
该context module
包含一个map(映射)对象
,会把 requests 翻译成对应的模块 id。
示例 map(映射):
{ "./table.ejs": 42, "./table-row.ejs": 43, "./directory/another.ejs": 44 }
此 context module 还包含一些访问这个 map 对象的 runtime 逻辑。
这意味着webpack 能够支持动态地 require
,但会导致所有可能用到的模块都包含在bundle
中。
我们可以通过require.context() 函数
来创建自己的context
。
可以给这个函数传入三个参数:
目录
,搜索其子目录
,正则表达式
。webpack
会在构建中解析代码中的 require.context()
。
语法如下:
require.context( directory, (useSubdirectories = true), (regExp = /^\.\/.*$/), (mode = 'sync') );
require.context('./test', false, /\.test\.js$/); //(创建出)一个 context,其中文件来自 test 目录,request 以 `.test.js` 结尾。
require.context('../', true, /\.stories\.js$/); // (创建出)一个 context,其中所有文件都来自父文件夹及其所有子级文件夹,request 以 `.stories.js` 结尾。
ps:
传递给 require.context 的参数必须是字面量(literal)!
一个context module
会导出一个(require)函数
,此函数可以接收一个参数:request
。
此导出函数有三个属性:resolve, keys, id
。
resolve
是一个函数,它返回 request 被解析后得到的模块 id。keys
也是一个函数,它返回一个数组
,由所有可能被此 context module处理的请求(译者注:参考下面第二段代码中的 key)组成。如果想引入一个文件夹下面的所有文件
,或者引入能匹配一个正则表达式的所有文件
,这个功能就会很有帮助,例如:
function importAll(r) { r.keys().forEach(r); } importAll(require.context('../components/', true, /\.js$/));
const cache = {}; function importAll(r) { r.keys().forEach((key) => (cache[key] = r(key))); } importAll(require.context('../components/', true, /\.js$/)); // 在构建时(build-time),所有被 require 的模块都会被填充到 cache 对象中
context module
的模块 id. 它可能在你使用 module.hot.accept
时会用到。目录结构如下
src\components\common\index.js
export default { install(Vue) { const ctxRequire = require.context('@/components/common', false, /\.vue$/) debugger console.log('ctxRequire:', ctxRequire) // console.log('ctxRequire.resolve():', ctxRequire.resolve()) console.log('ctxRequire.keys:', ctxRequire.keys()) console.log('ctxRequire.id:', ctxRequire.id) ctxRequire.keys().forEach((filePath) => { const fileName = filePath.replace(/(.*\/)*([^.]+).vue$/ig, '$2') // 获取Vue组件文件名 // "./breadcrumb.vue".replace(/(.*\/)*([^.]+).vue$/ig, '$2') 返回breadcrumb Vue.component(fileName, ctxRequire(filePath).default || ctxRequire(filePath)) }) } }
打印如下
控制台点击ctxRequire
打印的内容,会跳到源码页面,源码如下
var map = { "./VTitle.vue": "./src/components/common/VTitle.vue", "./breadcrumb.vue": "./src/components/common/breadcrumb.vue", "./common-title.vue": "./src/components/common/common-title.vue", "./custom-switch.vue": "./src/components/common/custom-switch.vue", "./file-list.vue": "./src/components/common/file-list.vue", "./more-button.vue": "./src/components/common/more-button.vue", "./nodata.vue": "./src/components/common/nodata.vue", "./pagination.vue": "./src/components/common/pagination.vue", "./previewDialog.vue": "./src/components/common/previewDialog.vue", "./server-type.vue": "./src/components/common/server-type.vue", "./step-nav.vue": "./src/components/common/step-nav.vue", "./svg-icon.vue": "./src/components/common/svg-icon.vue", "./tags.vue": "./src/components/common/tags.vue", "./upload-img.vue": "./src/components/common/upload-img.vue", "./upload-imgs.vue": "./src/components/common/upload-imgs.vue" }; function webpackContext(req) { var id = webpackContextResolve(req); return __webpack_require__(id); } function webpackContextResolve(req) { if(!__webpack_require__.o(map, req)) { var e = new Error("Cannot find module '" + req + "'"); e.code = 'MODULE_NOT_FOUND'; throw e; } return map[req]; } webpackContext.keys = function webpackContextKeys() { return Object.keys(map); }; webpackContext.resolve = webpackContextResolve; module.exports = webpackContext; webpackContext.id = "./src/components/common sync \\.vue$";
src\plugins\common.js
import Vue from 'vue' import commonComps from '../components/common/index' Vue.use(commonComps) // 会执行commonComps的install方法
src\main.js
import './plugins/common' ...
这样后,就可以在Vue任意地方用这些全局组件,不需要另外注册
PS:
ctxRequire(filePath).default || ctxRequire(filePath)
相当于import
进来的模块,假如filePath
的值是"./breadcrumb.vue"
等同于
mport breadcrumb from './breadcrumb.vue'
import breadcrumb from './breadcrumb.vue' export default { install(Vue) { const ctxRequire = require.context('@/components/common', false, /\.vue$/) ctxRequire.keys().forEach((filePath) => { const fileName = filePath.replace(/(.*\/)*([^.]+).vue$/ig, '$2') // 获取Vue组件文件名 if (fileName === 'breadcrumb') { console.log('breadcrumb:', breadcrumb) console.log(ctxRequire(filePath).default || ctxRequire(filePath)) console.log(breadcrumb === (ctxRequire(filePath).default || ctxRequire(filePath))) } console.log('ctxRequire(filePath):', ctxRequire(filePath)) Vue.component(fileName, ctxRequire(filePath).default || ctxRequire(filePath)) }) } }
打印如下
Vue源码学习完整目录
谢谢你阅读到了最后~
期待你关注、收藏、评论、点赞~
让我们一起 变得更强