Node.js(一)——(Node.js安裝及使用,通過Node.js搭建伺服器,模組化及自定義模組,npm/yarn/nvm,內建模組fs的使用,buffer及stream,新聞列表案例)

黎小小咩~發表於2020-04-21

目錄

1.Node.js介紹

2.安裝Node.js

3.使用Node.js實現第一個伺服器

3.1初步感受Node.js

3.2Google Chrome 預設非安全埠列表,儘量避免以下埠。

3.3nodemon自動監控服務端更改

4.模組化——Node.js使用commonjs規範

4.1建立自定義模組(引入檔案/資料夾,按需匯出)

4.1.1引入檔案

4.1.2引入資料夾

4.1.3引入node_modules裡的資料夾

4.1.4描述功能性檔案package.json

4.1.5 自定義模組的按需匯出

4.2.2內建模組;

5.npm包管理器

5.1 npm常用指令;

5.2註冊與釋出npm專案

5.2.1 註冊賬號:

5.2.2釋出包

5.2.3cnpm的安裝及使用

6.fs模組(內建模組)

6.1寫入檔案writeFile()

6.2刪除檔案

6.3修改檔名

6.4讀取檔案

6.5複製檔案

6.6自定義複製檔案實現原理

6.7建立目錄

6.8修改目錄名

6.9讀取目錄中的檔案及子目錄

6.10刪除目錄

6.10判斷檔案或目錄是否存在

6.11獲取檔案或目錄的詳細資訊

6.12刪除非空資料夾

7.buffer緩衝區

7.1buffer的建立:

8.stream流

8.1data:監控得到的資料,並將其分成多份

8.2 end監控檔案是否讀取完成

8.3pipe:管道,將得到的資料通過pipe進行寫入(fs.createWriteStream())

9.包管理工具yarn 

10.node.js版本管理工具NVM

10.1NVM下載

10.2安裝NVM

10.3NVM常用指令

11.通過fs模組載入頁面

11.1- 普通方式載入頁面

11.2- 通過stream流方式載入頁面

12.node+cheerio實現爬蟲獲取資料

13.實現新聞列表頁面


本節知識要點

  • - Node.js安裝及使用
  • - 通過Node.js搭建伺服器
  • - 模組化及自定義模組
  • - 內建模組fs的使用
  • - buffer及stream

1.Node.js介紹

- Node.js 誕生於2009年,Node.js採用C++語言編寫而成,是 一個Javascript的執行環境。Node.js 是一個基於 Chrome V8 引擎的 JavaScript 執行環境 ,讓JavaScript的執行脫離瀏覽器端,可以使用JavaScript語言 書寫伺服器端程式碼。

2.安裝Node.js

​ [Node.js官網](https://nodejs.org)下載穩定版本,node偶數版本為穩定版本,奇數版本為非穩定版本。

  1. - mac 直接安裝 或者 brew來安裝;windows直接點下一步安裝(注意windows系統下需要配置環境變數)
  2. - 安裝完Node.js會自動安裝NPM(Node Package Manager):包管理工具;
  3. - 通過指令 node -v 來檢視是否安裝完成和檢視node版本號;npm -v 來檢視npm版本。

3.使用Node.js實現第一個伺服器

3.1初步感受Node.js

//引入http模組
let http = require("http");
//建立一個伺服器
let serve = http.createServer((req, res) => {
    console.log("hello");
    res.end("hello world");
})

//設定埠號
serve.listen(3000);
  • 使用快捷鍵ctrl + `開啟VScode的終端;
  • 使用node server.js(node js檔名)執行js檔案;

結果:瀏覽器端列印hello world; 服務端列印hello

3.2Google Chrome 預設非安全埠列表,儘量避免以下埠。

1, // tcpmux
7, // echo
9, // discard
11, // systat
13, // daytime
15, // netstat
17, // qotd
19, // chargen
20, // ftp data
21, // ftp access
22, // ssh
23, // telnet
25, // smtp
37, // time
42, // name
43, // nicname
53, // domain
77, // priv-rjs
79, // finger
87, // ttylink
95, // supdup
101, // hostriame
102, // iso-tsap
103, // gppitnp
104, // acr-nema
109, // pop2
110, // pop3
111, // sunrpc
113, // auth
115, // sftp
117, // uucp-path
119, // nntp
123, // NTP
135, // loc-srv /epmap
139, // netbios
143, // imap2
179, // BGP
389, // ldap
465, // smtp+ssl
512, // print / exec
513, // login
514, // shell
515, // printer
526, // tempo
530, // courier
531, // chat
532, // netnews
540, // uucp
556, // remotefs
563, // nntp+ssl
587, // stmp?
601, // ??
636, // ldap+ssl
993, // ldap+ssl
995, // pop3+ssl
2049, // nfs
3659, // apple-sasl / PasswordServer
4045, // lockd
6000, // X11
6665, // Alternate IRC [Apple addition]
6666, // Alternate IRC [Apple addition]
6667, // Standard IRC [Apple addition]
6668, // Alternate IRC [Apple addition]
6669, // Alternate IRC [Apple addition]

3.3nodemon自動監控服務端更改

使用node js檔名,方式在node.js下啟動時,如果JS檔案進行了修改就必須,重啟服務;

解決:使用nodemon會自動監聽服務端JS檔案的修改

在全域性下安裝nodemon的命令:npm i nodemon -g

nodemon啟動:通過nodemon命令 nodemon JS檔名 啟動檔案後,會自動監聽和重啟伺服器。如 nodemon index.js

4.模組化——Node.js使用commonjs規範

為什麼會有模組化:

- 在JavaScript發展初期就是為了實現簡單的頁面互動邏輯,隨著前端程式碼日益膨脹,JavaScript作為嵌入式的指令碼語言的定位動搖了,JavaScript卻沒有為組織程式碼提供任何明顯幫助,JavaScript極其簡單的程式碼組織規範不足以駕馭如此龐大規模的程式碼;

每個模組之間有獨立的空間,從而防止模組間的變數汙染

Node.js中的模組化 commonjs規範:

- CommonJS就是為JS的表現來制定規範,因為js沒有模組的功能所以CommonJS應運而生,它希望js可以在任何地方執行,不只是瀏覽器中。

前端規範:AMD sea.js  和 CMD require.js

node.js安裝完成後,就自帶commonjs規範。

4.1建立自定義模組(引入檔案/資料夾,按需匯出)

模組化引入:可以引入檔案,資料夾和node_modules資料夾下的自定義資料夾。

4.1.1引入檔案

注意:當引入檔案或自定義資料夾時require("./mydir")中需要加"./",當引入的是node_modules時,不能加"./",如require("node_modules")

  • - 引入一個檔案 形式模組:require("./JS檔名"),也可以省略.js即require("./home");

moduleA.js:

console.log("這是moduleA.js 檔案");

home.js執行檔案:輸入命令:nodemon home.js

console.log("這是home主頁JS檔案");
require("./moduleA.js");

結果:執行home.js檔案中的內容時也能執行引入檔案moduleA.js檔案中的內容

4.1.2引入資料夾

  • - 引入資料夾形式模組 require("./資料夾名")

如下:home.js為執行JS檔案,自身執行及引入moduleA.js的同時引入了資料夾mydir ,則在資料夾mydir中會預設去找index.js檔案,發現index.js引入了a.js檔案,而a.js檔案引入了b.js檔案,所以執行home.js時,會執行index.js a.js b.js中的內容。

home.js執行檔案:

console.log("這是home主頁JS檔案");
require("./moduleA.js");//引入moduleA.js檔案
require("./mydir");//引入資料夾mydir,就會自動查詢資料夾mydir下的index.js檔案

​ home.js引入mydir資料夾後,會自動查詢資料夾下的index.js檔案執行:

index.js檔案:

console.log("這是index.js檔案");
require("./a");

index.js中引入了a.js檔案:

console.log("這是a.js檔案");
require("./b");

a.js檔案中又引入了b.js檔案:

console.log("這是b.js檔案");

結果:所以最後結果是,會執行mydir下所有js檔案,index.js a.js  b.js檔案

  • - 當然也可以配置預設啟動檔案(預設index.js檔案,但也可以修改),在資料夾內新建package.json來指定執行檔案
{
"name":"aModule",
"version":"1.0.0",
"main":"test.js"

}

4.1.3引入node_modules裡的資料夾

node_modules主要針對第三方JS檔案的管理。

引入node_modules裡的資料夾時,資料夾不需要加"./",如,require("資料夾")

package.json:描述功能性檔案。可以在裡面設定node_modules中的預設執行檔案,版本號,資料夾名等配置

示例:

檔案目錄層級:

home.js為執行檔案:在home.js中引入node_modules中的資料夾mytest

require("mytest");

index.js:

console.log("這是index.js檔案");
require("./a");

a.js:

console.log("這是a.js檔案");
require("./b");

b.js:

console.log("這是b.js檔案");

結果:

4.1.4描述功能性檔案package.json

專案上線或者專案轉移時,不會轉移或上線node_modules資料夾,而是使用時再通過package.json去下載第三方模組

node_modules的查詢規則:向上查詢。首先會在當前資料夾下查詢node_modules,沒有再查詢上一級資料夾下有沒有node_modules,再沒有會找到系統根目錄user資料夾下的node_modules。

引入node_modules資料夾下的自定義資料夾時,預設去執行資料夾下面的index.js。

如果想引入的資料夾預設執行檔案不是index.js,而執行自定義的資料夾,需要通過package.json檔案定義:

package.json檔案:

{
    "name":"mytest",
    "version":"1.0.0",
    "main":"test.js"
}

test.js:

console.log("使用了package.json檔案,預設引入text.js");

結果:

4.1.5 自定義模組的按需匯出

通過module.exports 匯出; \_\__dirname , \_\_filename

exports是module.exports的引用 :可以使用module.exports = {}進行匯出,而不能使用exports = {}進行匯出,要使用exports匯出,只能使用exports.a 的形式。因為exports是module.exports的一個引用。

__dirname: 獲得當前執行檔案所在目錄的完整目錄名
__filename: 獲得當前執行檔案的帶有完整絕對路徑的檔名
process.cwd():獲得當前執行node命令時候的資料夾目錄名
  • 通過module.exports 匯出

Ma.js:有變數a和類Person,通過module.exports = {}將變數a和Person類進行匯出

console.log("這是Ma.js檔案");
let a = 10;
class Person{
    constructor(){
        this.name = "zhangsan";
    }
    hobby(){
        console.log("喜歡籃球");
    }
}
module.exports = {
    a,
    Person
}

Mb.js:使用require("./Ma.js")引入檔案,將引入後的結果存起來,再獲取裡面的變數a和Person類

console.log("這是Mb.js檔案");
let Ma = require("./Ma");
console.log(Ma.a);
let p = new Ma.Person();
p.hobby();
  • 或者 通過 exports來匯出。

exports是module.exports的一個引用,使用時不能使用exports = {}進行匯出(不會改變module.exports的值,只會改變exports 的值就沒有意義),必須使用exports.a 和exports.Person

所以上例可以改寫為以下:

Ma.js:

console.log("這是Ma.js檔案");
let a = 10;
class Person{
    constructor(){
        this.name = "zhangsan";
    }
    hobby(){
        console.log("喜歡籃球");
    }
}
module.exports = {
    a,
    Person
}

exports.a = a;
exports.Person = Person;

//解構賦值
// exports.hobby = new Person().hobby;

Mb.js::注意使用解構賦值時 {hobby}結構的值必須和exports.hobby的值相同

console.log("這是Mb.js檔案");
let Ma = require("./Ma");
console.log(Ma.a);
let p = new Ma.Person();
p.hobby();

//也可以通過結構賦值方式得到值
// 或者 通過解構賦值 
// let { hobby } = require("./Ma");
// hobby();
  • - 模組載入的優先順序 ,先檔案再目錄;

4.2.2內建模組;

內建模組即不需要下載,nodejs中本身就有的模組,內建模組不需要安裝,外接模組需要安裝;

nodejs內建模組有:Buffer,C/C++Addons,Child Processes,Cluster,Console,Crypto,Debugger,DNS,Domain,Errors,Events,File System,Globals,HTTP,HTTPS,Modules,Net,OS,Path,Process,P unycode,Query Strings,Readline,REPL,Stream,String De coder,Timers,TLS/SSL,TTY,UDP/Datagram,URL, Utilities,V8,VM,ZLIB;

5.npm包管理器

安裝node.js時,npm也會一起安裝,npm的版本號和node.js的版本號是關聯的。一般如果npm版本不夠,只要升級node.js的版本即可。

NPM(Node Package Manager) 官網的地址是 [npm官網](https://www.npmjs.com)  ,可以註冊和釋出專案。

node.js是輕量級載入,用到某個模組時再去安裝,沒有用到就先不安裝。

5.1 npm常用指令;

  • - npm init:引導建立一個package.json檔案
  • - npm help(npm -h) :檢視npm幫助資訊
  • - npm version (npm -v) : 檢視npm版本;
  • - npm search:查詢
  • - npm install (npm i)  module_name :安裝 預設在當前目錄,如果沒有node_modules 會建立node_modules資料夾,並將下載的第三方模組放在建立好的node_modules裡;
  • - npm install module_name -S 或者--save 即 npm install module_name --save 寫入dependencies執行依賴新版本中預設會加上-S(--save)
  • - npm install module_name -D 或者 --save-dev 即 npm install module_name --save-dev 寫入devDependencies開發依賴
  • - npm install module_name -g 全域性安裝(命令列使用),即會按照在npm root -g下。如C:\Users\Administrator\AppData\Roaming\npm\node_modules即npm安裝的系統根目錄
  • - 指定版本安裝模組 npm i module_name @1.0 通過 "@"符號指定;
  • - npm update(npm -up):更新
  • - npm remove 或者 npm uninstall module_name :刪除
  • - npm root 檢視當前包安裝(即node-modules)的路徑 或者通過 npm root -g 來檢視全域性安裝路徑
  • npm config set registry https://registry.npmjs.org/ (官方地址)設定下載源地址,https://registry.npm.taobao.org/(淘寶源地址)
  • npm config list 查詢源地址 

示例:使用npm i cookie建立cookie模組:

如在npm資料夾下執行這一命令後,會自定建立一個node_modules資料夾以及package.json和package-lock.json檔案,並建立cookie模組

"cookie": "^0.4.0"的^表示安裝時自動查詢或使用0.4.0及其以上版本

使用npm init 引導建立package.json檔案:

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

5.2註冊與釋出npm專案

5.2.1 註冊賬號:

[https://www.npmjs.com/](https://www.npmjs.com/) (郵箱驗證)

- npm adduser 輸入剛剛註冊好的使用者名稱和密碼 ;

如果註冊地址不對需要重新改回來:使用淘寶源地址源下載速度快些。

  • npm config set registry https://registry.npmjs.org/  (官方地址)
  • https://registry.npm.taobao.org/(淘寶源地址)

查詢源地址:npm config list

5.2.2釋出包

建立專案:index.js預設入口檔案;npm i 建立package.json檔案,npm start啟動專案

  • 釋出publish 命令 :npm publish 專案名  。專案名不能重複
  • 下載釋出上去的專案:npm i 專案名
  • 刪除釋出的專案:npm unpublish 專案名

5.2.3cnpm的安裝及使用

cnpm是淘寶出的。使用cnpm會直接在淘寶源地址進行下載。

一般不建議使用cnpm命令 。下載地址會很怪,且會出現未知問題。

可以使用npm命令,使用淘寶源,下載地址就會是國內地址

安裝命令:會到國內淘寶源地址進行下載安裝

$ npm install -g cnpm --registry=https://registry.npm.taobao.org

6.fs模組(內建模組)

內建模組只需要引入,不需要安裝。

fs模組:針對檔案和目錄的增刪改查。

- fs是檔案操作模組,所有檔案操作都是有同步和非同步之分,特點是同步會加上 "Sync" 如:非同步讀取檔案 "readFile",同步讀取檔案 "readFileSync";

檔案操作:

6.1寫入檔案writeFile()

let fs = require("fs");

fs.writeFile(filename,data,[options],callback)寫入內容到檔案.

  1. 第一個引數filename:檔名;
  2. 第二個引數data:要寫入的內容;寫入資料的型別必須是字串或buffer二進位制資料 ,物件等。如果 data 是一個 buffer,則 encoding 選項會被忽略。如果 options 是一個字串,則可以指定字元編碼。
  3. 第三個引數options:可選,物件形式。encoding 檔案編碼,mode 寫入模式,寫入方式(a追加,w覆蓋),如{flag:'w'}
  • option Object
    • encoding String |nulldefault='utf-8'
    • mode Number default=438(aka 0666 in Octal)
    • flag Stringdefault='w'

在同一個檔案上多次使用 fs.writeFile() 且不等待回撥是不安全的。 對於這種情況,建議使用 fs.createWriteStream()

非同步寫入:

let fs = require("fs");

//非同步寫入
fs.writeFile("1.txt","323",{flag:'w'},function(err){
    //如果有錯誤,就返回錯誤
    if(err){
        return console.log(err);
    }
    //寫入成功
    console.log("寫入成功");
});

同步寫入:沒有錯誤回撥

fs.writeFileSync("2.txt","sdfa",{flags:'a'});

6.2刪除檔案

非同步刪除:

//非同步刪除檔案
fs.unlink("2.txt",function(err){
    //刪除失敗
    if(err){
        return console.log(err);
    }
    console.log("刪除成功");
});

同步刪除:

//同步刪除檔案
fs.unlinkSync("1.txt");

6.3修改檔名

非同步修改檔名:

fs.rename(oldFile,newFile,callback)

//非同步修改檔案
fs.rename("1.txt","11.txt",err=>{
    if(err){
        return console.log(err);
    }
    console.log("修改檔名成功");
});

同步修改檔名:

//同步修改檔名
fs.renameSync("11.txt","2.txt");

6.4讀取檔案

非同步讀取:

fs.readFile(path: string | number | Buffer | URL,callback)

//非同步讀取檔案
fs.readFile("1.txt",(err,data)=>{
    if(err){
        return console.log(err);
    }
    console.log("讀取成功"+data);
    
});

同步讀取:

readFileSync(path: string | number | Buffer | URL, options?: {
encoding?: null;
flag?: string;
})

直接讀取出來的資料時Buffer資料,需要使用toString()將其轉換為字串

//同步讀取檔案
let data = fs.readFileSync("1.txt").toString();
console.log(data);

6.5複製檔案

非同步複製檔案:

copyFile(src: PathLike, dest: PathLike, callback)

//非同步複製檔案
fs.copyFile("1.txt","3.txt",err=>{
    if(err){
        return console.log(err);
    }
    console.log("複製成功");
});

同步複製檔案:

//同步複製檔案
fs.copyFileSync("3.txt","4.txt");

6.6自定義複製檔案實現原理

原理:先讀取到檔案,再將讀取到的檔案進行寫入

//複製檔案實現原理
function copyFile(src,dir){
    let data = fs.readFileSync(src).toString();
    fs.writeFileSync(dir,data);
}

copyFile("1.txt","5.txt");

6.7建立目錄

非同步建立目錄:

//建立目錄
fs.mkdir("11",err=>{
    if(err){
        return console.log(err);
    }
    console.log("建立成功");
    
});

同步建立目錄:

fs.mkdirSync("22");

6.8修改目錄名

非同步修改目錄名:

//非同步修改目錄名
fs.rename("11","44",err=>{
    if(err){
        return console.log(err);
    }
    console.log(修改目錄成功);
});

同步修改目錄名:

fs.renameSync("44","33");

6.9讀取目錄中的檔案及子目錄

會將目錄中的子目錄和檔案都讀取出來,放到陣列中

//非同步讀取目錄中的檔案和子目錄
fs.readdir("33",(err,files)=>{
    if(err){
        return console.log(err);
    }
    console.log(files);//[ '1.txt', '2.txt', '22' ]
})
//同步讀取目錄中的檔案和子目錄
let data = fs.readdirSync("33");
console.log(data);//[ '1.txt', '2.txt', '22' ]

6.10刪除目錄

刪除的目錄必須是空目錄,否則刪除不了

//刪除空目錄
fs.rmdir("./33/22",err=>{
    if(err){
        return console.log(err);
    }
    console.log("刪除成功");
});

6.10判斷檔案或目錄是否存在

//判斷檔案或目錄是否存在
fs.exists("33",exists=>{
    console.log(exists);
});
//同步判斷檔案或目錄是否存在
let flag = fs.existsSync("33");
console.log(flag);

6.11獲取檔案或目錄的詳細資訊

//獲取檔案或目錄的詳細資訊
fs.stat("33",(err,stats)=>{
    if(err){
        return console.log(err);
    }
    console.log(stats);
    //通過stas.isFile()判斷是否是一個檔案
    console.log(stats.isFile());
    //通過stats.isDirectory()判斷是否是一個目錄
    console.log(stats.isDirectory());  
});

結果:

6.12刪除非空資料夾

  • 先獲取路徑下所有資料夾和檔案;
  • 再通過stat.isDirectory()判斷時資料夾時繼續找下面的檔案,是檔案就刪除;
  • 最後迴圈完成後刪除空資料夾
//自定義刪除非空資料夾
function removeDir(dir) {
    let data = fs.readdirSync(dir);
    data.forEach(item=>{
        //注意這裡的路徑迴圈出來的是原來目錄的子級,所以刪除時,需要加上原來目錄級別才能找到
        let url = dir + "/" + item;
        let stats = fs.statSync(url);
        if(stats.isDirectory()){
            //如果是資料夾繼續向下查詢
            removeDir(url);
        }else{
            //如果是檔案就刪除檔案
            console.log("是檔案");

            fs.unlinkSync(url);
        }
    });
    //迴圈完後刪除空目錄
    fs.rmdirSync(dir);
}
removeDir("33");

7.buffer緩衝區

  • - buffer的建立
  • - 直接建立
  • - 陣列建立
  • - 字串建立
  • - 亂碼的處理
  • - buffer轉換tostring

7.1buffer的建立:

buffer是以二進位制的形式進行儲存,但是是以十六進位制的方式進行展示

buffer建立方式一:ES6.1之前:new Buffer()

//ES6.1之前buffer緩衝區建立
let buffer = new Buffer("大家好");
console.log(buffer);//<Buffer e5 a4 a7 e5 ae b6 e5 a5 bd>

buffer建立方式二:ES6.1: let buffer = Buffer.alloc(10);

//buffer緩衝區建立方式二:
let buffer = Buffer.alloc(10);//建立一個10位元組的buffer緩衝區
console.log(buffer);//<Buffer 00 00 00 00 00 00 00 00 00 00>

buffer建立方式三:Buffer.from()

//buffer緩衝區建立方式三:
let buffer = Buffer.from("大家好");
console.log(buffer);//<Buffer e5 a4 a7 e5 ae b6 e5 a5 bd>

buffer建立方式四:陣列方式建立

//buffer緩衝區建立方式四:
let buffer = Buffer.from([0xe5, 0xa4, 0xa7, 0xe5, 0xae, 0xb6, 0xe5, 0xa5, 0xbd]);
console.log(buffer.toString());//大家好

通過陣列方式建立,一箇中文對應3個16進位制位,如果給的位數不對就會亂碼: 

//通過陣列方式建立,一箇中文對應3個16進位制位,如果給的位數不對就會亂碼
let buffer1 = Buffer.from([0xe5, 0xa4, 0xa7, 0xe5]);
let buffer2 = Buffer.from([ 0xae, 0xb6, 0xe5, 0xa5, 0xbd]);
console.log(buffer1.toString());//大�
console.log(buffer2.toString());//��好

亂碼解決方法一:使用concat([buffer1,buffer2])連線多個buffer(注意引數必須是陣列),並呼叫toString()再輸出

//解決:使用concat連線多個buffer(注意引數必須是陣列),再輸出
console.log(Buffer.concat([buffer1,buffer2]).toString());

亂碼解決方法二(效能更好):引入string_decoder模組,再通過其write()方法

// 亂碼解決方法二:
let { StringDecoder } = require("string_decoder");
let decoder = new StringDecoder();
let buf1 = decoder.write(buffer1);
let buf2 = decoder.write(buffer2);
console.log(buf1);//大
console.log(buf2);//家好

8.stream流

  • - stream流:流與資料處理方面密不可分
  • - 流的原理
  • - 流資料的獲取 fs.createReadStream()
  • - pipe:管道,將得到的資料通過pipe進行寫入(fs.createWriteStream())
  • - data:監控得到的資料,並將其分成多份
  • - end:監控檔案是否讀取完畢
  • - copy的流方法實現
  • - 載入檢視的流方法實現

資料傳輸時,如果資料過大,頻寬不足,會造成記憶體溢位,或者叫記憶體爆倉。

stream流會將資料,分割成多份,然後依次進行傳遞。

檔案上傳讀取等多會涉及到stream流。

8.1data:監控得到的資料,並將其分成多份

示例:

讀取home.js檔案中的內容:

home.js:

// console.log("這是home主頁JS檔案");
// require("./moduleA.js");//引入moduleA.js檔案
// require("./mydir");//引入資料夾mydir,就會自動查詢資料夾mydir下的index.js檔案
// require("mytest");

stream.js讀取檔案:

方式一:使用fs.readFileSync("home.js");讀取時發現是一次性進行讀取,一旦檔案內容過大,就會造成記憶體溢位。

//讀取檔案home.js的資料
const fs = require("fs");
let data = fs.readFileSync("home.js");
console.log(data);//<Buffer 0d 0a 2f 2f 20 63 6f 6e 73 6f 6c 65 2e 6c 6f 67 28 22 e8 bf 99 e6 98 af 68 6f 6d 65 e4 b8 bb e9 a1 b5 4a 53 e6 96 87 e4 bb b6 22 29 3b 0d 0a 2f 2f 20 ... >

方式二:fs.createReadStream("home.js"),通過on方法監聽這個資料,並分成多份

//使用可讀流進行讀取
let re = fs.createReadStream("home.js");
re.on("data",chunk=>{
    console.log(chunk.toString());
});

建立一個65kb的檔案,再通過createReadStream()進行讀取,發現會列印兩次chunk

//建立一個65kb大小的buffer,再通過createReadStream()讀取
let buffer = Buffer.alloc(65*1024);
fs.writeFile("65kb.txt",buffer,err=>{
    if(err){
        return console.log(err);
    }
});
let res = fs.createReadStream("65kb.txt");
let num = 0;
res.on("data",chunk=>{
    num++;
    console.log(chunk);//發現會列印兩次
    console.log(num);
    
    /*
        <Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... >
        1
        <Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... >
        2
    */
});

當建立64kb的檔案,通過createReadStream()進行讀取,發現只會列印一次:

//再讀取64kb檔案時,發現只會列印一次
let res = fs.createReadStream("64kb.txt");
let num = 0;
let str = "";
res.on("data",chunk=>{
    num++;
    str += chunk;
    console.log(chunk);//發現會列印兩次
    console.log(num);
    /*
        <Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... >
        1
    */
});

8.2 end監控檔案是否讀取完成

//再讀取64kb檔案時,發現只會列印一次
let res = fs.createReadStream("home.js");
let num = 0;
let str = "";
res.on("data",chunk=>{
    num++;
    str += chunk;
    console.log(chunk);//發現會列印兩次
    console.log(num);
    /*
        <Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... >
        1
    */
});

//end監控檔案是否讀取完成,並可以得到讀取完成後的資料
res.on("end",()=>{
    console.log(str);
    
});

結果:會將讀取到的所有資料進行列印

8.3pipe:管道,將得到的資料通過pipe進行寫入(fs.createWriteStream())

  • 通過fs.createWriteStream()建立寫入檔案;
  • 通過res.pipe() 流方式通過管道一點一點寫入到檔案中
let res = fs.createReadStream("home.js");
let num = 0;
let str = "";
res.on("data",chunk=>{
    num++;
    str += chunk;
    console.log(chunk);//發現會列印兩次
    console.log(num);
    /*
        <Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ... >
        1
    */
});

//pipe管道,將讀到的資料,通過pipe()寫入到檔案
let pipeTxt = fs.createWriteStream("pipe.txt");
res.pipe(pipeTxt);

9.包管理工具yarn 

npm install -g yarn

yarn會相對簡單,但是最好使用npm

10.node.js版本管理工具NVM

- 使用NVM(Node Version Manager)控制Node.js版本

10.1NVM下載

  • - nvm是mac環境下管理nodejs的工具。在windows環境下推薦使用nvmw或者nvm-windows(建議使用);Nvm-windows 下載地址 https://github.com/coreybutler/nvm-windows
  • 也可以在https://github.com/coreybutler/nvm-windows/releases 下載 nvm-setup.zip

10.2安裝NVM

mac上安裝NVM

  • - 在安裝nvm之前需要一個c++編譯器,在mac上可以安裝Xcode命令工具(已經安裝可以忽略) :`xcode-select --install`
  • - 使用 curl安裝:`curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash`
  • - 或者使用wget來安裝:`wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.34.0/install.sh | bash`

- NVM [github的地址](<https://github.com/creationix/nvm>)可以檢視最新版本

windows上直接上一步下一步;

10.3NVM常用指令

  • nvm --version檢視版本`
  • nvm install stable //安裝最新穩定版nodejs``
  • nvm install 8.11.1 //安裝指定版本``
  • nvm install 8.11 //安裝 8.11.x系列最新版本``
  • nvm ls-remote //列出遠端伺服器上所有可用的版本`` nvm use 8.11.1 //切換到8.11.1版本``
  • nvm use 8.11 //切換到8.11.x最新版本``
  • nvm use node //切換到最新版本``
  • nvm alias default node //設定預設版本為最新版本``
  • nvm ls //列出所有已經安裝的版本``

11.通過fs模組載入頁面

通過路由地址/index 和/product路由到不同的頁面

11.1- 普通方式載入頁面

  • - 路由區分
  • - 載入頁面

index.html:

<body>
    這是主頁........
</body>

product.html:

<body>
    這是產品頁........ 
</body>

style.css:

body {
    background: red;
}

index.js:建立伺服器,並使用fs模組載入不同頁面,並使用nodemon index.js啟動伺服器,即可熱更新,自動監控頁面的變化

頁面由頁面內容和請求頭,響應頭組成,頭資訊必不可少。

//使用fs模組載入頁面
const fs = require("fs");
const http = require("http");
const url = require("url");
const path = require("path");
//引入靜態檔案字尾json檔案
const mime = require("./mime.json");

let server = http.createServer((req, res) => {
    //如果不設定HTML請求頭會亂碼(注意引數的寫法不能錯)
    // res.setHeader('Content-Type', 'text/html; charset=utf-8');
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
        if (req.url === "/index") {
            // res.write("這是主頁");
            //方法一:通過fs模組讀取頁面,再進行寫入內容
            let index = fs.readFileSync("index.html");
            res.write(index);
            // 監控響應結束,不寫的話會一直 請求
            res.end();
        } else if (req.url === "/product") {
            // res.write("這是產品頁");
            //通過fs模組讀取頁面,再進行寫入內容
            let product = fs.readFileSync("product.html");
            res.write(product);
            res.end();

        } else {
            //其他靜態檔案(檔案的字尾不統一),使用mime.json檔案處理
            //通過path模組獲取到檔案字尾名 /.css => .css
            let extname = path.extname(req.url);
            //注意這裡必須設定響應頭,否則沒有響應頭頁面不會顯示(注意拼接形式)
            res.writeHead(200, { 'Content-Type': mime[extname]});
            //再讀取到靜態檔案,並響應到頁面
            let static = fs.createReadStream("."+req.url);
            static.pipe(res);
        }

});
server.listen("8000");

mime.json:各種靜態檔案(檔案的字尾不統一),操作複雜,可以使用mime.json檔案處理。

如,通過path模組獲取到檔案字尾名 /.css => .css , let extname = path.extname(req.url) ;然後必須設定響應頭,否則沒有響應頭頁面不會顯示(注意拼接形式)  res.writeHead(200, { 'Content-Type': mime[extname]});

{ ".323":"text/h323" ,
  ".3gp":"video/3gpp" ,
  ".aab":"application/x-authoware-bin" ,
  ".aam":"application/x-authoware-map" ,
  ".aas":"application/x-authoware-seg" ,
  ".acx":"application/internet-property-stream" ,
  ".ai":"application/postscript" ,
  ".aif":"audio/x-aiff" ,
  ".aifc":"audio/x-aiff" ,
  ".aiff":"audio/x-aiff" ,
  ".als":"audio/X-Alpha5" ,
  ".amc":"application/x-mpeg" ,
  ".ani":"application/octet-stream" ,
  ".apk":"application/vnd.android.package-archive" ,
  ".asc":"text/plain" ,
  ".asd":"application/astound" ,
  ".asf":"video/x-ms-asf" ,
  ".asn":"application/astound" ,
  ".asp":"application/x-asap" ,
  ".asr":"video/x-ms-asf" ,
  ".asx":"video/x-ms-asf" ,
  ".au":"audio/basic" ,
  ".avb":"application/octet-stream" ,
  ".avi":"video/x-msvideo" ,
  ".awb":"audio/amr-wb" ,
  ".axs":"application/olescript" ,
  ".bas":"text/plain" ,
  ".bcpio":"application/x-bcpio" ,
  ".bin ":"application/octet-stream" ,
  ".bld":"application/bld" ,
  ".bld2":"application/bld2" ,
  ".bmp":"image/bmp" ,
  ".bpk":"application/octet-stream" ,
  ".bz2":"application/x-bzip2" ,
  ".c":"text/plain" ,
  ".cal":"image/x-cals" ,
  ".cat":"application/vnd.ms-pkiseccat" ,
  ".ccn":"application/x-cnc" ,
  ".cco":"application/x-cocoa" ,
  ".cdf":"application/x-cdf" ,
  ".cer":"application/x-x509-ca-cert" ,
  ".cgi":"magnus-internal/cgi" ,
  ".chat":"application/x-chat" ,
  ".class":"application/octet-stream" ,
  ".clp":"application/x-msclip" ,
  ".cmx":"image/x-cmx" ,
  ".co":"application/x-cult3d-object" ,
  ".cod":"image/cis-cod" ,
  ".conf":"text/plain" ,
  ".cpio":"application/x-cpio" ,
  ".cpp":"text/plain" ,
  ".cpt":"application/mac-compactpro" ,
  ".crd":"application/x-mscardfile" ,
  ".crl":"application/pkix-crl" ,
  ".crt":"application/x-x509-ca-cert" ,
  ".csh":"application/x-csh" ,
  ".csm":"chemical/x-csml" ,
  ".csml":"chemical/x-csml" ,
  ".css":"text/css" ,
  ".cur":"application/octet-stream" ,
  ".dcm":"x-lml/x-evm" ,
  ".dcr":"application/x-director" ,
  ".dcx":"image/x-dcx" ,
  ".der":"application/x-x509-ca-cert" ,
  ".dhtml":"text/html" ,
  ".dir":"application/x-director" ,
  ".dll":"application/x-msdownload" ,
  ".dmg":"application/octet-stream" ,
  ".dms":"application/octet-stream" ,
  ".doc":"application/msword" ,
  ".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document" ,
  ".dot":"application/msword" ,
  ".dvi":"application/x-dvi" ,
  ".dwf":"drawing/x-dwf" ,
  ".dwg":"application/x-autocad" ,
  ".dxf":"application/x-autocad" ,
  ".dxr":"application/x-director" ,
  ".ebk":"application/x-expandedbook" ,
  ".emb":"chemical/x-embl-dl-nucleotide" ,
  ".embl":"chemical/x-embl-dl-nucleotide" ,
  ".eps":"application/postscript" ,
  ".epub":"application/epub+zip" ,
  ".eri":"image/x-eri" ,
  ".es":"audio/echospeech" ,
  ".esl":"audio/echospeech" ,
  ".etc":"application/x-earthtime" ,
  ".etx":"text/x-setext" ,
  ".evm":"x-lml/x-evm" ,
  ".evy":"application/envoy" ,
  ".exe":"application/octet-stream" ,
  ".fh4":"image/x-freehand" ,
  ".fh5":"image/x-freehand" ,
  ".fhc":"image/x-freehand" ,
  ".fif":"application/fractals" ,
  ".flr":"x-world/x-vrml" ,
  ".flv":"flv-application/octet-stream" ,
  ".fm":"application/x-maker" ,
  ".fpx":"image/x-fpx" ,
  ".fvi":"video/isivideo" ,
  ".gau":"chemical/x-gaussian-input" ,
  ".gca":"application/x-gca-compressed" ,
  ".gdb":"x-lml/x-gdb" ,
  ".gif":"image/gif" ,
  ".gps":"application/x-gps" ,
  ".gtar":"application/x-gtar" ,
  ".gz":"application/x-gzip" ,
  ".h":"text/plain" ,
  ".hdf":"application/x-hdf" ,
  ".hdm":"text/x-hdml" ,
  ".hdml":"text/x-hdml" ,
  ".hlp":"application/winhlp" ,
  ".hqx":"application/mac-binhex40" ,
  ".hta":"application/hta" ,
  ".htc":"text/x-component" ,
  ".htm":"text/html" ,
  ".html":"text/html" ,
  ".hts":"text/html" ,
  ".htt":"text/webviewhtml" ,
  ".ice":"x-conference/x-cooltalk" ,
  ".ico":"image/x-icon" ,
  ".ief":"image/ief" ,
  ".ifm":"image/gif" ,
  ".ifs":"image/ifs" ,
  ".iii":"application/x-iphone" ,
  ".imy":"audio/melody" ,
  ".ins":"application/x-internet-signup" ,
  ".ips":"application/x-ipscript" ,
  ".ipx":"application/x-ipix" ,
  ".isp":"application/x-internet-signup" ,
  ".it":"audio/x-mod" ,
  ".itz":"audio/x-mod" ,
  ".ivr":"i-world/i-vrml" ,
  ".j2k":"image/j2k" ,
  ".jad":"text/vnd.sun.j2me.app-descriptor" ,
  ".jam":"application/x-jam" ,
  ".jar":"application/java-archive" ,
  ".java":"text/plain" ,
  ".jfif":"image/pipeg" ,
  ".jnlp":"application/x-java-jnlp-file" ,
  ".jpe":"image/jpeg" ,
  ".jpeg":"image/jpeg" ,
  ".jpg":"image/jpeg" ,
  ".jpz":"image/jpeg" ,
  ".js":"application/x-javascript" ,
  ".jwc":"application/jwc" ,
  ".kjx":"application/x-kjx" ,
  ".lak":"x-lml/x-lak" ,
  ".latex":"application/x-latex" ,
  ".lcc":"application/fastman" ,
  ".lcl":"application/x-digitalloca" ,
  ".lcr":"application/x-digitalloca" ,
  ".lgh":"application/lgh" ,
  ".lha":"application/octet-stream" ,
  ".lml":"x-lml/x-lml" ,
  ".lmlpack":"x-lml/x-lmlpack" ,
  ".log":"text/plain" ,
  ".lsf":"video/x-la-asf" ,
  ".lsx":"video/x-la-asf" ,
  ".lzh":"application/octet-stream" ,
  ".m13":"application/x-msmediaview" ,
  ".m14":"application/x-msmediaview" ,
  ".m15":"audio/x-mod" ,
  ".m3u":"audio/x-mpegurl" ,
  ".m3url":"audio/x-mpegurl" ,
  ".m4a":"audio/mp4a-latm" ,
  ".m4b":"audio/mp4a-latm" ,
  ".m4p":"audio/mp4a-latm" ,
  ".m4u":"video/vnd.mpegurl" ,
  ".m4v":"video/x-m4v" ,
  ".ma1":"audio/ma1" ,
  ".ma2":"audio/ma2" ,
  ".ma3":"audio/ma3" ,
  ".ma5":"audio/ma5" ,
  ".man":"application/x-troff-man" ,
  ".map":"magnus-internal/imagemap" ,
  ".mbd":"application/mbedlet" ,
  ".mct":"application/x-mascot" ,
  ".mdb":"application/x-msaccess" ,
  ".mdz":"audio/x-mod" ,
  ".me":"application/x-troff-me" ,
  ".mel":"text/x-vmel" ,
  ".mht":"message/rfc822" ,
  ".mhtml":"message/rfc822" ,
  ".mi":"application/x-mif" ,
  ".mid":"audio/mid" ,
  ".midi":"audio/midi" ,
  ".mif":"application/x-mif" ,
  ".mil":"image/x-cals" ,
  ".mio":"audio/x-mio" ,
  ".mmf":"application/x-skt-lbs" ,
  ".mng":"video/x-mng" ,
  ".mny":"application/x-msmoney" ,
  ".moc":"application/x-mocha" ,
  ".mocha":"application/x-mocha" ,
  ".mod":"audio/x-mod" ,
  ".mof":"application/x-yumekara" ,
  ".mol":"chemical/x-mdl-molfile" ,
  ".mop":"chemical/x-mopac-input" ,
  ".mov":"video/quicktime" ,
  ".movie":"video/x-sgi-movie" ,
  ".mp2":"video/mpeg" ,
  ".mp3":"audio/mpeg" ,
  ".mp4":"video/mp4" ,
  ".mpa":"video/mpeg" ,
  ".mpc":"application/vnd.mpohun.certificate" ,
  ".mpe":"video/mpeg" ,
  ".mpeg":"video/mpeg" ,
  ".mpg":"video/mpeg" ,
  ".mpg4":"video/mp4" ,
  ".mpga":"audio/mpeg" ,
  ".mpn":"application/vnd.mophun.application" ,
  ".mpp":"application/vnd.ms-project" ,
  ".mps":"application/x-mapserver" ,
  ".mpv2":"video/mpeg" ,
  ".mrl":"text/x-mrml" ,
  ".mrm":"application/x-mrm" ,
  ".ms":"application/x-troff-ms" ,
  ".msg":"application/vnd.ms-outlook" ,
  ".mts":"application/metastream" ,
  ".mtx":"application/metastream" ,
  ".mtz":"application/metastream" ,
  ".mvb":"application/x-msmediaview" ,
  ".mzv":"application/metastream" ,
  ".nar":"application/zip" ,
  ".nbmp":"image/nbmp" ,
  ".nc":"application/x-netcdf" ,
  ".ndb":"x-lml/x-ndb" ,
  ".ndwn":"application/ndwn" ,
  ".nif":"application/x-nif" ,
  ".nmz":"application/x-scream" ,
  ".nokia-op-logo":"image/vnd.nok-oplogo-color" ,
  ".npx":"application/x-netfpx" ,
  ".nsnd":"audio/nsnd" ,
  ".nva":"application/x-neva1" ,
  ".nws":"message/rfc822" ,
  ".oda":"application/oda" ,
  ".ogg":"audio/ogg" ,
  ".oom":"application/x-AtlasMate-Plugin" ,
  ".p10":"application/pkcs10" ,
  ".p12":"application/x-pkcs12" ,
  ".p7b":"application/x-pkcs7-certificates" ,
  ".p7c":"application/x-pkcs7-mime" ,
  ".p7m":"application/x-pkcs7-mime" ,
  ".p7r":"application/x-pkcs7-certreqresp" ,
  ".p7s":"application/x-pkcs7-signature" ,
  ".pac":"audio/x-pac" ,
  ".pae":"audio/x-epac" ,
  ".pan":"application/x-pan" ,
  ".pbm":"image/x-portable-bitmap" ,
  ".pcx":"image/x-pcx" ,
  ".pda":"image/x-pda" ,
  ".pdb":"chemical/x-pdb" ,
  ".pdf":"application/pdf" ,
  ".pfr":"application/font-tdpfr" ,
  ".pfx":"application/x-pkcs12" ,
  ".pgm":"image/x-portable-graymap" ,
  ".pict":"image/x-pict" ,
  ".pko":"application/ynd.ms-pkipko" ,
  ".pm":"application/x-perl" ,
  ".pma":"application/x-perfmon" ,
  ".pmc":"application/x-perfmon" ,
  ".pmd":"application/x-pmd" ,
  ".pml":"application/x-perfmon" ,
  ".pmr":"application/x-perfmon" ,
  ".pmw":"application/x-perfmon" ,
  ".png":"image/png" ,
  ".pnm":"image/x-portable-anymap" ,
  ".pnz":"image/png" ,
  ".pot,":"application/vnd.ms-powerpoint" ,
  ".ppm":"image/x-portable-pixmap" ,
  ".pps":"application/vnd.ms-powerpoint" ,
  ".ppt":"application/vnd.ms-powerpoint" ,
  ".pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation" ,
  ".pqf":"application/x-cprplayer" ,
  ".pqi":"application/cprplayer" ,
  ".prc":"application/x-prc" ,
  ".prf":"application/pics-rules" ,
  ".prop":"text/plain" ,
  ".proxy":"application/x-ns-proxy-autoconfig" ,
  ".ps":"application/postscript" ,
  ".ptlk":"application/listenup" ,
  ".pub":"application/x-mspublisher" ,
  ".pvx":"video/x-pv-pvx" ,
  ".qcp":"audio/vnd.qcelp" ,
  ".qt":"video/quicktime" ,
  ".qti":"image/x-quicktime" ,
  ".qtif":"image/x-quicktime" ,
  ".r3t":"text/vnd.rn-realtext3d" ,
  ".ra":"audio/x-pn-realaudio" ,
  ".ram":"audio/x-pn-realaudio" ,
  ".rar":"application/octet-stream" ,
  ".ras":"image/x-cmu-raster" ,
  ".rc":"text/plain" ,
  ".rdf":"application/rdf+xml" ,
  ".rf":"image/vnd.rn-realflash" ,
  ".rgb":"image/x-rgb" ,
  ".rlf":"application/x-richlink" ,
  ".rm":"audio/x-pn-realaudio" ,
  ".rmf":"audio/x-rmf" ,
  ".rmi":"audio/mid" ,
  ".rmm":"audio/x-pn-realaudio" ,
  ".rmvb":"audio/x-pn-realaudio" ,
  ".rnx":"application/vnd.rn-realplayer" ,
  ".roff":"application/x-troff" ,
  ".rp":"image/vnd.rn-realpix" ,
  ".rpm":"audio/x-pn-realaudio-plugin" ,
  ".rt":"text/vnd.rn-realtext" ,
  ".rte":"x-lml/x-gps" ,
  ".rtf":"application/rtf" ,
  ".rtg":"application/metastream" ,
  ".rtx":"text/richtext" ,
  ".rv":"video/vnd.rn-realvideo" ,
  ".rwc":"application/x-rogerwilco" ,
  ".s3m":"audio/x-mod" ,
  ".s3z":"audio/x-mod" ,
  ".sca":"application/x-supercard" ,
  ".scd":"application/x-msschedule" ,
  ".sct":"text/scriptlet" ,
  ".sdf":"application/e-score" ,
  ".sea":"application/x-stuffit" ,
  ".setpay":"application/set-payment-initiation" ,
  ".setreg":"application/set-registration-initiation" ,
  ".sgm":"text/x-sgml" ,
  ".sgml":"text/x-sgml" ,
  ".sh":"application/x-sh" ,
  ".shar":"application/x-shar" ,
  ".shtml":"magnus-internal/parsed-html" ,
  ".shw":"application/presentations" ,
  ".si6":"image/si6" ,
  ".si7":"image/vnd.stiwap.sis" ,
  ".si9":"image/vnd.lgtwap.sis" ,
  ".sis":"application/vnd.symbian.install" ,
  ".sit":"application/x-stuffit" ,
  ".skd":"application/x-Koan" ,
  ".skm":"application/x-Koan" ,
  ".skp":"application/x-Koan" ,
  ".skt":"application/x-Koan" ,
  ".slc":"application/x-salsa" ,
  ".smd":"audio/x-smd" ,
  ".smi":"application/smil" ,
  ".smil":"application/smil" ,
  ".smp":"application/studiom" ,
  ".smz":"audio/x-smd" ,
  ".snd":"audio/basic" ,
  ".spc":"application/x-pkcs7-certificates" ,
  ".spl":"application/futuresplash" ,
  ".spr":"application/x-sprite" ,
  ".sprite":"application/x-sprite" ,
  ".sdp":"application/sdp" ,
  ".spt":"application/x-spt" ,
  ".src":"application/x-wais-source" ,
  ".sst":"application/vnd.ms-pkicertstore" ,
  ".stk":"application/hyperstudio" ,
  ".stl":"application/vnd.ms-pkistl" ,
  ".stm":"text/html" ,
  ".svg":"image/svg+xml" ,
  ".sv4cpio":"application/x-sv4cpio" ,
  ".sv4crc":"application/x-sv4crc" ,
  ".svf":"image/vnd" ,
  ".svg":"image/svg+xml" ,
  ".svh":"image/svh" ,
  ".svr":"x-world/x-svr" ,
  ".swf":"application/x-shockwave-flash" ,
  ".swfl":"application/x-shockwave-flash" ,
  ".t":"application/x-troff" ,
  ".tad":"application/octet-stream" ,
  ".talk":"text/x-speech" ,
  ".tar":"application/x-tar" ,
  ".taz":"application/x-tar" ,
  ".tbp":"application/x-timbuktu" ,
  ".tbt":"application/x-timbuktu" ,
  ".tcl":"application/x-tcl" ,
  ".tex":"application/x-tex" ,
  ".texi":"application/x-texinfo" ,
  ".texinfo":"application/x-texinfo" ,
  ".tgz":"application/x-compressed" ,
  ".thm":"application/vnd.eri.thm" ,
  ".tif":"image/tiff" ,
  ".tiff":"image/tiff" ,
  ".tki":"application/x-tkined" ,
  ".tkined":"application/x-tkined" ,
  ".toc":"application/toc" ,
  ".toy":"image/toy" ,
  ".tr":"application/x-troff" ,
  ".trk":"x-lml/x-gps" ,
  ".trm":"application/x-msterminal" ,
  ".tsi":"audio/tsplayer" ,
  ".tsp":"application/dsptype" ,
  ".tsv":"text/tab-separated-values" ,
  ".ttf":"application/octet-stream" ,
  ".ttz":"application/t-time" ,
  ".txt":"text/plain" ,
  ".uls":"text/iuls" ,
  ".ult":"audio/x-mod" ,
  ".ustar":"application/x-ustar" ,
  ".uu":"application/x-uuencode" ,
  ".uue":"application/x-uuencode" ,
  ".vcd":"application/x-cdlink" ,
  ".vcf":"text/x-vcard" ,
  ".vdo":"video/vdo" ,
  ".vib":"audio/vib" ,
  ".viv":"video/vivo" ,
  ".vivo":"video/vivo" ,
  ".vmd":"application/vocaltec-media-desc" ,
  ".vmf":"application/vocaltec-media-file" ,
  ".vmi":"application/x-dreamcast-vms-info" ,
  ".vms":"application/x-dreamcast-vms" ,
  ".vox":"audio/voxware" ,
  ".vqe":"audio/x-twinvq-plugin" ,
  ".vqf":"audio/x-twinvq" ,
  ".vql":"audio/x-twinvq" ,
  ".vre":"x-world/x-vream" ,
  ".vrml":"x-world/x-vrml" ,
  ".vrt":"x-world/x-vrt" ,
  ".vrw":"x-world/x-vream" ,
  ".vts":"workbook/formulaone" ,
  ".wav":"audio/x-wav" ,
  ".wax":"audio/x-ms-wax" ,
  ".wbmp":"image/vnd.wap.wbmp" ,
  ".wcm":"application/vnd.ms-works" ,
  ".wdb":"application/vnd.ms-works" ,
  ".web":"application/vnd.xara" ,
  ".wi":"image/wavelet" ,
  ".wis":"application/x-InstallShield" ,
  ".wks":"application/vnd.ms-works" ,
  ".wm":"video/x-ms-wm" ,
  ".wma":"audio/x-ms-wma" ,
  ".wmd":"application/x-ms-wmd" ,
  ".wmf":"application/x-msmetafile" ,
  ".wml":"text/vnd.wap.wml" ,
  ".wmlc":"application/vnd.wap.wmlc" ,
  ".wmls":"text/vnd.wap.wmlscript" ,
  ".wmlsc":"application/vnd.wap.wmlscriptc" ,
  ".wmlscript":"text/vnd.wap.wmlscript" ,
  ".wmv":"audio/x-ms-wmv" ,
  ".wmx":"video/x-ms-wmx" ,
  ".wmz":"application/x-ms-wmz" ,
  ".wpng":"image/x-up-wpng" ,
  ".wps":"application/vnd.ms-works" ,
  ".wpt":"x-lml/x-gps" ,
  ".wri":"application/x-mswrite" ,
  ".wrl":"x-world/x-vrml" ,
  ".wrz":"x-world/x-vrml" ,
  ".ws":"text/vnd.wap.wmlscript" ,
  ".wsc":"application/vnd.wap.wmlscriptc" ,
  ".wv":"video/wavelet" ,
  ".wvx":"video/x-ms-wvx" ,
  ".wxl":"application/x-wxl" ,
  ".x-gzip":"application/x-gzip" ,
  ".xaf":"x-world/x-vrml" ,
  ".xar":"application/vnd.xara" ,
  ".xbm":"image/x-xbitmap" ,
  ".xdm":"application/x-xdma" ,
  ".xdma":"application/x-xdma" ,
  ".xdw":"application/vnd.fujixerox.docuworks" ,
  ".xht":"application/xhtml+xml" ,
  ".xhtm":"application/xhtml+xml" ,
  ".xhtml":"application/xhtml+xml" ,
  ".xla":"application/vnd.ms-excel" ,
  ".xlc":"application/vnd.ms-excel" ,
  ".xll":"application/x-excel" ,
  ".xlm":"application/vnd.ms-excel" ,
  ".xls":"application/vnd.ms-excel" ,
  ".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ,
  ".xlt":"application/vnd.ms-excel" ,
  ".xlw":"application/vnd.ms-excel" ,
  ".xm":"audio/x-mod" ,
  ".xml":"text/plain",
  ".xml":"application/xml",
  ".xmz":"audio/x-mod" ,
  ".xof":"x-world/x-vrml" ,
  ".xpi":"application/x-xpinstall" ,
  ".xpm":"image/x-xpixmap" ,
  ".xsit":"text/xml" ,
  ".xsl":"text/xml" ,
  ".xul":"text/xul" ,
  ".xwd":"image/x-xwindowdump" ,
  ".xyz":"chemical/x-pdb" ,
  ".yz1":"application/x-yz1" ,
  ".z":"application/x-compress" ,
  ".zac":"application/x-zaurus-zac" ,
  ".zip":"application/zip" ,
  ".json":"application/json"
}

11.2- 通過stream流方式載入頁面

  • - 路由區分
  • - 載入頁面
  • - 設定頭部:mime.json
  • - 載入第三方資源

通過fs模組流 Stream讀取檔案:在檔案內容很大時一點一點讀取效率高,不會記憶體溢位。

注意點:

  1. fs.createReadStream()方法讀取後,直接通過pipe()方法響應到頁面,不需要再寫入(fs.createWriteStream())
  2. 注意HTML頭的書寫格式:res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  3. 靜態檔案字尾,通過path.extname(req.url)進行獲取,且對應json中不同的檔案字尾
//使用fs模組載入頁面
const fs = require("fs");
const http = require("http");
const url = require("url");
const path = require("path");
//引入靜態檔案字尾json檔案
const mime = require("./mime.json");

let server = http.createServer((req, res) => {
    //如果不設定HTML請求頭會亂碼(注意引數的寫法不能錯)
    // res.setHeader('Content-Type', 'text/html; charset=utf-8');
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
        if (req.url === "/index") {
            //方法二:通過fs模組流 Stream讀取檔案:在檔案內容很大時效率高,不會記憶體溢位
            let resIndex = fs.createReadStream("index.html");
            //pipe管道,將讀到的資料響應到頁面
            resIndex.pipe(res);

        } else if (req.url === "/product") {
            //注意createReadStream路由只需要讀取並響應,不需要寫入
            let product = fs.createReadStream("product.html");
            product.pipe(res);

        } else {
            //其他靜態檔案(檔案的字尾不統一),使用mime.json檔案處理
            //通過path模組獲取到檔案字尾名 /.css => .css
            let extname = path.extname(req.url);
            //注意這裡必須設定響應頭,否則沒有響應頭頁面不會顯示(注意拼接形式)
            res.writeHead(200, { 'Content-Type': mime[extname]});
            //再讀取到靜態檔案,並響應到頁面
            let static = fs.createReadStream("."+req.url);
            static.pipe(res);
        }

});
server.listen("8000");

12.node+cheerio實現爬蟲獲取資料

  • - http://news.ifeng.com/ 新聞地址
  • - http.get()使用
  • - cheerio來處理資料:
  • - 檔案方式儲存資料1

cheerio:類似於前端jQuery的node.js的模組,用法基本和jQuery相同,但是沒有window物件,即location,history,location等前端物件不存在。

crawlerData.js:

const http = require("http");
//通過get方法獲取爬蟲爬到的是整個頁面,需要使用cheerio模組進行處理(類似JQuery)
const cheerio = require("cheerio");//注意需要使用 npm i cheerio -S 按照cheerio模組到執行環境

const fs = require("fs");

let newsData = '';
//http的get 方法會並自動呼叫 req.end()
http.get("http://news.ifeng.com/", (res) => {
    res.on("data", chunk => {
        newsData += chunk;
    });
    res.on("end", () => {

        //資料必須在end裡面處理,否則獲取不到資料
         //使用cheerio載入整個資料
        let $ = cheerio.load(newsData);

        //用於存json資料
        let data = [];
        
        //新聞標題
        let titles = $(".news-stream-newsStream-news-item-infor h2 a");
        titles.each((index,ele)=>{
            data.push({
                "id":index+1,
                "title":ele.attribs['title']
            });
        });
        
        //釋出者
        let publisher = $(".news-stream-newsStream-news-item-infor .clearfix span");
        publisher.each((index,ele)=>{
            data[index].publisher = ele.children[0]['data'];
        });
        
        //釋出時間
        let time = $(".news-stream-newsStream-news-item-infor .clearfix time");
        time.each((index,ele)=>{
            data[index].time = ele.children[0]['data'];
        });

        //將得到的資料寫入到json檔案中
        let dataJson = fs.createWriteStream("data.json");
        //注意需要使用write(),並且將資料轉成字串格式才能進行寫出
        dataJson.write(JSON.stringify(data));
        res.pipe(dataJson);
    });
});

獲取到的json資料檔案:檔案已做一定修改

[
    {
        "id": 1,
        "title": "13歲少年成社會的災難",
        "publisher": "海外網",
        "time": "今天 17:08"
    },
    {
        "id": 2,
        "title": "(全文實錄)",
        "publisher": "中國網",
        "time": "今天 16:59"
    },
    {
        "id": 3,
        "title": "禁讀《哈利·波特》 稱咒語召喚邪靈",
        "publisher": "澎湃新聞",
        "time": "今天 16:56"
    },
    {
        "id": 4,
        "title": "XXXXXXXXXXX",
        "publisher": "海外網",
        "time": "今天 16:54"
    },
    {
        "id": 5,
        "title": "空缺190天后,迎來王浩",
        "publisher": "上游新聞",
        "time": "今天 16:54"
    },
    {
        "id": 6,
        "title": "中國遊客在日本突然昏迷 2名韓國消防員及時相救",
        "publisher": "海外網",
        "time": "今天 16:53"
    },
    {
        "id": 7,
        "title": "零售雪上加霜 奢侈品牌普拉達將關閉在港最大門店",
        "publisher": "觀察者網",
        "time": "今天 16:46"
    },
    {
        "id": 8,
        "title": "拖了18年?",
        "publisher": "海外網",
        "time": "今天 16:41"
    },
    {
        "id": 9,
        "title": "誤讀",
        "publisher": "環球網",
        "time": "今天 16:37"
    },
    {
        "id": 10,
        "title": "被男議員罵“不生孩子沒盡國家責任” 韓國55歲女學者懵了",
        "publisher": "海外網",
        "time": "今天 16:29"
    },
    {
        "id": 11,
        "title": "陳剛被提起公訴",
        "publisher": "海外網",
        "time": "今天 16:29"
    },
    {
        "id": 12,
        "title": "臺假裝尿急翻牆逃出營區 10天后在網咖被抓",
        "publisher": "海外網",
        "time": "今天 16:22"
    },
    {
        "id": 13,
        "title": "出糗:成語連說了3遍都沒對",
        "publisher": "海外網",
        "time": "今天 16:21"
    },
    {
        "id": 14,
        "title": "XXXX",
        "publisher": "澎湃新聞網",
        "time": "今天 16:09"
    },
    {
        "id": 15,
        "title": "XXXX權",
        "publisher": "新京報網",
        "time": "今天 16:00"
    },
    {
        "id": 16,
        "title": "特徵",
        "publisher": "新京報即時新聞",
        "time": "今天 15:53"
    },
    {
        "id": 17,
        "title": "XXXXXX",
        "publisher": "新京報即時新聞",
        "time": "今天 15:46"
    },
    {
        "id": 18,
        "title": "XXXXXX",
        "publisher": "中國網",
        "time": "今天 15:41"
    },
    {
        "id": 19,
        "title": "XXXX",
        "publisher": "新京報即時新聞",
        "time": "今天 15:35"
    },
    {
        "id": 20,
        "title": "XXXXXX",
        "publisher": "新京報即時新聞",
        "time": "今天 15:18"
    },
    {
        "id": 21,
        "title": "商務部:上週豬肉批發價格上漲8.9%",
        "publisher": "新京報即時新聞",
        "time": "今天 15:14"
    },
    {
        "id": 22,
        "title": "出糗:這個成語連說三遍都沒對",
        "publisher": "海外網",
        "time": "今天 14:54"
    },
    {
        "id": 23,
        "title": "兩高:高考等4類考試組織作弊屬犯罪 最高判7年",
        "publisher": "新京報即時新聞",
        "time": "今天 14:33"
    }
]

13.實現新聞列表頁面

  • - 檢視邏輯分離
  • - 讀取頁面
  • - 讀取動態資料
  • - 設定頭部引入其他資源
  • - 詳細頁顯示

index.js:

//將爬蟲得到的資料,渲染到頁面,需要進行頁面路由
const http = require("http");
const fs = require("fs");
const cheerio = require("cheerio");
const url = require("url");//處理地址
const path = require("path");//處理地址字尾名
const mime = require("./mime.json");//處理字尾名檔案
const dataJson = require("./data.json");//新聞資料

let server = http.createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
    if (req.url === "/" || req.url === "/index") {
        //方法一:讀取到index檔案,獲取檔案標籤,設定檔案標籤內容
        let index = fs.readFileSync("index.html");
        let $ = cheerio.load(index);
        //讀取到json檔案新聞資料
        //獲取ul 將HTML設定進去
        let ulHtml = '';
        dataJson.forEach(item => {
            ulHtml += `
                <li class="news">
                    <a href="javascript:;">
                        <img src="./img/img.png" alt="">
                    </a>
                    <div>
                        <h3>
                            <a href="javascript:;">${item.title}</a>
                        </h3>
                        <div class="info">
                            <span class="tips"><span>縱火</span><span${item.publisher}</span><span>逮捕</span></span>
                            <!-- <span class="line"></span> -->
                            <span class="time">| &nbsp;&nbsp;${item.time}</span>
                        </div>
                    </div>
                </li>
             `;
        });
        $(".news-list").html(ulHtml);
        res.end($.html());

        // //方法二:如果要使用createReadStream()流方式讀取檔案,就必須使用on("data")和on("end")監控頁面讀取完後再操作DOM
        // let index = fs.createReadStream("index.html");
        // let oldIndex = '';
        // index.on("data", chunk => {
        //     oldIndex += chunk;
        // });
        // index.on('end', () => {
        //     console.log(oldIndex);
        //     let $ = cheerio.load(oldIndex);
        //     let ulHtml = '';
        //     dataJson.forEach(item => {
        //         ulHtml += `
        //         <li class="news">
        //             <a href="javascript:;">
        //                 <img src="./img/img.png" alt="">
        //             </a>
        //             <div>
        //                 <h3>
        //                     <a href="javascript:;">${item.title}</a>
        //                 </h3>
        //                 <div class="info">
        //                     <span class="tips"><span>縱火</span><span${item.publisher}</span><span>逮捕</span></span>
        //                     <!-- <span class="line"></span> -->
        //                     <span class="time">| &nbsp;&nbsp;${item.time}</span>
        //                 </div>
        //             </div>
        //         </li>
        //      `;
        //     });
        //     $(".news-list").html(ulHtml);
        //     res.end($.html());
        // });

    } else if (req.url !== "/favicon.ico") {
        let extname = path.extname(req.url);
        res.writeHead(200, { 'Content-Type': mime[extname] });
        let static = fs.createReadStream("." + req.url);
        static.pipe(res);
    }

});
server.listen(4000);

index.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>文章資訊展示</title>
    <style>
        body {
            margin: 0;
        }

        ul {
            margin: 0;
            padding: 0;
            list-style: none;
        }

        a {
            text-decoration: none;
            color: #404040;
        }
        .wrap{
            width: 600px;
            margin: 0 auto;
        }

        .news-list {
            width: 600px;
        }

        .news {
            width: 100%;
            display: flex;
            justify-content: space-between;
            padding: 15px 0;
            border-bottom: 1px solid #999;
        }

        .info {
            display: flex;
            width: 170px;
            justify-content: space-between;
            font-size: 12px;
            color: #888;
        }

        .tips {
            display: flex;
            width: 100px;
            justify-content: space-between;
        }

        .news-list li:nth-child(5) {
            border-bottom: none;
        }
        .pagination{
            display: flex;
            width: 210px;
            text-align: center;
            background-color: rgb(252, 238, 238);
            border-radius: 25px;
            overflow: hidden;
            margin: 0 auto;
            justify-content: center;
        }
        .pagination a{
            width: 30px;
            line-height: 30px;
            color: #404040;
        }
        .pagination a:nth-child(1) {
            transform: rotate(-45deg) ;
        }
        .next {
            transform: rotate(45deg) ;
        }
        .pagination a:hover{
            color: rgb(247, 73, 73);
        }
        .news div{
            width:420px;
        }
        .pagination .active {
            color: rgb(247, 73, 73);
        }
    </style>
</head>

<body>
    <div class="wrap">
        <ul class="news-list">
            <li class="news">
                <a href="javascript:;">
                    <img src="./img/img.png" alt="">
                </a>
                <div>
                    <h3>
                        <a href="javascript:;">18人死傷!韓國一男子縱火後持凶器傷害避險鄰居</a>
                    </h3>
                    <div class="info">
                        <span class="tips"><span>縱火</span><span>韓國</span><span>逮捕</span></span>
                        <!-- <span class="line"></span> -->
                        <span class="time">| &nbsp;&nbsp;1小時前</span>
                    </div>
                </div>
            </li>
            <li class="news">
                <a href="javascript:;">
                    <img src="./img/img.png" alt="">
                </a>
                <div>
                    <h3>
                        <a href="javascript:;">18人死傷!韓國一男子縱火後持凶器傷害避險鄰居</a>
                    </h3>
                    <div class="info">
                        <span class="tips"><span>縱火</span><span>韓國</span><span>逮捕</span></span>
                        <!-- <span class="line"></span> -->
                        <span class="time">| &nbsp;&nbsp;1小時前</span>
                    </div>
                </div>
            </li>
            <li class="news">
                <a href="javascript:;">
                    <img src="./img/img.png" alt="">
                </a>
                <div>
                    <h3>
                        <a href="javascript:;">18人死傷!韓國一男子縱火後持凶器傷害避險鄰居</a>
                    </h3>
                    <div class="info">
                        <span class="tips"><span>縱火</span><span>韓國</span><span>逮捕</span></span>
                        <!-- <span class="line"></span> -->
                        <span class="time">| &nbsp;&nbsp;1小時前</span>
                    </div>
                </div>
            </li>
            <li class="news">
                <a href="javascript:;">
                    <img src="./img/img.png" alt="">
                </a>
                <div>
                    <h3>
                        <a href="javascript:;">18人死傷!韓國一男子縱火後持凶器傷害避險鄰居</a>
                    </h3>
                    <div class="info">
                        <span class="tips"><span>縱火</span><span>韓國</span><span>逮捕</span></span>
                        <!-- <span class="line"></span> -->
                        <span class="time">| &nbsp;&nbsp;1小時前</span>
                    </div>
                </div>
            </li>
            <li class="news">
                <a href="javascript:;">
                    <img src="./img/img.png" alt="">
                </a>
                <div>
                    <h3>
                        <a href="javascript:;">18人死傷!韓國一男子縱火後持凶器傷害避險鄰居</a>
                    </h3>
                    <div class="info">
                        <span class="tips"><span>縱火</span><span>韓國</span><span>逮捕</span></span>
                        <!-- <span class="line"></span> -->
                        <span class="time">| &nbsp;&nbsp;1小時前</span>
                    </div>
                </div>
            </li>
        </ul>
        <div class="pagination">
            <a href="javascript:;" class="prev">⌜</a>
            <a href="javascript:;">1</a>
            <a href="javascript:;">2</a>
            <a href="javascript:;">3</a>
            <a href="javascript:;">4</a>
            <a href="javascript:;">5</a>
            <a href="javascript:;" class="next">⌝</a>
        </div>
    </div>

</body>

</html>

detail.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .text{
            width: 640px;
            margin: 0 auto;
        }
        .article-info{
            color:#999;
            font-size: 14px;
        }
        p{
            font-size: 16px;
            line-height: 30px;
        }
    </style>
</head>
<body>
    <div class="text">
        <h1 class="title">新聞標題</h1>
        <div class="article-info"> 型別:縱火 時間:2019-6-18</div>
        <p class="content">
            新聞內容新聞內容
            新聞內容新聞內容
            新聞內容新聞內容
        </p>
    </div>
</body>
</html>

data.json:同上,爬蟲所得的資料

實現詳情頁展示和分頁功能

更改index.js為indexPager.js:其他檔案不變,直接覆蓋detail.html中的內容

//將爬蟲得到的資料,渲染到頁面,需要進行頁面路由
const http = require("http");
const fs = require("fs");
const cheerio = require("cheerio");
const url = require("url");//處理地址
const path = require("path");//處理地址字尾名
const mime = require("./mime.json");//處理字尾名檔案
const dataJson = require("./data.json");//新聞資料

let server = http.createServer((req, res) => {
    //位址列加上頁碼後,會有queryString :pathname:/index ; search: '?pageNum=2';query: 'pageNum=2'
    let pathname = url.parse(req.url).pathname;
    
    if (pathname === "/" || pathname === "/index") {
        res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
        //方法一:讀取到index檔案,獲取檔案標籤,設定檔案標籤內容
        let index = fs.readFileSync("index.html");
        let $ = cheerio.load(index);
        //分頁實現:總頁數=總資料條數/每頁多少條
        //當點選某頁時,需要獲取到傳過來的頁碼(通過path可以獲取到queryString引數)
        //url.parse(req.url, true).query 當沒有引數時[Object: null prototype] {},有引數時:[Object: null prototype] { pageNum: '2' }
        let pageNum = url.parse(req.url, true).query.pageNum || 1;//第幾頁(預設第一頁)


        let pageSize = 5;//每頁多少條
        let pageTotal = Math.ceil(dataJson.length/pageSize);//總共多少頁
        //通過陣列slice方法進行分頁(如果是資料庫,需要調資料庫)0-4 5-10 11-14 =>(pageNum-1)*pageSize , pageNum*pageSize
        let pageData = dataJson.slice((pageNum-1)*pageSize,pageNum*pageSize);

        //讀取到json檔案新聞資料
        //獲取ul 將HTML設定進去
        let ulHtml = '';
        pageData.forEach(item => {
            ulHtml += `
                <li class="news">
                    <a href="javascript:;">
                        <img src="./img/img.png" alt="">
                    </a>
                    <div>
                        <h3>
                            <a href="/detail?id=${item.id}">${item.title}</a>
                        </h3>
                        <div class="info">
                            <span class="tips"><span>縱火</span><span${item.publisher}</span><span>逮捕</span></span>
                            <!-- <span class="line"></span> -->
                            <span class="time">| &nbsp;&nbsp;${item.time}</span>
                        </div>
                    </div>
                </li>
             `;
        });
        $(".news-list").html(ulHtml);

        //渲染分頁按鈕
        //cheerio沒有事件,所以上下頁切換不能繫結點選事件,只能通過操作pageNum實現,注意需要將pageNum轉為Number型別
        let p = parseInt(pageNum);
        let pagerHtml = `<a href="/index?pageNum=${Math.max(1,p-1)}" class="prev">⌜</a>`;
        for(let i=1;i<=pageTotal;i++){
            //點選每頁還是跳轉到本頁面,這是頁碼變化
            pagerHtml += `<a href="/index?pageNum=${i}">${i}</a>`;
        }
        pagerHtml += `<a href="/index?pageNum=${Math.min(pageTotal,p+1)}" class="next">⌝</a>`;
        $(".pagination").html(pagerHtml);

        //點選事件:設定陣列中某個元素的樣式,.eq(pageNum)
        $(".pagination a").each((index,item)=>{
            if(pageNum == index){
                $(".pagination a").eq(pageNum).addClass('active')
            }
        });

        res.end($.html());

    }else if(pathname === "/detail"){
        //跳轉到詳情頁
        //使用id重新查詢對應資料
        let id = url.parse(req.url, true).query.id;
        let detailData = dataJson.find(item=>id==item.id);
        let detail = fs.readFileSync("detail.html");
        let $ = cheerio.load(detail);
        let detailHtml = `
            <h1 class="title">${detailData.title}</h1>
            <div class="article-info"> 型別:縱火 時間:${detailData.time}</div>
            <p class="content">
                ${detailData.title}
            </p>
        `;

        $(".text").html(detailHtml);  
        //一定要使用res.end()方式時響應結束
        res.end($.html());      

    } else if (pathname !== "/favicon.ico") {
        let extname = path.extname(req.url);
        res.writeHead(200, { 'Content-Type': mime[extname] });
        let static = fs.createReadStream("." + req.url);
        static.pipe(res);
    }
});
server.listen(3000);

效果:

相關文章