glob 在正則出現(xiàn)之前就有了锭环,主要用于匹配文件路徑,例如大名鼎鼎的 gulp 就使用了 glob 規(guī)則來匹配泊藕、查找并處理各種后綴的文件辅辩。在前端工程化的過程中,不可避免地會用 Node.js 來讀取文件,例如想找到 src
目錄下所有 js
和 jsx
文件玫锋,代碼應(yīng)該怎么寫呢蛾茉?首先安裝依賴包:
yarn add glob
然后 3 行代碼搞定:
const glob = require('glob')
const files = glob.sync('src/**/*.js{,x}')
console.log(files)
有沒有感覺很強大呢?更重要的是 glob 語法在命令行就支持撩鹿,不需要安裝任何依賴谦炬,例如老板讓你創(chuàng)建 a1.js
到 a9.js
、b1.js
到 b9.js
這 18 個測試文件的話节沦,怎么操作键思?一個個創(chuàng)建的話太傻了,glob 一句話就搞定:
$ touch {a,b}{1..9}.js
$ ls
a1.js a3.js a5.js a7.js a9.js b2.js b4.js b6.js b8.js
a2.js a4.js a6.js a8.js b1.js b3.js b5.js b7.js b9.js
更更更重要的是甫贯,glob 的語法非常簡單吼鳞,只要記住下面7個符號代表的含義就能掌握了:
- 基礎(chǔ)語法:
/
、*
获搏、?
赖条、[]
- 拓展語法:
**
、{}
常熙、()
接下來就逐個解釋一下:
基礎(chǔ)語法
分隔符和片段
概念:分隔符是/
纬乍,通過split('/')
得到的數(shù)組每一項是片段。
示例:
-
src/index.js
有兩個片段裸卫,分別是src
和index.js
-
src/**/*.js
有三個片段仿贬,分別是src
、**
和*.js
單個星號
概念:單個星號*
用于匹配單個片段中的零個或多個字符墓贿。
示例:
-
src/*.js
表示src
目錄下所有以js
結(jié)尾的文件茧泪,但是不能匹配src
子目錄中的文件,例如src/login/login.js
-
/home/*/.bashrc
匹配所有用戶的.bashrc
文件
需要注意的是聋袋,*
不能匹配分隔符/
队伟,也就是說不能跨片段匹配字符。
問號
概念:問號 ?
匹配單個片段中的單個字符幽勒。
示例:
-
test/?at.js
匹配形如test/cat.js
嗜侮、test/bat.js
等所有3個字符且后兩位是at
的 js 文件,但是不能匹配test/flat.js
-
src/index.??
匹配 src 目錄下以index
打頭啥容,后綴名是兩個字符的文件锈颗,例如可以匹配src/index.js
和src/index.md
,但不能匹配src/index.jsx
中括號
概念:同樣是匹配單個片段中的單個字符咪惠,但是字符集只能從括號內(nèi)選擇击吱,如果字符集內(nèi)有-
,表示范圍遥昧。
示例:
-
test/[bc]at.js
只能匹配test/bat.js
和test/cat.js
-
test/[c-f]at.js
能匹配test/cat.js
覆醇、test/dat.js
朵纷、test/eat.js
和test/fat.js
驚嘆號
概念:表示取反,即排除那些去掉驚嘆號之后能夠匹配到的文件永脓。
示例:
-
test/[!bc]at.js
不能匹配test/bat.js
和test/cat.js
柴罐,但是可以匹配test/fat.js
-
!test/tmp/**'
排除test/tmp
目錄下的所有目錄和文件
擴展語法
基礎(chǔ)語法非常簡單好記,但是功能非常局限憨奸,為了豐富 glob 的功能,衍生了下面三種擴展語法:
兩個星號
概念:兩個星號**
可以跨片段匹配零個或多個字符凿试,也就是說**
是遞歸匹配所有文件和目錄的排宰,如果后面有分隔符,即 **/
的話那婉,則表示只遞歸匹配所有目錄(不含隱藏目錄)板甘。
示例:
-
/var/log/**
匹配/var/log
目錄下所有文件和文件夾,以及文件夾里面所有子文件和子文件夾 -
/var/log/**/*.log
匹配/var/log
及其子目錄下的所有以.log
結(jié)尾的文件 -
/home/*/.ssh/**/*.key
匹配所有用戶的.ssh
目錄及其子目錄內(nèi)的以.key
結(jié)尾的文件
大括號
概念:匹配大括號內(nèi)的所有模式详炬,模式之間用逗號進行分隔盐类,支持大括號嵌套,支持用..
匹配連續(xù)的字符呛谜,即{start..end}
語法在跳。
示例:
-
a.{png,jp{,e}g}
匹配a.png
、a.jpg
隐岛、a.jpeg
-
{a..c}{1..2}
匹配a1 a2 b1 b2 c1 c2
注意:{}
與 []
有一個很重要的區(qū)別:如果匹配的文件不存在猫妙,[]
會失去模式的功能,變成一個單純的字符串聚凹,而 {}
依然可以展開割坠。
小括號
概念:小括號必須跟在 ?
、*
妒牙、+
彼哼、@
、!
后面使用湘今,且小括號里面的內(nèi)容是一組以 |
分隔符的模式集合敢朱,例如:abc|a?c|ac*
。
示例:
-
?(pattern|pattern|pattern)
:匹配0次或1次給定的模式 -
*(pattern|pattern|pattern)
:匹配0次或多次給定的模式 -
+(pattern|pattern|pattern)
:匹配1次或多次給定的模式 -
@(pattern|pattern|pattern)
:嚴格匹配給定的模式 -
!(pattern|pattern|pattern)
:匹配非給定的模式
應(yīng)用場景
webpack 多頁面應(yīng)用自動打包配置
在一個 webpack 項目中象浑,假如我們有多個入口蔫饰,每個入口都有一個 index.html
模板和 index.js
文件,而且這個入口是動態(tài)變化的愉豺,不希望每增加一個入口就改 webpack.config.js
配置文件篓吁,應(yīng)該怎么辦呢?
此時可以約定在 src 下面創(chuàng)建的文件夾蚪拦,只要里面有 index.js
杖剪,我們就把它當做一個入口文件進行打包:
src
├── detail
│ ├── index.html
│ └── index.js
├── home
│ ├── index.html
│ └── index.js
├── login
│ ├── index.html
│ └── index.js
├── shop
│ ├── index.html
│ └── index.js
用 glob 很快就能寫出下面的自動打包代碼:
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path')
const glob = require('glob')
// 動態(tài)生成 entry 和 html-webpack-plugin
function getMpa() {
const entry = {}, htmlPlugins = []
const files = glob.sync('src/*/index.js')
files.forEach((file) => {
const filename = file.split('/')[1]
entry[filename] = path.join(__dirname, file)
htmlPlugins.push(
new HtmlWebpackPlugin({
template: path.join(__dirname, `src/${filename}/index.html`),
filename: `${filename}.html`,
chunks: [filename],
})
)
})
return { entry, htmlPlugins }
}
const mpa = getMpa()
// 動態(tài)的配置文件
module.exports = {
entry: mpa.entry,
output: {
path: path.join(__dirname, 'dist'),
filename: '[name]-[hash:6].js',
},
plugins: [...mpa.htmlPlugins],
}
這樣冻押,無論增加多少個入口,webpack.config.js
都不用變盛嘿。
手寫一個約定大于配置的 Node.js 框架
egg.js 是一款優(yōu)秀的 Node.js 企業(yè)級開發(fā)框架洛巢,就應(yīng)用了約定大于配置的思想,例如:
- 約定一個中間件是一個放置在
app/middleware
目錄下的單獨文件 - 約定了
app/router.js
文件用于統(tǒng)一所有路由規(guī)則 - 約定 Service 文件必須放在
app/service
目錄次兆,可以支持多級目錄稿茉,訪問的時候可以通過目錄名級聯(lián)訪問
因為一個大規(guī)模的團隊需要遵循一定的約束和約定,開發(fā)效率才更高芥炭,有了這些約定之后漓库,我們就可以利用 glob 寫出匹配規(guī)則,找到用戶放到指定目錄下的文件并進行動態(tài)加載了园蝠,一個最基礎(chǔ)的 load 函數(shù)如下:
function load(folder, options) {
const extname = options.extname || '.{js,ts}'
return glob.sync(require('path').join(folder, `./**/*.js`)).forEach((item) => require(item))
}
使用 glob渺蒿,配合相應(yīng)的規(guī)范,例如 RESTful彪薛,我們自己也能封裝一個簡易的茂装、基于約定的 Node.js 框架了。