## 一、文件系統
  fs模塊可與文件系統進行交互,封裝了常規的POSIX函數。POSIX(Portable Operating System Interface,可移植操作系統接口)是UNIX系統的一個設計標準。fs模塊提供了多個操作目錄和文件的方法,大多會有同步和異步兩種版本,同步的方法名稱會以Sync為后綴。
**1)目錄處理**
  fs模塊的readdir()方法可異步的讀取目錄內容,回調函數包含兩個參數,第一個是錯誤對象,第二個是一個包含文件名稱的數組,對應的同步方法是readdirSync()。在下面的示例中,當前目錄包含兩個文件,上一級目錄包含一個目錄名稱。
~~~
const fs = require('fs');
fs.readdir('./', (err, files) => {
console.log(files); //[ 'demo.txt', 'index.js' ]
});
fs.readdir('../', (err, files) => {
console.log(files); //[ '1' ]
});
~~~
  其它處理目錄的方法還有opendir()、mkdir()等。
**2)讀寫文件**
  在fs模塊中,可使用批量方法readFile()將文件內容一次性的加載到內存中,如下所示。
~~~
const fs = require('fs');
fs.readFile('./origin.txt', (err, buf) => {
console.log(buf.toString()); //"hello Node.js"
});
~~~
  對應的寫入方法是writeFile(),如下所示,文件路徑、寫入內容和回調函數是必傳的參數。如果文件不存在,那么會自動創建。
~~~
fs.writeFile('./target.txt', 'hello Node.js', (err) => {
if (err) throw err;
console.log('文件已被保存');
});
~~~
  當文件很大時,像上面這樣直接讀取會有問題,可以改用流式方法createReadStream(),分批次的讀取文件,如下所示,每次只讀7個字節的內容。
~~~
const readable = fs.createReadStream('./origin.txt', {highWaterMark: 7});
readable.on("data", (chunk) => {
/*************/
/* "hello N"
/* "ode.js"
/************/
console.log(chunk.toString());
});
~~~
  通過管道方法pipe()將origin.txt中的內容寫入到target-stream.txt中,如下所示,對于不存在的文件,也會自動創建。
~~~
const writable = fs.createWriteStream('./target-stream.txt');
readable.pipe(writable);
~~~
**3)文件描述**
  fs模塊的stat()方法可讀取文件的描述信息,如下所示。
~~~
fs.stat('./demo.txt', (err, stats) => {
console.log(stats);
});
~~~
  回調函數中的stats參數是一個fs.Stats對象,其屬性如下所示。它還包含一些判斷方法,例如isDirectory()、isFile()等。
~~~
Stats {
dev: 195650,
mode: 33206,
nlink: 1,
uid: 0,
gid: 0,
rdev: 0,
blksize: undefined,
ino: 36873221949682120,
size: 13,
blocks: undefined,
atimeMs: 1586227933993.0217,
mtimeMs: 1585882949091.0166,
ctimeMs: 1586227933995.0222,
birthtimeMs: 1586227933993.0217,
atime: 2020-04-07T02:52:13.993Z,
mtime: 2020-04-03T03:02:29.091Z,
ctime: 2020-04-07T02:52:13.995Z,
birthtime: 2020-04-07T02:52:13.993Z
}
~~~
  fs模塊還提供了fstat()方法,在功能上與stat()等價,只是fstat()方法的第一個參數是文件描述符。在POSIX系統中,文件描述符是一個正整數,它實際上是一個索引值,指向內核為每一個進程所維護的該進程打開文件的記錄表。當打開或創建一個文件時,就會被分配一個文件描述符。
  在下面的示例中,open()方法的回調函數中包含一個fd參數(即文件描述符),搭配fstat()方法就能讀取文件信息。
~~~
fs.open('./demo.txt', 'r', (err, fd) => {
fs.fstat(fd, (err, stats) => {
console.log(stats);
});
});
~~~
**4)監控文件**
  fs模塊提供了兩種方法來監控文件:watch()和watchFile(),前者能監控文件或目錄的更改,后者只能監控文件的更改。
  watch()方法的監聽器回調包含兩個參數,第一個是事件類型(包括rename和change),第二個是觸發事件的文件名稱,如下所示。
~~~
fs.watch('./demo.txt', (eventType, filename) => {
console.log(eventType, filename);
});
~~~
  執行node命令后,每次更改demo.txt文件,就會在控制臺打印出下面這條語句。
~~~
$ node index.js
change demo.txt
~~~
  雖然watch()方法的性能優于watchFile()方法,但是watch()不是一個跨平臺的方法,其表現在各個平臺中并非百分百一致(例如filename參數不能保證提供),而watchFile()是跨平臺的。
**5)異步文件**
  fs.promises是Node提供的一組備用的異步文件系統方法,它們會返回Promise對象而不是通過回調來處理結果。例如以Promise的方式使用stat()方法,如下所示。
~~~
const fsPromises = require('fs').promises;
fsPromises.stat('./demo.txt').then(stats => {
console.log(stats);
});
~~~
## 二、網絡
  Node可以處理的網絡協議包括HTTP、TCP、HTTPS等,本節將會重點分析HTTP協議。Node中的http模塊基于net、stream、events等模塊,提供了一系列的網絡傳輸的底層API,可創建HTTP服務器和客戶端,包含重定向、代理、上傳數據等功能。
**1)HTTP服務器**
  通過http.createServer()方法就能創建HTTP服務器,其內部會調用net.Server()方法,返回一個http.Server實例。當服務被創建后,就能通過server.listen()方法監聽一個端口。
  在下面的示例中,監聽的端口是8000,createServer()的回調函數包含兩個參數:req和res,分別表示HTTP的請求和響應,前者是IncomingMessage類(一個可讀流)的實例,后者是ServerResponse類(一個可寫流)的實例。\_\_dirname是一個全局變量,保存著當前文件所處的路徑。
~~~
const http = require('http');
const fs = require('fs');
const server = http.createServer(function (req, res) {
fs.readFile(__dirname + "/index.html", function (err, data) {
if (err) {
res.statusCode = 500;
res.end(String(err));
} else {
res.end(data);
}
});
}).listen(8000);
~~~
  在回調函數中,可修改或解析響應報文,例如像上面當發生錯誤時,將狀態碼改成500。在瀏覽器中訪問http://localhost:8000,就能通過end()方法在頁面中輸出index.html的文件內容。
  注意,必須調用響應對象的end()方法結束此次響應,如果省略,那么頁面將會一直處于加載中,阻塞內容渲染。
  http.Server包含一組事件,例如connection和request,如下所示。前者會在建立新的TCP流時觸發,后者會在每次請求服務器時觸發。
~~~
server.on("connection", function (socket) {
console.log(connection");
});
server.on("request", function (req, res) {
console.log('request');
});
~~~
**2)重定向**
  3XX格式的狀態碼用于重定向,例如在Node中實現302跳轉,如下所示。
~~~
http.createServer(function (req, res) {
if(req.url == '/strick') {
res.writeHead(302, {'Location': 'http://www.pwstrick.com'});
}
res.end();
}).listen(8000);
~~~
  當瀏覽器訪問http://localhost:8000/strick時,才會執行重定向。writeHead()方法可一次性設置響應的狀態碼和所有的首部。如果只想設置單個響應首部,可以使用setHeader()方法。
**3)上傳數據**
  在實際的業務開發中,免不了上傳數據的需求,例如表單提交、圖像上傳等。下面是一張form表單,包含一個文本框和一個提交按鈕。
~~~html
<form action="http://localhost:8000" method="post">
<input type="text" name="name" />
<button type="submit">提交</button>
</form>
~~~
  點擊提交按鈕,會將整張表單提交到HTTP服務器(代碼如下所示),假設文本框中輸入的內容為“咖啡機strick”。
~~~
const http = require('http');
const querystring = require('querystring');
const server = http.createServer(function (req, res) {
//聲明Content-Type響應首部,以免中文亂碼
res.setHeader("Content-Type", "application/json; charset=utf-8");
const arr = [];
req.on("data", chunk => {
arr.push(chunk);
});
req.on("end", () => {
const buf = Buffer.concat(arr); //拼接Buffer數據
const params = querystring.decode(buf.toString());
console.log(params.name); //"咖啡機strick"
res.write(JSON.stringify(params)); //響應數據
res.end();
});
}).listen(8000);
~~~
  在data事件中接收數據并添加到arr數組中,然后在end事件中由Buffer.concat()方法拼接Buffer數據。querystring模塊的decode()方法可將查詢字符串解析成一個對象。write()方法會將響應數據發送給客戶端。
**4)客戶端服務**
  http模塊提供了request()方法,可讓客戶端向服務器發起請求。在下面的示例中,postData變量是要發送的數據,options變量是各種配置參數,包括請求首部的信息。
~~~
const http = require('http');
const querystring = require('querystring');
const postData = querystring.stringify({
'name': '咖啡機strick'
});
const options = {
hostname: 'localhost',
port: 8000,
path: '/',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = http.request(options, (res) => {
const code = res.statusCode; //狀態碼
const headers = JSON.stringify(res.headers); //響應首部
res.setEncoding('utf8'); //為可讀流設置字符編碼
res.on('data', (chunk) => {
console.log(chunk); //響應數據
});
res.on('end', () => {
console.log('響應中已無數據');
});
});
req.write(postData);
req.end();
~~~
  注意,在請求最后需要調用end()方法,結束本次請求。Node還提供了一個基于request()的便捷方法:get(),它會自動調用req.end()方法,并且請求方法是GET。
**5)代理**
  代理是一種特殊的網絡服務,允許一個客戶端通過這個服務與目標服務器進行間接的連接,也就是在它們之間建立了一層中轉,可用來過濾廣告、控制內部資源的訪問權限、保障終端的隱私或安全等。
  http模塊可搭建出一個簡易的HTTP代理服務器,下面示例引用自《[Node.js硬實戰](https://book.douban.com/subject/26937390/)》技巧52一節。
~~~
const http = require('http');
const url = require('url');
http.createServer(function (req, res) {
const options = url.parse(req.url); //一個URL對象
options.headers = req.headers;
req.on('data', chunk => { //將請求的原始數據發送給代理
proxyRequest.write(chunk);
});
req.on('end', chunk => { //完成原始請求
proxyRequest.end();
});
//復制原始請求
const proxyRequest = http.request(options, proxyResponse => {
proxyResponse.on('data', chunk => { //將響應的原始數據發送給客戶端
res.write(chunk);
});
proxyResponse.on('end', chunk => { //完成代理請求
res.end();
});
//將響應報文發送給客戶端
res.writeHead(proxyResponse.statusCode, proxyResponse.headers);
});
}).listen(8000);
~~~
  url模塊的parse()方法能將字符串轉換成一個URL對象。在綁定請求對象的data事件后,就能將客戶端的請求數據發送給代理。通過http.request()方法發起一次代理請求,并將服務器的響應數據發送給客戶端。
*****
> 原文出處:
[博客園-Node.js躬行記](https://www.cnblogs.com/strick/category/1688575.html)
[知乎專欄-Node.js躬行記](https://zhuanlan.zhihu.com/pwnode)
已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎瀏覽。

推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、擴展運算符和剩余參數
- 3、解構
- 4、模板字面量
- 5、對象字面量的擴展
- 6、Symbol
- 7、代碼模塊化
- 8、數字
- 9、字符串
- 10、正則表達式
- 11、對象
- 12、數組
- 13、類型化數組
- 14、函數
- 15、箭頭函數和尾調用優化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、類
- 21、類的繼承
- 22、Promise
- 23、Promise的靜態方法和應用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基礎實踐
- 3、WebRTC視頻通話
- 4、Web音視頻基礎
- CSS進階
- 1、CSS基礎拾遺
- 2、偽類和偽元素
- 3、CSS屬性拾遺
- 4、浮動形狀
- 5、漸變
- 6、濾鏡
- 7、合成
- 8、裁剪和遮罩
- 9、網格布局
- 10、CSS方法論
- 11、管理后臺響應式改造
- React
- 1、函數式編程
- 2、JSX
- 3、組件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表單
- 8、樣式
- 9、組件通信
- 10、高階組件
- 11、Redux基礎
- 12、Redux中間件
- 13、React Router
- 14、測試框架
- 15、React Hooks
- 16、React源碼分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基礎
- 4、webpack進階
- 5、Git
- 6、Fiddler
- 7、自制腳手架
- 8、VSCode插件研發
- 9、WebView中的頁面調試方法
- Vue.js
- 1、數據綁定
- 2、指令
- 3、樣式和表單
- 4、組件
- 5、組件通信
- 6、內容分發
- 7、渲染函數和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、數據類型
- 2、接口
- 3、類
- 4、泛型
- 5、類型兼容性
- 6、高級類型
- 7、命名空間
- 8、裝飾器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系統和網絡
- 3、命令行工具
- 4、自建前端監控系統
- 5、定時任務的調試
- 6、自制短鏈系統
- 7、定時任務的進化史
- 8、通用接口
- 9、微前端實踐
- 10、接口日志查詢
- 11、E2E測試
- 12、BFF
- 13、MySQL歸檔
- 14、壓力測試
- 15、活動規則引擎
- 16、活動配置化
- 17、UmiJS版本升級
- 18、半吊子的可視化搭建系統
- 19、KOA源碼分析(上)
- 20、KOA源碼分析(下)
- 21、花10分鐘入門Node.js
- 22、Node環境升級日志
- 23、Worker threads
- 24、低代碼
- 25、Web自動化測試
- 26、接口攔截和頁面回放實驗
- 27、接口管理
- 28、Cypress自動化測試實踐
- 29、基于Electron的開播助手
- Node.js精進
- 1、模塊化
- 2、異步編程
- 3、流
- 4、事件觸發器
- 5、HTTP
- 6、文件
- 7、日志
- 8、錯誤處理
- 9、性能監控(上)
- 10、性能監控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 監控系統
- 1、SDK
- 2、存儲和分析
- 3、性能監控
- 4、內存泄漏
- 5、小程序
- 6、較長的白屏時間
- 7、頁面奔潰
- 8、shin-monitor源碼分析
- 前端性能精進
- 1、優化方法論之測量
- 2、優化方法論之分析
- 3、瀏覽器之圖像
- 4、瀏覽器之呈現
- 5、瀏覽器之JavaScript
- 6、網絡
- 7、構建
- 前端體驗優化
- 1、概述
- 2、基建
- 3、后端
- 4、數據
- 5、后臺
- Web優化
- 1、CSS優化
- 2、JavaScript優化
- 3、圖像和網絡
- 4、用戶體驗和工具
- 5、網站優化
- 6、優化閉環實踐
- 數據結構與算法
- 1、鏈表
- 2、棧、隊列、散列表和位運算
- 3、二叉樹
- 4、二分查找
- 5、回溯算法
- 6、貪心算法
- 7、分治算法
- 8、動態規劃
- 程序員之路
- 大學
- 2011年
- 2012年
- 2013年
- 2014年
- 項目反思
- 前端基礎學習分享
- 2015年
- 再一次項目反思
- 然并卵
- PC網站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端學習之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 2024年
- 日志
- 2020