[TOC]
```
//公共引用
var fs = require('fs'),
path = require('path');
```
<br>
# 讀取文件
```
//readFile(filename,[options],callback);
/**
* filename, 必選參數,文件名
* [options],可選參數,可指定flag(文件操作選項,如r+ 讀寫;w+ 讀寫,文件不存在則創建)及encoding屬性
* callback 讀取文件后的回調函數,參數默認第一個err,第二個data 數據
*/
fs.readFile(__dirname + '/test.txt', {flag: 'r+', encoding: 'utf8'}, function (err, data) {
if(err) {
console.error(err);
return;
}
console.log(data);
});
```
<br>
# 寫文件
```
// fs.writeFile(filename,data,[options],callback);
var w_data = '這是一段通過fs.writeFile函數寫入的內容;\r\n';
var w_data = new Buffer(w_data);
/**
* filename, 必選參數,文件名
* data, 寫入的數據,可以字符或一個Buffer對象
* [options],flag,mode(權限),encoding
* callback 讀取文件后的回調函數,參數默認第一個err,第二個data 數據
*/
fs.writeFile(__dirname + '/test.txt', w_data, {flag: 'a'}, function (err) {
if(err) {
console.error(err);
} else {
console.log('寫入成功');
}
});
```
<br>
# 以追加方式寫文件
```
// fs.appendFile(filename,data,[options],callback);
fs.appendFile(__dirname + '/test.txt', '使用fs.appendFile追加文件內容', function () {
console.log('追加內容完成');
});
```
# 打開文件
```
// fs.open(filename, flags, [mode], callback);
/**
* filename, 必選參數,文件名
* flags, 操作標識,如"r",讀方式打開
* [mode],權限,如777,表示任何用戶讀寫可執行
* callback 打開文件后回調函數,參數默認第一個err,第二個fd為一個整數,表示打開文件返回的文件描述符,window中又稱文件句柄
*/
fs.open(__dirname + '/test.txt', 'r', '0666', function (err, fd) {
console.log(fd);
});
```
<br>
# 讀文件,讀取打開的文件內容到緩沖區中
```
//fs.read(fd, buffer, offset, length, position, callback);
/**
* fd, 使用fs.open打開成功后返回的文件描述符
* buffer, 一個Buffer對象,v8引擎分配的一段內存
* offset, 整數,向緩存區中寫入時的初始位置,以字節為單位
* length, 整數,讀取文件的長度
* position, 整數,讀取文件初始位置;文件大小以字節為單位
* callback(err, bytesRead, buffer), 讀取執行完成后回調函數,bytesRead實際讀取字節數,被讀取的緩存區對象
*/
fs.open(__dirname + '/test.txt', 'r', function (err, fd) {
if(err) {
console.error(err);
return;
} else {
var buffer = new Buffer(255);
console.log(buffer.length);
//每一個漢字utf8編碼是3個字節,英文是1個字節
fs.read(fd, buffer, 0, 9, 3, function (err, bytesRead, buffer) {
if(err) {
throw err;
} else {
console.log(bytesRead);
console.log(buffer.slice(0, bytesRead).toString());
//讀取完后,再使用fd讀取時,基點是基于上次讀取位置計算;
fs.read(fd, buffer, 0, 9, null, function (err, bytesRead, buffer) {
console.log(bytesRead);
console.log(buffer.slice(0, bytesRead).toString());
});
}
});
}
});
```
<br>
# 寫文件,將緩沖區內數據寫入使用fs.open打開的文件
```
//fs.write(fd, buffer, offset, length, position, callback);
/**
* fd, 使用fs.open打開成功后返回的文件描述符
* buffer, 一個Buffer對象,v8引擎分配的一段內存
* offset, 整數,從緩存區中讀取時的初始位置,以字節為單位
* length, 整數,從緩存區中讀取數據的字節數
* position, 整數,寫入文件初始位置;
* callback(err, written, buffer), 寫入操作執行完成后回調函數,written實際寫入字節數,buffer被讀取的緩存區對象
*/
fs.open(__dirname + '/test.txt', 'a', function (err, fd) {
if(err) {
console.error(err);
return;
} else {
var buffer = new Buffer('寫入文件數據內容');
//寫入'入文件'三個字
fs.write(fd, buffer, 3, 9, 12, function (err, written, buffer) {
if(err) {
console.log('寫入文件失敗');
console.error(err);
return;
} else {
console.log(buffer.toString());
//寫入'數據內'三個字
fs.write(fd, buffer, 12, 9, null, function (err, written, buffer) {
console.log(buffer.toString());
})
}
});
}
});
```
<br>
# 刷新緩存區
```
// 使用fs.write寫入文件時,操作系統是將數據讀到內存,再把數據寫入到文件中,
// 當數據讀完時并不代表數據已經寫完,因為有一部分還可能在內在緩沖區內。
// 因此可以使用fs.fsync方法將內存中數據寫入文件;--刷新內存緩沖區;
//fs.fsync(fd, [callback])
/**
* fd, 使用fs.open打開成功后返回的文件描述符
* [callback(err, written, buffer)], 寫入操作執行完成后回調函數,written實際寫入字節數,buffer被讀取的緩存區對象
*/
fs.open(__dirname + '/test.txt', 'a', function (err, fd) {
if(err)
throw err;
var buffer = new Buffer('我愛nodejs編程');
fs.write(fd, buffer, 0, 9, 0, function (err, written, buffer) {
console.log(written.toString());
fs.write(fd, buffer, 9, buffer.length - 9, null, function (err, written) {
console.log(written.toString());
fs.fsync(fd);
fs.close(fd);
})
});
});
```
<br>
# 創建目錄
```
//使用fs.mkdir創建目錄
//fs.mkdir(path, [mode], callback);
/**
* path, 被創建目錄的完整路徑及目錄名;
* [mode], 目錄權限,默認0777
* [callback(err)], 創建完目錄回調函數,err錯誤對象
*/
fs.mkdir(__dirname + '/fsDir', function (err) {
if(err)
throw err;
console.log('創建目錄成功')
});
```
<br>
# 讀取目錄
```
//使用fs.readdir讀取目錄,重點其回調函數中files對象
//fs.readdir(path, callback);
/**
* path, 要讀取目錄的完整路徑及目錄名;
* [callback(err, files)], 讀完目錄回調函數;err錯誤對象,files數組,存放讀取到的目錄中的所有文件名
*/
fs.readdir(__dirname + '/fsDir/', function (err, files) {
if(err) {
console.error(err);
return;
} else {
files.forEach(function (file) {
var filePath = path.normalize(__dirname + '/fsDir/' + file);
fs.stat(filePath, function (err, stat) {
if(stat.isFile()) {
console.log(filePath + ' is: ' + 'file');
}
if(stat.isDirectory()) {
console.log(filePath + ' is: ' + 'dir');
}
});
});
for (var i = 0; i < files.length; i++) {
//使用閉包無法保證讀取文件的順序與數組中保存的致
(function () {
var filePath = path.normalize(__dirname + '/fsDir/' + files[i]);
fs.stat(filePath, function (err, stat) {
if(stat.isFile()) {
console.log(filePath + ' is: ' + 'file');
}
if(stat.isDirectory()) {
console.log(filePath + ' is: ' + 'dir');
}
});
})();
}
}
});
```
<br>
# 查看文件與目錄的信息
```
//fs.stat(path, callback);
//fs.lstat(path, callback); //查看符號鏈接文件
/**
* path, 要查看目錄/文件的完整路徑及名;
* [callback(err, stats)], 操作完成回調函數;err錯誤對象,stat fs.Stat一個對象實例
* 提供如:isFile, isDirectory,isBlockDevice等方法及size,ctime,mtime等屬性
*/
//實例,查看fs.readdir
```
<br>
# 查看文件與目錄的是否存在
```
//fs.exists(path, callback);
/**
* path, 要查看目錄/文件的完整路徑及名;
* [callback(exists)], 操作完成回調函數;exists true存在,false表示不存在
*/
fs.exists(__dirname + '/te', function (exists) {
var retTxt = exists ? retTxt = '文件存在' : '文件不存在';
console.log(retTxt);
});
```
# 修改文件訪問時間與修改時間
```
//fs.utimes(path, atime, mtime, callback);
/**
* path, 要查看目錄/文件的完整路徑及名;
* atime, 新的訪問時間
* ctime, 新的修改時間
* [callback(err)], 操作完成回調函數;err操作失敗對象
*/
fs.utimes(__dirname + '/test.txt', new Date(), new Date(), function (err) {
if(err) {
console.error(err);
return;
}
fs.stat(__dirname + '/test.txt', function (err, stat) {
console.log('訪問時間: ' + stat.atime.toString() + '; \n修改時間:' + stat.mtime);
console.log(stat.mode);
})
});
```
<br>
# 修改文件或目錄的操作權限
```
//fs.utimes(path, mode, callback);
/**
* path, 要查看目錄/文件的完整路徑及名;
* mode, 指定權限,如:0666 8進制,權限:所有用戶可讀、寫,
* [callback(err)], 操作完成回調函數;err操作失敗對象
*/
fs.chmod(__dirname + '/fsDir', 0666, function (err) {
if(err) {
console.error(err);
return;
}
console.log('修改權限成功')
});
```
<br>
# 移動/重命名文件或目錄
```
//fs.rename(oldPath, newPath, callback);
/**
* oldPath, 原目錄/文件的完整路徑及名;
* newPath, 新目錄/文件的完整路徑及名;如果新路徑與原路徑相同,而只文件名不同,則是重命名
* [callback(err)], 操作完成回調函數;err操作失敗對象
*/
fs.rename(__dirname + '/test', __dirname + '/fsDir', function (err) {
if(err) {
console.error(err);
return;
}
console.log('重命名成功')
});
```
<br>
# 刪除空目錄
```
//fs.rmdir(path, callback);
/**
* path, 目錄的完整路徑及目錄名;
* [callback(err)], 操作完成回調函數;err操作失敗對象
*/
fs.rmdir(__dirname + '/test', function (err) {
fs.mkdir(__dirname + '/test', 0666, function (err) {
console.log('創建test目錄');
});
if(err) {
console.log('刪除空目錄失敗,可能原因:1、目錄不存在,2、目錄不為空')
console.error(err);
return;
}
console.log('刪除空目錄成功!');
});
```
<br>
# 監視文件
```
//對文件進行監視,并且在監視到文件被修改時執行處理
//fs.watchFile(filename, [options], listener);
/**
* filename, 完整路徑及文件名;
* [options], persistent true表示持續監視,不退出程序;interval 單位毫秒,表示每隔多少毫秒監視一次文件
* listener, 文件發生變化時回調,有兩個參數:curr為一個fs.Stat對象,被修改后文件,prev,一個fs.Stat對象,表示修改前對象
*/
fs.watchFile(__dirname + '/test.txt', {interval: 20}, function (curr, prev) {
if(Date.parse(prev.ctime) == 0) {
console.log('文件被創建!');
} else if(Date.parse(curr.ctime) == 0) {
console.log('文件被刪除!')
} else if(Date.parse(curr.mtime) != Date.parse(prev.mtime)) {
console.log('文件有修改');
}
});
fs.watchFile(__dirname + '/test.txt', function (curr, prev) {
console.log('這是第二個watch,監視到文件有修改');
});
```
<br>
# 取消監視文件
```
//取消對文件進行監視
//fs.unwatchFile(filename, [listener]);
/**
* filename, 完整路徑及文件名;
* [listener], 要取消的監聽器事件,如果不指定,則取消所有監聽處理事件
*/
var listener = function (curr, prev) {
console.log('我是監視函數')
}
fs.unwatchFile(__dirname + '/test.txt', listener);
```
# 監視文件或目錄
```
// 對文件或目錄進行監視,并且在監視到修改時執行處理;
// fs.watch返回一個fs.FSWatcher對象,擁有一個close方法,用于停止watch操作;
// 當fs.watch有文件變化時,會觸發fs.FSWatcher對象的change(err, filename)事件,err錯誤對象,filename發生變化的文件名
// fs.watch(filename, [options], [listener]);
/**
* filename, 完整路徑及文件名或目錄名;
* [listener(event, filename], 監聽器事件,有兩個參數:event 為rename表示指定的文件或目錄中有重命名、
* 刪除或移動操作或change表示有修改,filename表示發生變化的文件路徑
*/
var fsWatcher = fs.watch(__dirname + '/test', function (event, filename) {
//console.log(event)
});
//console.log(fsWatcher instanceof FSWatcher);
fsWatcher.on('change', function (event, filename) {
console.log(filename + ' 發生變化')
});
//30秒后關閉監視
setTimeout(function () {
console.log('關閉')
fsWatcher.close(function (err) {
if(err) {
console.error(err)
}
console.log('關閉watch')
});
}, 30000);
```
<br>
# 文件流
```
/*
* 流,在應用程序中表示一組有序的、有起點有終點的字節數據的傳輸手段;
* Node.js中實現了stream.Readable/stream.Writeable接口的對象進行流數據讀寫;以上接口都繼承自EventEmitter類,因此在讀/寫流不同狀態時,觸發不同事件;
* 關于流讀取:Node.js不斷將文件一小塊內容讀入緩沖區,再從緩沖區中讀取內容;
* 關于流寫入:Node.js不斷將流數據寫入內在緩沖區,待緩沖區滿后再將緩沖區寫入到文件中;重復上面操作直到要寫入內容寫寫完;
* readFile、read、writeFile、write都是將整個文件放入內存而再操作,而則是文件一部分數據一部分數據操作;
*
* -----------------------流讀取-------------------------------------
* 讀取數據對象:
* fs.ReadStream 讀取文件
* http.IncomingMessage 客戶端請求或服務器端響應
* net.Socket Socket端口對象
* child.stdout 子進程標準輸出
* child.stdin 子進程標準入
* process.stdin 用于創建進程標準輸入流
* Gzip、Deflate、DeflateRaw 數據壓縮
*
* 觸發事件:
* readable 數據可讀時
* data 數據讀取后
* end 數據讀取完成時
* error 數據讀取錯誤時
* close 關閉流對象時
*
* 讀取數據的對象操作方法:
* read 讀取數據方法
* setEncoding 設置讀取數據的編
* pause 通知對象眾目停止觸發data事件
* resume 通知對象恢復觸發data事件
* pipe 設置數據通道,將讀入流數據接入寫入流;
* unpipe 取消通道
* unshift 當流數據綁定一個解析器時,此方法取消解析器
*
* ------------------------流寫入-------------------------------------
* 寫數據對象:
* fs.WriteStream 寫入文件對象
* http.clientRequest 寫入HTTP客戶端請求數據
* http.ServerResponse 寫入HTTP服務器端響應數據
* net.Socket 讀寫TCP流或UNIX流,需要connection事件傳遞給用戶
* child.stdout 子進程標準輸出
* child.stdin 子進程標準入
* Gzip、Deflate、DeflateRaw 數據壓縮
*
* 寫入數據觸發事件:
* drain 當write方法返回false時,表示緩存區中已經輸出到目標對象中,可以繼續寫入數據到緩存區
* finish 當end方法調用,全部數據寫入完成
* pipe 當用于讀取數據的對象的pipe方法被調用時
* unpipe 當unpipe方法被調用
* error 當發生錯誤
*
* 寫入數據方法:
* write 用于寫入數據
* end 結束寫入,之后再寫入會報錯;
*/
```
<br>
# 創建讀取流
```
//fs.createReadStream(path, [options])
/**
* path 文件路徑
* [options] flags:指定文件操作,默認'r',讀操作;encoding,指定讀取流編碼;
* autoClose, 是否讀取完成后自動關閉,默認true;start指定文件開始讀取位置;end指定文件開始讀結束位置
*/
var rs = fs.createReadStream(__dirname + '/test.txt', {start: 0, end: 2});
//open是ReadStream對象中表示文件打開時事件,
rs.on('open', function (fd) {
console.log('開始讀取文件');
});
rs.on('data', function (data) {
console.log(data.toString());
});
rs.on('end', function () {
console.log('讀取文件結束')
});
rs.on('close', function () {
console.log('文件關閉');
});
rs.on('error', function (err) {
console.error(err);
});
//暫停和回復文件讀取;
rs.on('open', function () {
console.log('開始讀取文件');
});
rs.pause();
rs.on('data', function (data) {
console.log(data.toString());
});
setTimeout(function () {
rs.resume();
}, 2000);
```
<br>
# 創建寫入流
```
//fs.createWriteStream(path, [options])
/**
* path 文件路徑
* [options] flags:指定文件操作,默認'w',;encoding,指定讀取流編碼;start指定寫入文件的位置
*/
/* ws.write(chunk, [encoding], [callback]);
* chunk, 可以為Buffer對象或一個字符串,要寫入的數據
* [encoding], 編碼
* [callback], 寫入后回調
*/
/* ws.end([chunk], [encoding], [callback]);
* [chunk], 要寫入的數據
* [encoding], 編碼
* [callback], 寫入后回調
*/
var ws = fs.createWriteStream(__dirname + '/test.txt', {start: 0});
var buffer = new Buffer('我也喜歡你');
ws.write(buffer, 'utf8', function (err, buffer) {
console.log(arguments);
console.log('寫入完成,回調函數沒有參數')
});
//最后再寫入的內容
ws.end('再見');
//使用流完成復制文件操作
var rs = fs.createReadStream(__dirname + '/test.txt')
var ws = fs.createWriteStream(__dirname + '/test/test.txt');
rs.on('data', function (data) {
ws.write(data)
});
ws.on('open', function (fd) {
console.log('要寫入的數據文件已經打開,文件描述符是: ' + fd);
});
rs.on('end', function () {
console.log('文件讀取完成');
ws.end('完成', function () {
console.log('文件全部寫入完成')
});
});
//關于WriteStream對象的write方法返回一個布爾類型,當緩存區中數據全部寫滿時,返回false;
//表示緩存區寫滿,并將立即輸出到目標對象中
//第一個例子
var ws = fs.createWriteStream(__dirname + '/test/test.txt');
for (var i = 0; i < 10000; i++) {
var w_flag = ws.write(i.toString());
//當緩存區寫滿時,輸出false
console.log(w_flag);
}
```
```
//第二個例子
var ws = fs.createWriteStream(__dirname + '/test/untiyou.mp3');
var rs = fs.createReadStream(__dirname + '/test/Until You.mp3');
rs.on('data', function (data) {
var flag = ws.write(data);
console.log(flag);
});
//系統緩存區數據已經全部輸出觸發drain事件
ws.on('drain', function () {
console.log('系統緩存區數據已經全部輸出。')
});
```
# 管道pipe實現流讀寫
```
//rs.pipe(destination, [options]);
/**
* destination 必須一個可寫入流數據對象
* [opations] end 默認為true,表示讀取完成立即關閉文件;
*/
var rs = fs.createReadStream(__dirname + '/test/Until You.mp3');
var ws = fs.createWriteStream(__dirname + '/test/untiyou.mp3');
rs.pipe(ws);
rs.on('data', function (data) {
console.log('數據可讀')
});
rs.on('end', function () {
console.log('文件讀取完成');
//ws.end('再見')
});
```
- 第一部分 HTML
- meta
- meta標簽
- HTML5
- 2.1 語義
- 2.2 通信
- 2.3 離線&存儲
- 2.4 多媒體
- 2.5 3D,圖像&效果
- 2.6 性能&集成
- 2.7 設備訪問
- SEO
- Canvas
- 壓縮圖片
- 制作圓角矩形
- 全局屬性
- 第二部分 CSS
- CSS原理
- 層疊上下文(stacking context)
- 外邊距合并
- 塊狀格式化上下文(BFC)
- 盒模型
- important
- 樣式繼承
- 層疊
- 屬性值處理流程
- 分辨率
- 視口
- CSS API
- grid(未完成)
- flex
- 選擇器
- 3D
- Matrix
- AT規則
- line-height 和 vertical-align
- CSS技術
- 居中
- 響應式布局
- 兼容性
- 移動端適配方案
- CSS應用
- CSS Modules(未完成)
- 分層
- 面向對象CSS(未完成)
- 布局
- 三列布局
- 單列等寬,其他多列自適應均勻
- 多列等高
- 圣杯布局
- 雙飛翼布局
- 瀑布流
- 1px問題
- 適配iPhoneX
- 橫屏適配
- 圖片模糊問題
- stylelint
- 第三部分 JavaScript
- JavaScript原理
- 內存空間
- 作用域
- 執行上下文棧
- 變量對象
- 作用域鏈
- this
- 類型轉換
- 閉包(未完成)
- 原型、面向對象
- class和extend
- 繼承
- new
- DOM
- Event Loop
- 垃圾回收機制
- 內存泄漏
- 數值存儲
- 連等賦值
- 基本類型
- 堆棧溢出
- JavaScriptAPI
- document.referrer
- Promise(未完成)
- Object.create
- 遍歷對象屬性
- 寬度、高度
- performance
- 位運算
- tostring( ) 與 valueOf( )方法
- JavaScript技術
- 錯誤
- 異常處理
- 存儲
- Cookie與Session
- ES6(未完成)
- Babel轉碼
- let和const命令
- 變量的解構賦值
- 字符串的擴展
- 正則的擴展
- 數值的擴展
- 數組的擴展
- 函數的擴展
- 對象的擴展
- Symbol
- Set 和 Map 數據結構
- proxy
- Reflect
- module
- AJAX
- ES5
- 嚴格模式
- JSON
- 數組方法
- 對象方法
- 函數方法
- 服務端推送(未完成)
- JavaScript應用
- 復雜判斷
- 3D 全景圖
- 重載
- 上傳(未完成)
- 上傳方式
- 文件格式
- 渲染大量數據
- 圖片裁剪
- 斐波那契數列
- 編碼
- 數組去重
- 淺拷貝、深拷貝
- instanceof
- 模擬 new
- 防抖
- 節流
- 數組扁平化
- sleep函數
- 模擬bind
- 柯里化
- 零碎知識點
- 第四部分 進階
- 計算機原理
- 數據結構(未完成)
- 算法(未完成)
- 排序算法
- 冒泡排序
- 選擇排序
- 插入排序
- 快速排序
- 搜索算法
- 動態規劃
- 二叉樹
- 瀏覽器
- 瀏覽器結構
- 瀏覽器工作原理
- HTML解析
- CSS解析
- 渲染樹構建
- 布局(Layout)
- 渲染
- 瀏覽器輸入 URL 后發生了什么
- 跨域
- 緩存機制
- reflow(回流)和repaint(重繪)
- 渲染層合并
- 編譯(未完成)
- Babel
- 設計模式(未完成)
- 函數式編程(未完成)
- 正則表達式(未完成)
- 性能
- 性能分析
- 性能指標
- 首屏加載
- 優化
- 瀏覽器層面
- HTTP層面
- 代碼層面
- 構建層面
- 移動端首屏優化
- 服務器層面
- bigpipe
- 構建工具
- Gulp
- webpack
- Webpack概念
- Webpack工具
- Webpack優化
- Webpack原理
- 實現loader
- 實現plugin
- tapable
- Webpack打包后代碼
- rollup.js
- parcel
- 模塊化
- ESM
- 安全
- XSS
- CSRF
- 點擊劫持
- 中間人攻擊
- 密碼存儲
- 測試(未完成)
- 單元測試
- E2E測試
- 框架測試
- 樣式回歸測試
- 異步測試
- 自動化測試
- PWA
- PWA官網
- web app manifest
- service worker
- app install banners
- 調試PWA
- PWA教程
- 框架
- MVVM原理
- Vue
- Vue 餓了么整理
- 樣式
- 技巧
- Vue音樂播放器
- Vue源碼
- Virtual Dom
- computed原理
- 數組綁定原理
- 雙向綁定
- nextTick
- keep-alive
- 導航守衛
- 組件通信
- React
- Diff 算法
- Fiber 原理
- batchUpdate
- React 生命周期
- Redux
- 動畫(未完成)
- 異常監控、收集(未完成)
- 數據采集
- Sentry
- 貝塞爾曲線
- 視頻
- 服務端渲染
- 服務端渲染的利與弊
- Vue SSR
- React SSR
- 客戶端
- 離線包
- 第五部分 網絡
- 五層協議
- TCP
- UDP
- HTTP
- 方法
- 首部
- 狀態碼
- 持久連接
- TLS
- content-type
- Redirect
- CSP
- 請求流程
- HTTP/2 及 HTTP/3
- CDN
- DNS
- HTTPDNS
- 第六部分 服務端
- Linux
- Linux命令
- 權限
- XAMPP
- Node.js
- 安裝
- Node模塊化
- 設置環境變量
- Node的event loop
- 進程
- 全局對象
- 異步IO與事件驅動
- 文件系統
- Node錯誤處理
- koa
- koa-compose
- koa-router
- Nginx
- Nginx配置文件
- 代理服務
- 負載均衡
- 獲取用戶IP
- 解決跨域
- 適配PC與移動環境
- 簡單的訪問限制
- 頁面內容修改
- 圖片處理
- 合并請求
- PM2
- MongoDB
- MySQL
- 常用MySql命令
- 自動化(未完成)
- docker
- 創建CLI
- 持續集成
- 持續交付
- 持續部署
- Jenkins
- 部署與發布
- 遠程登錄服務器
- 增強服務器安全等級
- 搭建 Nodejs 生產環境
- 配置 Nginx 實現反向代理
- 管理域名解析
- 配置 PM2 一鍵部署
- 發布上線
- 部署HTTPS
- Node 應用
- 爬蟲(未完成)
- 例子
- 反爬蟲
- 中間件
- body-parser
- connect-redis
- cookie-parser
- cors
- csurf
- express-session
- helmet
- ioredis
- log4js(未完成)
- uuid
- errorhandler
- nodeclub源碼
- app.js
- config.js
- 消息隊列
- RPC
- 性能優化
- 第七部分 總結
- Web服務器
- 目錄結構
- 依賴
- 功能
- 代碼片段
- 整理
- 知識清單、博客
- 項目、組件、庫
- Node代碼
- 面試必考
- 91算法
- 第八部分 工作代碼總結
- 樣式代碼
- 框架代碼
- 組件代碼
- 功能代碼
- 通用代碼