持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情
相关简介
-
前言 之前在使用
Vue开发项目的时候,会用到webpack提供的一个Api接口:require.context,基于该Api可以很方便的检索和匹配对应的文件,继而实现模块自动化的导入。
但是,在node.js里面就没有这个好用的Api方法,所以这里就基于node.js实现一个简单的require.context方法。 -
介绍 要实现一个
Api方法,就得先了解它的基本使用方法,先来看看require.context的基本用法:
const cache = {};
function importAll(r) {
r.keys().forEach((key) => (cache[key] = r(key)));
}
importAll(require.context('../components/', true, /.js$/));
从上面的代码中,可以看到:require.context可以接受三个参数:
- directory:第一个参数,表示需要进行匹配的目录;
- useSubdirectories: 第二个参数,表示是否匹配子级目录;
- regExp:第三个参数,匹配文件使用的正则表达式。
接着,可以知道该方法返回一个方法对象(上下文模块API),该对象可以通过keys方法遍历出匹配到的文件;并且,通过该对象可以再次执行得到模块内容。
基本实现
知道require.context的基本使用后,就可以基于此进行一个简单的模拟,步骤如下:
- 自定义
requireContext方法
const fs = require('fs')
const resolve = require('path').resolve
const requireContext = (dirPath, deep = false, reg) => {}
- 通过
path.resolve方法来解析路径:
const fs = require('fs')
const resolve = require('path').resolve
const requireContext = (dirPath, deep = false, reg) => {
dirPath = resolve(process.cwd(), dirPath)
}
- 定义读取目录的方法,该方法如下:
1). 通过readdirSync读取目录信息;
2). 通过statSync在遍历目录信息时,区分出文件和目录格式,并对对应处理;
3). 通过递归的方式,读取玩目录中的所有内容。 代码实现如下:
const fs = require('fs')
const resolve = require('path').resolve
const readDirSync = dirPath => {
const result = [], dirs = []
const files = fs.readdirSync(dirPath)
files.forEach(file => {
const stat = fs.statSync(resolve(dirPath, file))
stat.isDirectory() ? dirs.push(file) : result.push(resolve(dirPath, file))
})
dirs.forEach(dir => result.push(...readDirSync(resolve(dirPath, dir))))
return result
}
- 判断是否需要读取子级目录,并做不同处理:
1). 如果需要读取子级目录,则通过定义好的readdirSync递归处理;
2). 如果不需要读取子级目录,则直接使用fs.readdirSync读取,并过滤掉目录文件。
const fs = require('fs')
const resolve = require('path').resolve
const requireContext = (dirPath, deep = false, reg) => {
dirPath = resolve(process.cwd(), dirPath)
let files = deep
? readDirSync(dirPath)
: fs.readdirSync(dirPath).filter(file => !fs.statSync(resolve(dirPath, file)).isDirectory())
}
- 使用正则表达式配合
filter方法过滤文件:
const fs = require('fs')
const resolve = require('path').resolve
const requireContext = (dirPath, deep = false, reg) => {
dirPath = resolve(process.cwd(), dirPath)
let files = deep
? readDirSync(dirPath)
: fs.readdirSync(dirPath).filter(file => !fs.statSync(resolve(dirPath, file)).isDirectory())
if (reg instanceof RegExp) {
files = files.filter(file => reg.test(file))
}
}
- 返回上下文模块对象,并实现
keys方法:
const fs = require('fs')
const resolve = require('path').resolve
const requireContext = (dirPath, deep = false, reg) => {
dirPath = resolve(process.cwd(), dirPath)
let files = deep
? readDirSync(dirPath)
: fs.readdirSync(dirPath).filter(file => !fs.statSync(resolve(dirPath, file)).isDirectory())
if (reg instanceof RegExp) {
files = files.filter(file => reg.test(file))
}
const context = file => require(file)
context.keys = () => files.map(file => resolve(dirPath, file))
return context
}
实现以上代码后就可以使用了,下面看看使用的例子(simple-require-context是写的一个npm的demo包,这里方便直接下载引入):
const basename = require('path').basename
const requireContext = require('simple-require-context')
const context = requireContext('./demo', false, /\.js$/)
const modules = {}
context.keys().forEach(m => {
modules[basename(m, '.js')] = context(m)
})
console.log(modules)
文件目录和执行结果如下:
以上就是一个最简单的实现,完整代码如下:
const fs = require('fs')
const resolve = require('path').resolve
const readDirSync = dirPath => {
const result = [], dirs = []
const files = fs.readdirSync(dirPath)
files.forEach(file => {
const stat = fs.statSync(resolve(dirPath, file))
stat.isDirectory() ? dirs.push(file) : result.push(resolve(dirPath, file))
})
dirs.forEach(dir => result.push(...readDirSync(resolve(dirPath, dir))))
return result
}
const requireContext = (dirPath, deep = false, reg) => {
dirPath = resolve(process.cwd(), dirPath)
let files = deep ? readDirSync(dirPath) : fs.readdirSync(dirPath).filter(file => !fs.statSync(resolve(dirPath, file)).isDirectory())
if (reg instanceof RegExp) {
files = files.filter(file => reg.test(file))
}
const context = file => require(file)
context.keys = () => files.map(file => resolve(dirPath, file))
return context
}
module.exports = requireContext
除此之外,require.context方法还有其他的一些实现,因为平时没用到就不琢磨了,感兴趣的可以看看。
至此,就已经简单实现了require.context自动模块化引入了:)