摘要:回調(diào)函數(shù)提供兩個(gè)參數(shù)和,表示有沒有錯(cuò)誤發(fā)生,是文件內(nèi)容。文件關(guān)閉第一個(gè)參數(shù)文件時(shí)傳遞的文件描述符第二個(gè)參數(shù)回調(diào)函數(shù)回調(diào)函數(shù)有一個(gè)參數(shù)錯(cuò)誤,關(guān)閉文件后執(zhí)行。
人所缺乏的不是才干而是志向,不是成功的能力而是勤勞的意志。 —— 部爾衛(wèi)
文章同步到github博客:https://github.com/koala-codi...
前言文件操作是開發(fā)過程中并不可少的一部分。Node.js 中的 fs 模塊是文件操作的封裝,它提供了文件讀取、寫入、更名、刪除、遍歷目錄、鏈接等 POSIX 文件系統(tǒng)操作。與其它模塊不同的是,fs 模塊中所有的操作都提供了異步和同步的兩個(gè)版本,具有 sync 后綴的方法為同步方法,不具有 sync 后綴的方法為異步方法
文章概覽計(jì)算機(jī)中關(guān)于系統(tǒng)和文件的一些常識(shí)
-- 權(quán)限位 mode
-- 標(biāo)識(shí)位 flag
-- 文件描述符 fs
Node.js 中 fs 模塊的 api 詳細(xì)講解與對(duì)應(yīng) Demo
-- 常規(guī)文件操作
-- 高級(jí)文件操作
-- 文件目錄操縱
Node.js 中 fs 模塊的 api 對(duì)應(yīng) demo
fs 模塊的應(yīng)用場(chǎng)景及實(shí)戰(zhàn)訓(xùn)練(大小文件實(shí)現(xiàn)拷貝)
面試會(huì)問說幾個(gè)fs模塊的常用函數(shù)?什么情況下使用fs.open的方式讀取文件?用fs模塊寫一個(gè)大文件拷貝的例子(注意大文件)?文件常識(shí)
計(jì)算機(jī)中的一些文件知識(shí),文件的權(quán)限位 mode、標(biāo)識(shí)位 flag、文件描述符 fd等你有必要了解下。這些內(nèi)容對(duì)于你接下來(lái)學(xué)習(xí) fs 的 api ,記憶和使用都會(huì)有很多幫助。
權(quán)限位 mode因?yàn)?fs 模塊需要對(duì)文件進(jìn)行操作,會(huì)涉及到操作權(quán)限的問題,所以需要先清楚文件權(quán)限是什么,都有哪些權(quán)限。
文件權(quán)限表:
在上面表格中,我們可以看出系統(tǒng)中針對(duì)三種類型進(jìn)行權(quán)限分配,即文件所有者(自己)、文件所屬組(家人)和其他用戶(陌生人),文件操作權(quán)限又分為三種,讀、寫和執(zhí)行,數(shù)字表示為八進(jìn)制數(shù),具備權(quán)限的八進(jìn)制數(shù)分別為 4 、2、1,不具備權(quán)限為 0。
為了更容易理解,我們可以隨便在一個(gè)目錄中打開 Git,使用 Linux 命令 ls -al 來(lái)查目錄中文件和文件夾的權(quán)限位
drwxr-xr-x 1 koala 197121 0 Jun 28 14:41 core -rw-r--r-- 1 koala 197121 293 Jun 23 17:44 index.md
在上面的目錄信息當(dāng)中,很容易看出用戶名、創(chuàng)建時(shí)間和文件名等信息,但最重要的是開頭第一項(xiàng)(十位的字符)。
第一位代表是文件還是文件夾,d 開頭代表文件夾,- 開頭的代表文件,而后面九位就代表當(dāng)前用戶、用戶所屬組和其他用戶的權(quán)限位,按每三位劃分,分別代表讀(r)、寫(w)和執(zhí)行(x),- 代表沒有當(dāng)前位對(duì)應(yīng)的權(quán)限。
權(quán)限參數(shù) mode 主要針對(duì) Linux 和 Unix 操作系統(tǒng),Window 的權(quán)限默認(rèn)是可讀、可寫、不可執(zhí)行,所以權(quán)限位數(shù)字表示為 0o666,轉(zhuǎn)換十進(jìn)制表示為 438。標(biāo)識(shí)位 flag
Node.js 中,標(biāo)識(shí)位代表著對(duì)文件的操作方式,如可讀、可寫、即可讀又可寫等等,在下面用一張表來(lái)表示文件操作的標(biāo)識(shí)位和其對(duì)應(yīng)的含義。
符號(hào) | 含義 |
---|---|
r | 讀取文件,如果文件不存在則拋出異常。 |
r+ | 讀取并寫入文件,如果文件不存在則拋出異常。 |
rs | 讀取并寫入文件,指示操作系統(tǒng)繞開本地文件系統(tǒng)緩存。 |
w | 寫入文件,文件不存在會(huì)被創(chuàng)建,存在則清空后寫入。 |
wx | 寫入文件,排它方式打開。 |
w+ | 讀取并寫入文件,文件不存在則創(chuàng)建文件,存在則清空后寫入。 |
wx+ | 和 w+ 類似,排他方式打開。 |
a | 追加寫入,文件不存在則創(chuàng)建文件。 |
ax | 與 a 類似,排他方式打開。 |
a+ | 讀取并追加寫入,不存在則創(chuàng)建。 |
ax+ | 與 a+ 類似,排他方式打開。 |
上面表格就是這些標(biāo)識(shí)位的具體字符和含義,但是 flag 是不經(jīng)常使用的,不容易被記住,所以在下面總結(jié)了一個(gè)加速記憶的方法。
r:讀取
w:寫入
s:同步
+:增加相反操作
x:排他方式
r+ 和 w+ 的區(qū)別,當(dāng)文件不存在時(shí),r+ 不會(huì)創(chuàng)建文件,而會(huì)拋出異常,但 w+ 會(huì)創(chuàng)建文件;如果文件存在,r+ 不會(huì)自動(dòng)清空文件,但 w+ 會(huì)自動(dòng)把已有文件的內(nèi)容清空。文件描述符 fs
操作系統(tǒng)會(huì)為每個(gè)打開的文件分配一個(gè)名為文件描述符的數(shù)值標(biāo)識(shí),文件操作使用這些文件描述符來(lái)識(shí)別與追蹤每個(gè)特定的文件,Window 系統(tǒng)使用了一個(gè)不同但概念類似的機(jī)制來(lái)追蹤資源,為方便用戶,NodeJS 抽象了不同操作系統(tǒng)間的差異,為所有打開的文件分配了數(shù)值的文件描述符。
在 Node.js 中,每操作一個(gè)文件,文件描述符是遞增的,文件描述符一般從 3 開始,因?yàn)榍懊嬗?0、1、2 三個(gè)比較特殊的描述符,分別代表 process.stdin(標(biāo)準(zhǔn)輸入)、process.stdout(標(biāo)準(zhǔn)輸出)和 process.stderr(錯(cuò)誤輸出)。
文件操作 完整性讀寫文件操作 文件讀取-fs.readFilefs.readFile(filename,[encoding],[callback(error,data)]
文件讀取函數(shù)
它接收第一個(gè)必選參數(shù)filename,表示讀取的文件名。
第二個(gè)參數(shù) encoding 是可選的,表示文件字符編碼。
第三個(gè)參數(shù)callback是回調(diào)函數(shù),用于接收文件的內(nèi)容。
說明:如果不指定 encoding ,則callback就是第二個(gè)參數(shù)。
回調(diào)函數(shù)提供兩個(gè)參數(shù) err 和 data , err 表示有沒有錯(cuò)誤發(fā)生,data 是文件內(nèi)容。
如果指定 encoding , data是一個(gè)解析后的字符串,否則將會(huì)以 Buffer 形式表示的二進(jìn)制數(shù)據(jù)。
demo:
const fs = require("fs"); const path = require("path"); const filePath = path.join(__dirname,"koalaFile.txt") const filePath1 = path.join(__dirname,"koalaFile1.txt") // -- 異步讀取文件 fs.readFile(filePath,"utf8",function(err,data){ console.log(data);// 程序員成長(zhǎng)指北 }); // -- 同步讀取文件 const fileResult=fs.readFileSync(filePath,"utf8"); console.log(fileResult);// 程序員成長(zhǎng)指北文件寫入fs.writeFile
fs.writeFile(filename,data,[options],callback)
文件寫入操作
第一個(gè)必選參數(shù) filename ,表示讀取的文件名
第二個(gè)參數(shù)要寫的數(shù)據(jù)
第三個(gè)參數(shù) option 是一個(gè)對(duì)象,如下
encoding {String | null} default="utf-8" mode {Number} default=438(aka 0666 in Octal) flag {String} default="w"
這個(gè)時(shí)候第一章節(jié)講的計(jì)算機(jī)知識(shí)就用到了,flag值,默認(rèn)為w,會(huì)清空文件,然后再寫。flag值,r代表讀取文件,w代表寫文件,a代表追加。
demo:
// 寫入文件內(nèi)容(如果文件不存在會(huì)創(chuàng)建一個(gè)文件) // 寫入時(shí)會(huì)先清空文件 fs.writeFile(filePath, "寫入成功:程序員成長(zhǎng)指北", function(err) { if (err) { throw err; } // 寫入成功后讀取測(cè)試 var data=fs.readFileSync(filePath, "utf-8"); console.log("new data -->"+data); }); // 通過文件寫入并且利用flag也可以實(shí)現(xiàn)文件追加 fs.writeFile(filePath, "程序員成長(zhǎng)指北追加的數(shù)據(jù)", {"flag":"a"},function(err) { if (err) { throw err; } console.log("success"); var data=fs.readFileSync(filePath, "utf-8") // 寫入成功后讀取測(cè)試 console.log("追加后的數(shù)據(jù) -->"+data); });文件追加-appendFile
fs.appendFile(filename, data, [options], callback)
第一個(gè)必選參數(shù) filename ,表示讀取的文件名
第二個(gè)參數(shù) data,data 可以是任意字符串或者緩存
第三個(gè)參數(shù) option 是一個(gè)對(duì)象,與write的區(qū)別就是[options]的flag默認(rèn)值是”a”,所以它以追加方式寫入數(shù)據(jù).
說明:該方法以異步的方式將 data 插入到文件里,如果文件不存在會(huì)自動(dòng)創(chuàng)建
demo:
// -- 異步另一種文件追加操作(非覆蓋方式) // 寫入文件內(nèi)容(如果文件不存在會(huì)創(chuàng)建一個(gè)文件) fs.appendFile(filePath, "新數(shù)據(jù)程序員成長(zhǎng)指北456", function(err) { if (err) { throw err; } // 寫入成功后讀取測(cè)試 var data=fs.readFileSync(filePath, "utf-8"); console.log(data); }); // -- 同步另一種文件追加操作(非覆蓋方式) fs.appendFileSync(filePath, "同步追加一條新數(shù)據(jù)程序員成長(zhǎng)指北789");拷貝文件-copyFile
fs.copyFile(filenameA, filenameB,callback)
第一個(gè)參數(shù)原始文件名
第二個(gè)參數(shù)要拷貝到的文件名
demo:
// 將filePath文件內(nèi)容拷貝到filePath1文件內(nèi)容 fs.copyFileSync(filePath, filePath1); let data = fs.readFileSync(filePath1, "utf8"); console.log(data); // 程序員成長(zhǎng)指北刪除文件-unlink
fs.unlink(filename, callback)
第一個(gè)參數(shù)文件路徑大家應(yīng)該都知道了,后面我就不重復(fù)了
第二個(gè)回調(diào)函數(shù) callback
demo:
// -- 異步文件刪除 fs.unlink(filePath,function(err){ if(err) return; }); // -- 同步刪除文件 fs.unlinkSync(filePath,function(err){ if(err) return; });指定位置讀寫文件操作(高級(jí)文件操作)
接下來(lái)的高級(jí)文件操作會(huì)與上面有些不同,流程稍微復(fù)雜一些,要先用fs.open來(lái)打開文件,然后才可以用fs.read去讀,或者用fs.write去寫文件,最后,你需要用fs.close去關(guān)掉文件。
特殊說明:read 方法與 readFile 不同,一般針對(duì)于文件太大,無(wú)法一次性讀取全部?jī)?nèi)容到緩存中或文件大小未知的情況,都是多次讀取到 Buffer 中。文件打開-fs.open
想了解 Buffer 可以看 NodeJS —— Buffer 解讀。(注意這里換成我的文章)
fs.open(path,flags,[mode],callback)
第一個(gè)參數(shù):文件路徑
第二個(gè)參數(shù):與開篇說的標(biāo)識(shí)符 flag 相同
第三個(gè)參數(shù):[mode] 是文件的權(quán)限(可選參數(shù),默認(rèn)值是0666)
第四個(gè)參數(shù):callback 回調(diào)函數(shù)
demo:
fs.open(filePath,"r","0666",function(err,fd){ console.log("哈哈哈",fd); //返回的第二個(gè)參數(shù)為一個(gè)整數(shù),表示打開文件返回的文件描述符,window中又稱文件句柄 })
demo 說明:返回的第二個(gè)參數(shù)為一個(gè)整數(shù),表示打開文件返回的文件描述符,window中又稱文件句柄,在開篇也有對(duì)文件描述符說明。
文件讀取-fs.readfs.read(fd, buffer, offset, length, position, callback);
六個(gè)參數(shù)
fd:文件描述符,需要先使用 open 打開,使用fs.open打開成功后返回的文件描述符;
buffer:一個(gè) Buffer 對(duì)象,v8引擎分配的一段內(nèi)存,要將內(nèi)容讀取到的 Buffer;
offset:整數(shù),向 Buffer 緩存區(qū)寫入的初始位置,以字節(jié)為單位;
length:整數(shù),讀取文件的長(zhǎng)度;
position:整數(shù),讀取文件初始位置;文件大小以字節(jié)為單位
callback:回調(diào)函數(shù),有三個(gè)參數(shù) err(錯(cuò)誤),bytesRead(實(shí)際讀取的字節(jié)數(shù)),buffer(被寫入的緩存區(qū)對(duì)象),讀取執(zhí)行完成后執(zhí)行。
demo:
const fs = require("fs"); let buf = Buffer.alloc(6);// 創(chuàng)建6字節(jié)長(zhǎng)度的buf緩存對(duì)象 // 打開文件 fs.open("6.txt", "r", (err, fd) => { // 讀取文件 fs.read(fd, buf, 0, 3, 0, (err, bytesRead, buffer) => { console.log(bytesRead); console.log(buffer); // 繼續(xù)讀取 fs.read(fd, buf, 3, 3, 3, (err, bytesRead, buffer) => { console.log(bytesRead); console.log(buffer); console.log(buffer.toString()); }); }); }); // 3 //文件寫入-fs.write// 3 // // 你好
fs.write(fd, buffer, offset, length, position, callback);
六個(gè)參數(shù)
fd:文件描述符,使用fs.open 打開成功后返回的;
buffer:一個(gè) Buffer 對(duì)象,v8 引擎分配的一段內(nèi)存,存儲(chǔ)將要寫入文件數(shù)據(jù)的 Buffer;
offset:整數(shù),從 Buffer 緩存區(qū)讀取數(shù)據(jù)的初始位置,以字節(jié)為單位;
length:整數(shù),讀取 Buffer 數(shù)據(jù)的字節(jié)數(shù);
position:整數(shù),寫入文件初始位置;
callback:寫入操作執(zhí)行完成后回調(diào)函數(shù),有三個(gè)參數(shù) err(錯(cuò)誤),bytesWritten(實(shí)際寫入的字節(jié)數(shù)),buffer(被讀取的緩存區(qū)對(duì)象),寫入完成后執(zhí)行。
demo:
文件關(guān)閉-fs.closefs.close(fd,callback)
第一個(gè)參數(shù):fd 文件open時(shí)傳遞的文件描述符
第二個(gè)參數(shù) callback 回調(diào)函數(shù),回調(diào)函數(shù)有一個(gè)參數(shù) err(錯(cuò)誤),關(guān)閉文件后執(zhí)行。
demo:
// 注意文件描述符fd fs.open(filePath, "r", (err, fd) => { fs.close(fd, err => { console.log("關(guān)閉成功");// 關(guān)閉成功 }); });目錄(文件夾)操作
1、fs.mkdir 創(chuàng)建目錄
fs.mkdir(path, [options], callback)
第一個(gè)參數(shù):path 目錄路徑
第二個(gè)參數(shù)[options],recursive
mode
第三個(gè)參數(shù)回調(diào)函數(shù),回調(diào)函數(shù)有一個(gè)參數(shù) err(錯(cuò)誤),關(guān)閉文件后執(zhí)行。
demo:
fs.mkdir("./mkdir",function(err){ if(err) return; console.log("創(chuàng)建目錄成功"); })
注意:
在 Windows 上,在根目錄上使用 fs.mkdir() (即使使用遞歸參數(shù))也會(huì)導(dǎo)致錯(cuò)誤:
fs.mkdir("/", { recursive: true }, (err) => { // => [Error: EPERM: operation not permitted, mkdir "C:"] });
2、fs.rmdir刪除目錄
fs.rmdir(path,callback)
第一個(gè)參數(shù):path目錄路徑
第三個(gè)參數(shù)回調(diào)函數(shù),回調(diào)函數(shù)有一個(gè)參數(shù) err(錯(cuò)誤),關(guān)閉文件后執(zhí)行。
demo:
const fs = require("fs"); fs.rmdir("./mkdir",function(err){ if(err) return; console.log("刪除目錄成功"); })
注意:在文件(而不是目錄)上使用 fs.rmdir() 會(huì)導(dǎo)致在 Windows 上出現(xiàn) ENOENT 錯(cuò)誤、在 POSIX 上出現(xiàn) ENOTDIR 錯(cuò)誤。
3、fs.readdir讀取目錄
fs.readdir(path, [options], callback)
第一個(gè)參數(shù):path 目錄路徑
第二個(gè)參數(shù)[options]可選的 options 參數(shù)可以是指定編碼的字符串,也可以是具有 encoding 屬性的對(duì)象,該屬性指定用于傳給回調(diào)的文件名的字符編碼。 如果 encoding 設(shè)置為 "buffer",則返回的文件名是 Buffer 對(duì)象。
如果 options.withFileTypes 設(shè)置為 true,則 files 數(shù)組將包含 fs.Dirent 對(duì)象。
第三個(gè)參數(shù)回調(diào)函數(shù),回調(diào)函數(shù)有兩個(gè)參數(shù),第一個(gè) err(錯(cuò)誤),第二個(gè)返回 的data 為一個(gè)數(shù)組,包含該文件夾的所有文件,是目錄中的文件名的數(shù)組(不包括 "." 和 "..")。
demo:
const fs = require("fs"); fs.readdir("./file",function(err,data){ if(err) return; //data為一個(gè)數(shù)組 console.log("讀取的數(shù)據(jù)為:"+data[0]); });實(shí)戰(zhàn)訓(xùn)練:
只講文件相關(guān) Api 顯得很枯燥,下面說一些 fs 在 Node.js 中的具體應(yīng)用
「示例:fs 模塊如何實(shí)現(xiàn)文件拷貝」文件拷貝例子包括小文件拷貝和大文件拷貝(之前講的 fs 模塊也可以實(shí)現(xiàn)文件拷貝)
小文件拷貝小文件拷貝除了上面 fs 自己提供的 api 我們自己也可以通過讀寫完成一個(gè)拷貝例子,如下:
// 文件拷貝 將data.txt文件中的內(nèi)容拷貝到copyData.txt // 讀取文件 const fileName1 = path.resolve(__dirname, "data.txt") fs.readFile(fileName1, function (err, data) { if (err) { // 出錯(cuò) console.log(err.message) return } // 得到文件內(nèi)容 var dataStr = data.toString() // 寫入文件 const fileName2 = path.resolve(__dirname, "copyData.txt") fs.writeFile(fileName2, dataStr, function (err) { if (err) { // 出錯(cuò) console.log(err.message) return } console.log("拷貝成功") }) })
我們使用 readFile 和 writeFile 實(shí)現(xiàn)了一個(gè) copy 函數(shù),那個(gè) copy 函數(shù)是將被拷貝文件的數(shù)據(jù)一次性讀取到內(nèi)存,一次性寫入到目標(biāo)文件中,這種針對(duì)小文件還好。
大文件拷貝如果是一個(gè)大文件幾百M(fèi)一次性讀取寫入不現(xiàn)實(shí),所以需要多次讀取多次寫入,接下來(lái)使用文件操作的高級(jí)方法對(duì)大文件和文件大小未知的情況實(shí)現(xiàn)一個(gè) copy 函數(shù)。當(dāng)然除了這種方式還有我在之前的文章講過的stream模塊也可以實(shí)現(xiàn),而且性能更好,但是這里就不再重復(fù)說明,本篇主要講fs模塊。
demo:
// copy 方法 function copy(src, dest, size = 16 * 1024, callback) { // 打開源文件 fs.open(src, "r", (err, readFd) => { // 打開目標(biāo)文件 fs.open(dest, "w", (err, writeFd) => { let buf = Buffer.alloc(size); let readed = 0; // 下次讀取文件的位置 let writed = 0; // 下次寫入文件的位置 (function next() { // 讀取 fs.read(readFd, buf, 0, size, readed, (err, bytesRead) => { readed += bytesRead; // 如果都不到內(nèi)容關(guān)閉文件 if (!bytesRead) fs.close(readFd, err => console.log("關(guān)閉源文件")); // 寫入 fs.write(writeFd, buf, 0, bytesRead, writed, (err, bytesWritten) => { // 如果沒有內(nèi)容了同步緩存,并關(guān)閉文件后執(zhí)行回調(diào) if (!bytesWritten) { fs.fsync(writeFd, err => { fs.close(writeFd, err => return !err && callback()); }); } writed += bytesWritten; // 繼續(xù)讀取、寫入 next(); }); }); })(); }); }); }
在上面的 copy 方法中,我們手動(dòng)維護(hù)的下次讀取位置和下次寫入位置,如果參數(shù) readed 和 writed 的位置傳入 null,NodeJS 會(huì)自動(dòng)幫我們維護(hù)這兩個(gè)值。
現(xiàn)在有一個(gè)文件 6.txt 內(nèi)容為 “你好”,一個(gè)空文件 7.txt,我們將 6.txt 的內(nèi)容寫入 7.txt 中。
const fs = require("fs"); // buffer 的長(zhǎng)度 const BUFFER_SIZE = 3; // 拷貝文件內(nèi)容并寫入 copy("6.txt", "7.txt", BUFFER_SIZE, () => { fs.readFile("7.txt", "utf8", (err, data) => { // 拷貝完讀取 7.txt 的內(nèi)容 console.log(data); // 你好 }); });
在 NodeJS 中進(jìn)行文件操作,多次讀取和寫入時(shí),一般一次讀取數(shù)據(jù)大小為 64k,寫入數(shù)據(jù)大小為 16k。
?大家好,我是koala,在做一個(gè)一個(gè)Node.js高級(jí)進(jìn)階路線,今天就分享這么多,如果對(duì)分享的內(nèi)容感興趣,可以關(guān)注公眾號(hào)「程序員成長(zhǎng)指北」,或者加入技術(shù)交流群,大家一起討論。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/106176.html
6. 目錄操作 6.1 創(chuàng)建目錄 如果存在該目錄,就創(chuàng)建失敗 同步創(chuàng)建目錄fs.mkdirSync(path, [mode]) const fs = require(fs); let mkdir = ./mkdir; fs.mkdir(mkdir, (err) => { if (err) { console.log(`mkdir ${mkdir} file faile...
摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。對(duì)該漏洞的綜合評(píng)級(jí)為高危。目前,相關(guān)利用方式已經(jīng)在互聯(lián)網(wǎng)上公開,近期出現(xiàn)攻擊嘗試爆發(fā)的可能。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn);分為新聞熱點(diǎn)、開發(fā)教程、工程實(shí)踐、深度閱讀、開源項(xiàng)目、巔峰人生等欄目。歡...
摘要:感謝大神的免費(fèi)的計(jì)算機(jī)編程類中文書籍收錄并推薦地址,以后在倉(cāng)庫(kù)里更新地址,聲音版全文狼叔如何正確的學(xué)習(xí)簡(jiǎn)介現(xiàn)在,越來(lái)越多的科技公司和開發(fā)者開始使用開發(fā)各種應(yīng)用。 說明 2017-12-14 我發(fā)了一篇文章《沒用過Node.js,就別瞎逼逼》是因?yàn)橛腥嗽谥跎虾贜ode.js。那篇文章的反響還是相當(dāng)不錯(cuò)的,甚至連著名的hax賀老都很認(rèn)同,下班時(shí)讀那篇文章,竟然坐車的還坐過站了。大家可以很...
摘要:感謝大神的免費(fèi)的計(jì)算機(jī)編程類中文書籍收錄并推薦地址,以后在倉(cāng)庫(kù)里更新地址,聲音版全文狼叔如何正確的學(xué)習(xí)簡(jiǎn)介現(xiàn)在,越來(lái)越多的科技公司和開發(fā)者開始使用開發(fā)各種應(yīng)用。 說明 2017-12-14 我發(fā)了一篇文章《沒用過Node.js,就別瞎逼逼》是因?yàn)橛腥嗽谥跎虾贜ode.js。那篇文章的反響還是相當(dāng)不錯(cuò)的,甚至連著名的hax賀老都很認(rèn)同,下班時(shí)讀那篇文章,竟然坐車的還坐過站了。大家可以很...
閱讀 2398·2021-10-09 09:41
閱讀 3200·2021-09-26 09:46
閱讀 846·2021-09-03 10:34
閱讀 3185·2021-08-11 11:22
閱讀 3380·2019-08-30 14:12
閱讀 721·2019-08-26 11:34
閱讀 3353·2019-08-26 11:00
閱讀 1785·2019-08-26 10:26