require.context
一个webpack的api,通过执行require.context函数获取一个特定的上下文,主要用来实现自动化导入模块,在前端工程中,如果遇到从一个文件夹引入很多模块的情况,可以使用这个api,它会遍历文件夹中的指定文件,然后自动导入,使得不需要每次显式的调用import导入模块
我想使用require.context引入components目录下的所有组件:
webpack会在构建的时候解析代码中的require.context()。
require.context函数接收三个参数:
- directory {String} -读取文件的路径
- useSubdirectories {Boolean} -是否遍历文件的子目录
- regExp {RegExp} -匹配文件的正则
// 语法
let files = require.context(directory, useSubdirectories = false, regExp = /^\.\//);
require.context模块导出(返回)一个(require)函数files,这个函数有三个属性:
- resolve:是一个函数,接受一个参数:directory下面匹配文件的相对路径,返回这个匹配文件相对于整个工程的相对路径。
- keys:也是一个函数,它返回一个数组,返回匹配成功模块的名字组成的数组(不包括文件夹名称)。
- id:执行环境的id,返回的是一个字符串,主要用在module.hot.accept
const files = require.context('.', true, /index.vue$/);
/* resolve */
console.log('resolve', files.resolve(files.keys()[0]));
// 输出:resolve ./src/components/ke-fu/index.vue
/* keys */
./ke-fu/index.vue
./layout/index.vue
./loading/src/index.vue
./operation-status/index.vue
./product-grade/index.vue
./scroll-list/index.vue
./slide/index.vue
同时files作为一个函数,也接受一个req参数,这个和resolve方法的req参数是一样的,即匹配的文件名的相对路径,而files函数返回的是一个模块,这个模块才是真正我们需要的。这个Module模块和使用import导入的模块是一样的。
得到components目录下所有的模块之后,需要导出,那么问题来了。这种方式引入的模块,似乎只能通过export default全部导出,而不能通过export { …allModules }导出。
模块导出方式
ES6 模块与 CommonJS 模块完全不同。重大差异:
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
- ES6 模块之中,顶层的this指向undefined;CommonJS 模块的顶层this指向当前模块
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。【CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。】
// 报错
let allModules = {
Layout,
VLoading,
};
export allModules
// 正确
export {
Layout,
VLoading,
};
// 正确
export let allModules = {
Layout,
VLoading,
}
// 正确
// 本质上,export default就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字。export default a的含义是将变量a的值赋给变量default
export default allModules
ES6 模块加载 CommonJS 模块
CommonJS 模块的输出都定义在module.exports这个属性上面。Node 的import命令加载 CommonJS 模块,Node 会自动将module.exports属性,当作模块的默认输出,即等同于export default xxx。【CommonJS 模块的输出缓存机制,在 ES6 加载方式下依然有效。】
// a.js
module.exports = {
foo: 'hello',
bar: 'world'
};
// 等同于
export default {
foo: 'hello',
bar: 'world'
};
import命令加载上面的模块,module.exports会被视为默认输出,即import命令实际上输入的是这样一个对象{ default: module.exports }
// 写法一
import baz from './a';
// baz = {foo: 'hello', bar: 'world'};
// 写法二
import {default as baz} from './a';
// baz = {foo: 'hello', bar: 'world'};
// 写法三
import * as baz from './a';
// baz = {
// get default() {return module.exports;},
// get foo() {return this.default.foo}.bind(baz),
// get bar() {return this.default.bar}.bind(baz)
// }
由于 ES6 模块是编译时确定输出接口,CommonJS 模块是运行时确定输出接口,所以采用import命令加载 CommonJS 模块时,不允许采用下面的写法。
// 不正确
import { readFile } from 'fs';
上面的写法不正确,因为fs是 CommonJS 格式,只有在运行时才能确定readFile接口,而import命令要求编译时就确定这个接口。解决方法就是改为整体输入。
// 正确的写法一
import * as express from 'express';
const app = express.default();
// 正确的写法二
import express from 'express';
const app = express();
CommonJS 模块加载 ES6 模块
CommonJS 模块加载 ES6 模块,不能使用require命令,而要使用import()函数。ES6 模块的所有输出接口,会成为输入对象的属性。
模块的全局注册和按需引入
还有个问题就是,我们在加载页面的过程中,希望组件也是按需加载的。如果是通过export default全部导出,那么当页面需要引用其中某些组件时,是不是将所有的组件都打包引入了呢?全局注册的意思是不是已经全部被引入?
对于所有组件都在项目中被用到的情况来说,全局注册和按需引入的方式在打包和运行效率上并没有什么区别,只是相对来说按需引入可读性更强一点。
对于引入外部组件库来说,按需引入的方式则更好。(webpack 摇树处理 Tree Shaking)
本文探讨了webpack的API require.context,用于自动化导入模块,并详细解释了其参数和返回的函数特性。同时,文章对比了ES6模块与CommonJS模块的差异,包括导出方式和加载机制。最后,讨论了全局注册与按需引入组件的策略及其适用场景。
1049

被折叠的 条评论
为什么被折叠?



