:基礎(chǔ)內(nèi)置模塊)
Node系列 · Node基礎(chǔ)基礎(chǔ)內(nèi)置模塊Node 內(nèi)置的核心模塊里os/path/url/util是幾乎每個(gè)項(xiàng)目都會(huì)用的四件套。它們不依賴任何第三方包跨平臺(tái)行為一致理解它們的真實(shí)行為能避免一類經(jīng)典的在我電腦能跑問(wèn)題。一、os與操作系統(tǒng)交互os模塊提供操作系統(tǒng)層面的查詢能力——CPU、內(nèi)存、用戶目錄、網(wǎng)絡(luò)接口等。常用 APIAPI返回類型用途os.EOLstring當(dāng)前系統(tǒng)的行結(jié)束符\n/\r\nos.arch()stringCPU 架構(gòu)arm64/x64os.cpus()array每顆邏輯 CPU 核心信息os.freemem()number空閑內(nèi)存字節(jié)數(shù)os.totalmem()number總內(nèi)存字節(jié)數(shù)os.homedir()string當(dāng)前用戶目錄os.hostname()string主機(jī)名os.tmpdir()string系統(tǒng)臨時(shí)目錄os.platform()string平臺(tái)標(biāo)識(shí)darwin/linux/win32os.uptime()number系統(tǒng)啟動(dòng)到現(xiàn)在的秒數(shù)os.networkInterfaces()object網(wǎng)絡(luò)接口列表典型應(yīng)用// 根據(jù) CPU 核心數(shù)決定要不要起 worker const cpuCount os.cpus().length; const workers Math.max(1, cpuCount - 1); // 把日志寫(xiě)到系統(tǒng)臨時(shí)目錄 const logDir path.join(os.tmpdir(), my-app-logs); fs.mkdirSync(logDir, { recursive: true });::: warning不要拿os.cpus().length當(dāng)真實(shí)物理核心數(shù)。它返回的是邏輯核心數(shù)含超線程。Node 單進(jìn)程只能用一個(gè) CPU 核心物理多核需要 Worker Threads 或多進(jìn)程。:::二、path跨平臺(tái)路徑處理path模塊的核心價(jià)值是屏蔽 Windows / POSIX 路徑差異。Node 代碼里永遠(yuǎn)不要直接拼字符串路徑全部走pathAPI。2.1 核心 APIAPI作用示例path.basename(p)取文件名path.basename(/a/b/c.txt)→c.txtpath.dirname(p)取目錄path.dirname(/a/b/c.txt)→/a/bpath.extname(p)取后綴path.extname(/a/b/c.txt)→.txtpath.join(...p)拼接多段路徑path.join(/a, b, c.txt)→/a/b/c.txtpath.resolve(...p)解析為絕對(duì)路徑path.resolve(c.txt)→cwd/c.txtpath.relative(from, to)計(jì)算相對(duì)路徑path.relative(/a/b, /a/c/d)→../c/dpath.isAbsolute(p)是否絕對(duì)路徑path.isAbsolute(/a)→truepath.normalize(p)規(guī)范化路徑path.normalize(/a//b/./c)→/a/b/cpath.sep當(dāng)前系統(tǒng)的路徑分隔符Linux\\(實(shí)際 /) / Windows\path.delimiter環(huán)境變量分隔符Linux:/ Windows;2.2joinvsresolve兩個(gè)看著像行為差異很大// join純拼接結(jié)果是不是絕對(duì)路徑看輸入 path.join(/a, b, c.txt); // /a/b/c.txt path.join(a, b, c.txt); // a/b/c.txt // resolve從右往左拼遇到絕對(duì)路徑就重置起點(diǎn) path.resolve(a, b, c.txt); // cwd/a/b/c.txt path.resolve(/a, b, /c.txt); // /c.txt遇到 /c.txt 重置2.3 跨平臺(tái)注意事項(xiàng)// ? 錯(cuò)誤直接拼字符串 const filePath __dirname /config/ filename; // ? 正確用 path.join const filePath path.join(__dirname, config, filename); // ? 錯(cuò)誤硬編碼分隔符 const tmpPath /tmp/ name; // ? 正確用 os.tmpdir() path.join const tmpPath path.join(os.tmpdir(), name);三、urlURL 解析與構(gòu)造url模塊提供 WHATWG URL 標(biāo)準(zhǔn)實(shí)現(xiàn)Node 10。3.1 核心 APIAPI用途new URL(input, base?)構(gòu)造一個(gè) URL 對(duì)象URLSearchParamsURL 查詢字符串解析url.fileURLToPath(url)file://URL → 路徑url.pathToFileURL(path)路徑 →file://URL3.2 URL 對(duì)象const u new URL(https://user:passexample.com:8080/path/to?x1y2#hash); u.protocol; // https: u.host; // example.com:8080 u.hostname; // example.com u.port; // 8080 u.pathname; // /path/to u.search; // ?x1y2 u.hash; // #hash u.username; // user u.password; // pass u.origin; // https://example.com:80803.3 查詢參數(shù)const u new URL(https://example.com/api?x1y2); // 讀取 u.searchParams.get(x); // 1 u.searchParams.getAll(x); // [1] u.searchParams.has(z); // false // 增刪改 u.searchParams.append(z, 3); u.searchParams.set(x, 10); u.searchParams.delete(y); // 序列化 u.toString(); // https://example.com/api?x10z3 // 單獨(dú)構(gòu)造 const params new URLSearchParams({ foo: 1, bar: 2 }); params.toString(); // foo1bar23.4 路徑與 URL 互轉(zhuǎn)import { fileURLToPath, pathToFileURL } from node:url; // path → file:// URL pathToFileURL(/usr/local/bin); // file:///usr/local/bin // file:// URL → path fileURLToPath(file:///usr/local/bin); // /usr/local/binESM 模塊下import.meta.url是file://URL要拿路徑必須fileURLToPath轉(zhuǎn)一次CJS 下直接是__dirname。四、util工具函數(shù)集合util模塊聚集了各種雜項(xiàng)但有用的工具。4.1 類型判斷util.isArray([]); // true util.isDate(new Date()); // true util.isRegExp(/x/); // true util.types.isPromise(Promise.resolve()); // true util.types.isMap(new Map()); // true util.types.isSet(new Set()); // true util.types.isArrayBuffer(new ArrayBuffer(8)); // true::: tipNode 10 之后Array.isArray/instanceof已經(jīng)夠用util.isArray等被視為遺留 API。新代碼建議用util.types或原生Array.isArray。:::4.2 回調(diào)與 Promise 互轉(zhuǎn)// 舊的 CJS 回調(diào)風(fēng)格 API(err, value) {...} // 想用 async/awaitutil.promisify 把它包成返回 Promise 的函數(shù) const fs require(fs); const readFile util.promisify(fs.readFile); const data await readFile(./config.json, utf-8); // 反向Promise 風(fēng)格 API 想給老代碼用util.callbackify const asyncAdd async (a, b) a b; const cbAdd util.callbackify(asyncAdd); cbAdd(1, 2, (err, sum) { console.log(sum); // 3 });4.3 繼承inherits// ES6 class 時(shí)代幾乎不用——直接用 extends 即可 // 留給老代碼理解 function Animal() {} Animal.prototype.greet function () { return hello; }; function Dog() {} util.inherits(Dog, Animal); Dog.prototype.bark function () { return woof; }; const d new Dog(); d.greet(); // hello d.bark(); // woof4.4 深度嚴(yán)格比較util.isDeepStrictEqual( { a: 1, b: [1, 2] }, { a: 1, b: [1, 2] } ); // true // 與 的關(guān)鍵區(qū)別遞歸比較對(duì)象、數(shù)組、Map、Set 1 1; // true { a: 1 } { a: 1 }; // false util.isDeepStrictEqual( { a: 1 }, { a: 1 } ); // true4.5 調(diào)試輸出const obj { a: 1, b: { c: [1, 2, 3] } }; console.log(util.inspect(obj, { depth: 4, colors: true }));util.inspect是console.log內(nèi)部實(shí)現(xiàn)可以指定深度、顏色、隱藏字段等。調(diào)試復(fù)雜對(duì)象時(shí)比直接JSON.stringify信息更全保留函數(shù)、undefined、循環(huán)引用。五、其他常用內(nèi)置模塊速覽模塊用途備注querystringURL 查詢字符串URLSearchParams已覆蓋大部分場(chǎng)景assert斷言單元測(cè)試Node 自帶測(cè)試時(shí)用jest 流行后少用eventsEventEmitter見(jiàn)第 12 章stream流處理見(jiàn)第 7 章crypto加密 / hash見(jiàn)后續(xù)章節(jié)zlib壓縮 / 解壓gzip / deflatechild_process子進(jìn)程spawn / exec / fork六、最佳實(shí)踐場(chǎng)景推薦做法反例拼接文件路徑path.join/path.resolve字符串拼讀用戶目錄os.homedir()假設(shè)是/home/x寫(xiě)臨時(shí)文件os.tmpdir()path.join假設(shè)是/tmp解析 URLnew URL(...)searchParams手寫(xiě) split老 API 轉(zhuǎn) Promiseutil.promisify自己手寫(xiě) wrapper判斷內(nèi)置類型util.typesinstanceof跨 realm 不可靠多行字符串拼接path.join或os.EOL硬編碼\n/\r\n七、小結(jié)os提供系統(tǒng)信息查詢os.cpus().length是邏輯核心數(shù)含超線程path是跨平臺(tái)路徑處理的唯一正確選擇join拼接、resolve解析為絕對(duì)路徑url用 WHATWG 標(biāo)準(zhǔn)new URLsearchParams處理查詢參數(shù)util.promisify把回調(diào) API 包成 Promiseutil.callbackify反向這些模塊零依賴——新項(xiàng)目能少裝一個(gè)包就少裝一個(gè)