在日常使用 Node 進行開發(fā)的時候,,會使用到一些文件系統(tǒng),、路徑操作等基礎(chǔ) API,,這里整理一下,方便大家理解和直接使用,。
這里只介紹最常用的那些,,不是所有哈,想要看更全的,,直接看官方文檔[1]就 OK,。
盡量不廢話,多上代碼,。
Process 模塊
先介紹 process 模塊,,它提供了當(dāng)前 Node 進程相關(guān)的全局環(huán)境信息。在后面的 API 中被用到,。
// 內(nèi)置模塊,,直接使用
const process = require('process');
process.cwd()
這是一個函數(shù),返回當(dāng)前 Node 進程執(zhí)行的目錄,,舉例一個常見的場景:
一個 Node 模塊 A
通過 NPM 發(fā)布,項目 B
中使用了模塊 A
,。在 A
中需要操作 B
項目下的文件時,,就可以用 process.cwd()
來獲取 B
項目的路徑。
const cwd = process.cwd(); // 輸出:/Users/xiaolian/Code/node-api-test
process.argv
在終端通過 Node 執(zhí)行命令的時候,,通過 process.argv
可以獲取傳入的命令行參數(shù),,返回值是一個數(shù)組:
- 1: 被執(zhí)行的 JS 文件路徑(一般用不到,,直接忽略)
所以,,我們只要從 process.argv[2]
開始獲取就好了,。一般都是這樣用:
const args = process.argv.slice(2);
直接獲取我們想要的參數(shù)。
process.env
返回一個對象,,存儲當(dāng)前環(huán)境相關(guān)的所有信息,,一般很少直接用到。
一般我們會在 process.env
上掛載一些變量標(biāo)識當(dāng)前的環(huán)境,。比如最常見的用 process.env.NODE_ENV
區(qū)分 development
和 production
,。在 vue-cli
的源碼中也經(jīng)常會看到 process.env.VUE_CLI_DEBUG
標(biāo)識當(dāng)前是不是一 DEBUG
模式。
這里提一個 webpack 的插件 DefinePlugin[2],,在日常的構(gòu)建流程中,,我們經(jīng)常會通過這個插件來注入不同的全局變量,從而執(zhí)行不同的構(gòu)建流程,,并且代碼中的 process.env.xxx
會被替換成具體的值,,在 Terser 壓縮階段會將 deadCode 移除,優(yōu)化代碼體積,。
process.platform
這個用的不多,,返回當(dāng)前系統(tǒng)信息,枚舉值如下:
console.log(process.platform);
// 'aix'
// 'darwin' - macOS
// 'freebsd'
// 'linux' - linux
// 'openbsd'
// 'sunos'
// 'win32' - windows
Path 模塊
// 內(nèi)置模塊,,直接使用
const path = require('path');
Node 中幾乎路徑相關(guān)的操作都會使用這個模塊,。
這里就說 5 個最常用的:
path.join(...paths)
path.join
作用是將傳入的多個路徑拼成一個完整的路徑。
const dPath = path.join('template', 'aaa', 'bbb', 'ccc', 'd.js');
// 輸出: template/aaa/bbb/ccc/d.js
來看一個非常常見的場景,,我們需要獲取當(dāng)前項目的 package.json 文件,,就可以這樣獲取它的路徑:
const pkgPath = path.join(process.cwd(), './package.json');
// 輸出: /Users/xiaolian/Code/node-api-test/package.json
path.join
可以傳入任意個路徑,比如:
['package.json', 'README.md'].forEach(fileName => {
const templateFilePath = path.join(process.cwd(), 'template', fileName);
console.log(templateFilePath);
});
// 輸出: /Users/xiaolian/Code/node-api-test/template/package.json
// 輸出: /Users/xiaolian/Code/node-api-test/template/README.md
path.resolve(...paths)
path.resovle
和 path.join
的區(qū)別在于它的作用是將傳入的多個路徑和當(dāng)前執(zhí)行路徑拼接成一個完整的絕對路徑,。
假設(shè)我現(xiàn)在 index.js
在 scripts
目錄下,,然后我在根目錄下執(zhí)行 node scripts/index.js
,它的代碼如下:
const dPath = path.resolve('aaa', 'bbb', 'ccc', 'd.js');
// 輸出: /Users/xiaolian/Code/node-api-test/aaa/bbb/ccc/d.js
一般情況下,,當(dāng) path.resolve
的第一個參數(shù)為 ./
時,可以直接理解和 path.join(processs.cwd(), '')
表現(xiàn)一致,。
path.basename(path[, ext])
path.basename
返回指定 path
最后一個路徑名,,其中第二個參數(shù) ext
可選,表示文件擴展名,。比如:
console.log(path.basename('scripts/index.js')); // index.js
console.log(path.basename('scripts/index.js', '.js')); // 匹配到 .js,,返回 index
console.log(path.basename('scripts/index.js', '.json')); // 沒匹配到,返回 index.js
path.dirname(path)
和 path.basename
對應(yīng),,返回指定 path
最后一個路徑名之前的路徑,。比如:
console.log(path.basename('scripts/index.js')); // scripts
console.log(path.basename('scripts/hook/index.js')); // scripts/hook
path.extname(path)
和 path.basename
對應(yīng),返回指定 path
最后一個路徑名的文件擴展名(含小數(shù)點 .
),。比如:
console.log(path.basename('scripts/index.js')); // .js
console.log(path.basename('README.md')); // .md
對比
最后再來對比一下各個路徑相關(guān)的 API 的區(qū)別,。
項目 A
的目錄結(jié)構(gòu)如下:
├── scripts
│ └── index.js
├── src
│ └── index.js
├── package.json
├── README.md
scripts/index.js
的代碼如下:
const path = require('path');
console.log(path.join('package.json'));
console.log(path.resolve('package.json'));
console.log(path.join('src', 'index.js'));
console.log(path.resolve('src', 'index.js'));
console.log(path.join(process.cwd(), 'package.json'));
console.log(path.resolve('./', 'package.json'));
console.log(__filename);
console.log(__dirname);
然后,,我們在項目 A
的根目錄下執(zhí)行 node scripts/index.js
,結(jié)果如下:
-> node scripts/index.js
package.json
/Users/xiaolian/Code/A/package.json
src/index.js
/Users/xiaolian/Code/A/src/index.js
/Users/xiaolian/Code/A/package.json
/Users/xiaolian/Code/A/package.json
/Users/xiaolian/Code/A/scripts/index.js
/Users/xiaolian/Code/A/scripts
品,,仔細品,,它們有什么區(qū)別。
個人而言,,一般還是習(xí)慣用 path.join(process.cwd(), 'xxx')
,。
File System 模塊
// 內(nèi)置模塊,直接使用
const fs = require('fs');
文件系統(tǒng)相關(guān)操作的模塊,,除了 fs
之外,,我們還經(jīng)常用到 fs-extra
,后面會介紹,。
這個模塊在平時的 Node 開發(fā)中會被大量使用,,這里簡單列幾個,其它的還是看文檔哈:https:///dist/latest-v14.x/docs/api/fs.html[3]
fs
模塊的 API 默認都是異步回調(diào)的形式,,如果你想使用同步的方法,,有兩種解決方法:
- 使用 Node 提供的同步 API:
xxxSync
,也就是在 API 的后面加一個 Sync
后綴,,它就是一個同步方法了(具體還是需要查文檔哈,,是否有提供同步 API)
fs.stat(path[, options], callback)
fs.stat()
返回一個文件或者目錄的信息。
const fs = require('fs');
fs.stat('a.js', function(err, stats) {
console.log(stats);
});
其中包含的參數(shù)有很多,,介紹幾個比較常用的:
export interface StatsBase<T> {
isFile(): boolean; // 判斷是否是一個文件
isDirectory(): boolean; // 判斷是否一個目錄
size: T; // 大?。ㄗ止?jié)數(shù))
atime: Date; // 訪問時間
mtime: Date; // 上次文件內(nèi)容修改時間
ctime: Date; // 上次文件狀態(tài)改變時間
birthtime: Date; // 創(chuàng)建時間
}
一般我們會使用 fs.stat
來取文件的大小,做一些判斷邏輯,,比如發(fā)布的時候可以檢測文件大小是否符合規(guī)范,。在 CLI 中,經(jīng)常需要獲取一個路徑下的所有文件,,這時候也需要使用 fs.stat
來判斷是目錄還是文件,,如果是目錄則繼續(xù)遞歸。當(dāng)然,,現(xiàn)在也有更方便的 API 可以完成這個工作,。
同步方法
const fs = require('fs');
try {
const stats = fs.statSync('a.js');
} catch(e) {}
fs.readdir(path[, options], callback)
fs.readdir(path)
獲取 path
目錄下的文件和目錄,返回值為一個包含 file
和 directory
的數(shù)組,。
假設(shè)當(dāng)前目錄為:
.
├── a
│ ├── a.js
│ └── b
│ └── b.js
├── index.js
└── package.json
執(zhí)行以下代碼:
const fs = require('fs');
fs.readdir(process.cwd(), function (error, files) {
if (!error) {
console.log(files);
}
});
返回值為:
[ 'a',
'index.js',
'package.json' ]
可以看到這里只返回了根目錄下的文件和目錄,,并沒有去深度遍歷。所以如果需要獲取所有文件名,,就需要自己實現(xiàn)遞歸。
同步方法
const fs = require('fs');
try {
const dirs = fs.readdirSync(process.cwd());
} catch(e) {}
fs.readFile(path[, options], callback)
文件讀取的 API,,通過 fs.readFile
可以獲取指定 path
的文件內(nèi)容,。
入?yún)⑷缦拢?/p>
- 第二個參數(shù): 配置對象,,包括
encoding
和 flag
,也可以直接傳如 encoding
字符串 - 第三個參數(shù): 回調(diào)函數(shù)
使用方法如下:
const fs = require('fs');
const path = require('path');
fs.readFile(path.join(process.cwd(), 'package.json'), 'utf-8', function (
error,
content
) {
if (!error) {
console.log(content);
}
});
如果沒傳 encoding
,,則其默認值為 null
,,此時返回的文件內(nèi)容為 Buffer
格式。
同步方法
const fs = require('fs');
try {
fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf-8');
} catch(e) {}
fs.writeFile(file, data[, options], callback)
對應(yīng)著讀文件 readFile
,,fs
也提供了寫文件的 API writeFile
,,接收四個參數(shù):
- 第二個參數(shù): 待寫入的文件內(nèi)容
- 第三個參數(shù): 配置對象,包括
encoding
和 flag
,,也可以直接傳如 encoding
字符串 - 第三個參數(shù): 回調(diào)函數(shù)
使用方法如下:
const fs = require('fs');
const path = require('path');
fs.writeFile(
path.join(process.cwd(), 'result.js'),
'console.log("Hello World")',
function (error, content) {
console.log(error);
}
);
同步方法
const fs = require('fs');
const path = require('path');
try {
fs.writeFileSync(
path.join(process.cwd(), 'result.js'),
'console.log("Hello World")',
'utf-8'
);
} catch (e) {}
本文主要是總結(jié)了一下在開發(fā) Node 時常用的一些 API,,后續(xù)的文章會帶來 Node 常用的一些三方包。
參考資料
[1]官方文檔: https:///dist/latest-v14.x/docs/api/
[2]DefinePlugin: https://webpack./plugins/define-plugin
[3]https:///dist/latest-v14.x/docs/api/fs.html: https:///dist/latest-v14.x/docs/api/fs.html