由require.context引发的思考

本文探讨了webpack的API require.context,用于自动化导入模块,并详细解释了其参数和返回的函数特性。同时,文章对比了ES6模块与CommonJS模块的差异,包括导出方式和加载机制。最后,讨论了全局注册与按需引入组件的策略及其适用场景。

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 }导出。

参考:vue官方示例基础组件的自动化全局注册

模块导出方式

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 模块的所有输出接口,会成为输入对象的属性。

参考:Module 的加载实现

模块的全局注册和按需引入

还有个问题就是,我们在加载页面的过程中,希望组件也是按需加载的。如果是通过export default全部导出,那么当页面需要引用其中某些组件时,是不是将所有的组件都打包引入了呢?全局注册的意思是不是已经全部被引入?

对于所有组件都在项目中被用到的情况来说,全局注册和按需引入的方式在打包和运行效率上并没有什么区别,只是相对来说按需引入可读性更强一点。
对于引入外部组件库来说,按需引入的方式则更好。(webpack 摇树处理 Tree Shaking)

Last login: Wed May 28 22:57:11 on ttys000 /Users/wangxuguang/.zshrc:3: no such file or directory: /usr/local/bin/brew /Users/wangxuguang/.zshrc:export:3: not valid in this context: wangxuguang@MacBookPro ~ % pipx install enex2notion zsh: command not found: pipx wangxuguang@MacBookPro ~ % pip Usage: pip <command> [options] Commands: install Install packages. lock Generate a lock file. download Download packages. uninstall Uninstall packages. freeze Output installed packages in requirements format. inspect Inspect the python environment. list List installed packages. show Show information about installed packages. check Verify installed packages have compatible dependencies. config Manage local and global configuration. search Search PyPI for packages. cache Inspect and manage pip's wheel cache. index Inspect information available from package indexes. wheel Build wheels from your requirements. hash Compute hashes of package archives. completion A helper command used for command completion. debug Show information useful for debugging. help Show help for commands. General Options: -h, --help Show help. --debug Let unhandled exceptions propagate outside the main subroutine, instead of logging them to stderr. --isolated Run pip in an isolated mode, ignoring environment variables and user configuration. --require-virtualenv Allow pip to only run in a virtual environment; exit with an error otherwise. --python <python> Run pip with the specified Python interpreter. -v, --verbose Give more output. Option is additive, and can be used up to 3 times. -V, --version Show version and exit. -q, --quiet Give less output. Option is additive, and can be used up to 3 times (corresponding to WARNING, ERROR, and CRITICAL logging levels). --log <path> Path to a verbose appending log. --no-input Disable prompting for input. --keyring-provider <keyring_provider> Enable the credential lookup via the keyring library if user input is allowed. Specify which mechanism to use [auto, disabled, import, subprocess]. (default: auto) --proxy <proxy> Specify a proxy in the form scheme://[user:passwd@]proxy.server:port. --retries <retries> Maximum attempts to establish a new HTTP connection. (default: 5) --timeout <sec> Set the socket timeout (default 15 seconds). --exists-action <action> Default action when a path already exists: (s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort. --trusted-host <hostname> Mark this host or host:port pair as trusted, even though it does not have valid or any HTTPS. --cert <path> Path to PEM-encoded CA certificate bundle. If provided, overrides the default. See 'SSL Certificate Verification' in pip documentation for more information. --client-cert <path> Path to SSL client certificate, a single file containing the private key and the certificate in PEM format. --cache-dir <dir> Store the cache data in <dir>. --no-cache-dir Disable the cache. --disable-pip-version-check Don't periodically check PyPI to determine whether a new version of pip is available for download. Implied with --no-index. --no-color Suppress colored output. --use-feature <feature> Enable new functionality, that may be backward incompatible. --use-deprecated <feature> Enable deprecated functionality, that will be removed in the future. --resume-retries <resume_retries> Maximum attempts to resume or restart an incomplete download. (default: 0) wangxuguang@MacBookPro ~ % pip3 install enex2notion error: externally-managed-environment × This environment is externally managed ╰─> To install Python packages system-wide, try brew install xyz, where xyz is the package you are trying to install. If you wish to install a Python library that isn't in Homebrew, use a virtual environment: python3 -m venv path/to/venv source path/to/venv/bin/activate python3 -m pip install xyz If you wish to install a Python application that isn't in Homebrew, it may be easiest to use 'pipx install xyz', which will manage a virtual environment for you. You can install pipx with brew install pipx You may restore the old behavior of pip by passing the '--break-system-packages' flag to pip, or by adding 'break-system-packages = true' to your pip.conf file. The latter will permanently disable this error. If you disable this error, we STRONGLY recommend that you additionally pass the '--user' flag to pip, or set 'user = true' in your pip.conf file. Failure to do this can result in a broken Homebrew installation. Read more about this behavior here: <https://peps.python.org/pep-0668/> note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages. hint: See PEP 668 for the detailed specification. wangxuguang@MacBookPro ~ %
05-29
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值