一文入門NodeJS


 

NodeJS

1.環境配置

之前講ES6的時候有提過一部分Node的知識,簡單回顧下:一文讀懂ES6

1.1.NPM國內鏡像

npm國內鏡像https://npm.taobao.org

配置國內源:npm install -g cnpm --registry=https://registry.npm.taobao.org

然后就可以把cnpm當作npm來用了,比如之前的React組件案例:

  1. cnpm install react
  2. cnpm install react-dom
  3. cnpm i babel-core@old

卸載安裝的包npm uninstall -g uuidnpm而不是cnpm

常用參數說明:

  1. iinstall的簡寫
  2. -g是安裝到全局環境中(默認是當前目錄)
  3. -D添加為開發依賴(-D ==> --save-dev 開發環境)
  4. -S添加為生產依賴(-S ==> --save 生產環境)
    • eg:cnpm i express -S

eg:cnpm init之后: 1.init.png 1.add.png

PS:你把依賴包刪了也沒事,執行cnpm i就會會根據package.json自動安裝依賴包

課外閱讀:

npm如何管理依賴包的版本
https://www.jianshu.com/p/1470c5d7b8c3

禁止npm自動升級依賴包以及所有下級依賴包版本的方法
https://www.jianshu.com/p/f481cf9b0817

1.2.VSCode調試

這個之前也說過,可以看看:VSCode and NoteBook for JavaScript | NodeJS,簡單說下:

每次F5運行的時候選一下NodeJS,或者添加一下調試的配置文件

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "啟動程序",
            "program": "${workspaceFolder}/${relativeFile}"
        }
    ]
}

nodejs用法和js基本一樣,只是多了些服務器的模塊,eg: 1.vscode.png

配置NodeJS和HTML

如果想同時運行nodejshtml再添加下配置就行了,比如:

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "chrome",
            "request": "launch",
            "name": "Launch Chrome against localhost",
            "url": "http://localhost:8080/${relativeFile}",
            "webRoot": "${workspaceFolder}"
        },
        {
            "type": "node",
            "request": "launch",
            "name": "啟動程序",
            "program": "${workspaceFolder}/${relativeFile}"
        }
    ]
}

配置完成后:想運行HTML就選擇谷歌瀏覽器 1.瀏覽器.png

想運行nodejsjs文件就選擇啟動程序(現在運行JS文件的時候,F5即可自動切換成node1.nodejs.png

配置說明

  1. ${workspaceRoot} VS Code當前打開的文件夾
  2. ${file} 當前打開的文件
  3. ${relativeFile} 相對於workspaceRoot的相對路徑
  4. ${fileBasename} 當前打開文件的文件名
  5. ${fileDirname} 所在的文件夾,是絕對路徑
  6. ${fileExtname} 當前打開文件的擴展名

1.3.Jupyter NoteBook

ijavascript依賴於Python2.7,詳細過程:Jupyter NoteBook IJavaScript 配置

# 如果nodejs和npm沒安裝可以先安裝下
sudo apt-get install nodejs npm

# 把ijavascript安裝到全局環境中
sudo npm install -g ijavascript

#  安裝
ijsinstall

然后就和Python一樣用了: 1.ijavascript.png

 

2.常用模塊

這塊官方文檔寫的很詳細,我就簡單說說,后面用到再詳細說

中文文檔:http://nodejs.cn/api

官方文檔:https://nodejs.org/api

2.1.http(常用)

文檔:http://nodejs.cn/api/http.html or https://nodejs.org/api/http.html

NodeJS既然作為服務器,那得先有個服務器的樣子,我們來個簡單的案例:

node
// ES6語法:import http from "http"; (現在還沒能完全支持)
const http = require("http")

// 創建一個服務器
let server = http.createServer((request, response) => {
    // 每次請求都會執行這個方法
    console.log(request.url);

    response.write("<h1>Test NodeJS</h>");
    response.end() // 告訴瀏覽器響應結束

});

// 服務器啟動並監聽指定端口
server.listen(8080); // 這個和其他語言不一樣,直接監聽對應的端口

效果: 1.vscode.png

其他內容需要結合其他模塊一起講解

2.2.fs(常用)

文檔:http://nodejs.cn/api/fs.html or https://nodejs.org/api/fs.html

這個是IO對應的模塊,推薦使用異步方法,簡單看看:(xxxFileSync是同步方式,不建議使用)

In [1]:
const fs = require("fs");
In [2]:
// 文件讀取
fs.readFile("test.txt",(ex,data) => {
    // 如果文件不存在就輸出錯誤信息
    if(ex){
        console.log(ex);
    }else{
        console.log(data.toString());
    }
});

console.log("[ReadFile異步驗證]我出現就是異步");
 
[ReadFile異步驗證]我出現就是異步
{ Error: ENOENT: no such file or directory, open 'test.txt' errno: -2, code: 'ENOENT', syscall: 'open', path: 'test.txt' }
In [3]:
// 創建一個文件
fs.writeFile("test.txt","文本內容",ex => {
    if(ex){
        // 出錯就輸出info
        console.log(ex);
    }
});

console.log("[WriteFile異步驗證]我出現就是異步");
 
[WriteFile異步驗證]我出現就是異步
In [4]:
// 文件追加
fs.appendFile("test.txt","追加內容",ex => {
   if(ex){
        // 出錯就輸出info
       console.log(ex);
   } 
});

console.log("[AppendFile異步驗證]我出現就是異步");
 
[AppendFile異步驗證]我出現就是異步
In [5]:
// 現在再讀着看看
fs.readFile("test.txt",(ex,data) => {
    // 如果文件不存在就輸出錯誤信息
    if(ex){
        console.log(ex);
    }else{
        console.log(data.toString());
    }
});
 
文本內容追加內容
 

PS:如果文件不是文本文件,就不能toString了(data默認是buffer類型)

node
fs.readFile("知識星球.png", (ex, data) => {
    if (ex) {
        console.log("讀取錯誤:", ex);
    } else {
        console.log(data); // 看看buffer是啥樣的
        fs.writeFile("test.png", data, ex => {
            if (ex) {
                console.log("復制錯誤:", ex);
            }
        });
    }
});

效果:

<Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 02 10 00 00 02 61 08 06 00 00 00 10 7e 89 ed 00 00 20 00 49 44 41 54 78 01 ec bd 07 98 64 57 79 ... >

圖片照常打開: 公眾號:逸鵬說道

簡單小結一下:

  1. 讀取文件:fs.readFile("xxx", (ex, data) => { });
  2. 創建文件:fs.writeFile("xxx", data, ex => {});
  3. 追加文件:fs.appendFile("xxx", data, ex => {});
  4. databuffer類型,內置了:
    • toString():buffer轉換成字符串
    • toJSON():buffer轉化成Json
In [6]:
// 看個案例
data = { "name": "小明", "age": "23" };

fs.writeFile("to.txt", data, ex => {
    if (ex) {
        console.log(ex);
    }
});

fs.readFile("to.txt", (ex, data) => {
    if (ex) {
        console.log(ex);
    } else {
        console.log(data);
        console.log(data.toJSON());
        console.log(data.toString());
        console.log(data.toLocaleString());
    }
});
 
<Buffer 5b 6f 62 6a 65 63 74 20 4f 62 6a 65 63 74 5d>
{ type: 'Buffer',
  data: [ 91, 111, 98, 106, 101, 99, 116, 32, 79, 98, 106, 101, 99, 116, 93 ] }
[object Object]
[object Object]
 

注意點

上面幾個方法(eg:readFile)都是先把數據都緩存到內存中,然后才回調,這樣比較浪費內存,對於大文件不友好,so ==> 流走起

Stream(常用)

用法比較簡單,看個案例:

node
const fs = require("fs");

let rs = fs.createReadStream("知識星球.png");
let ws = fs.createWriteStream("test.png");
// 可以這么理解,rs是水龍頭防水的地方,寫反了也就出不了水了
rs.pipe(ws); // 創建一個管道,流從r端到w端

還有一些類似於監聽的事件:

node
const fs = require("fs");

let rs = fs.createReadStream("知識星球.png");
let ws = fs.createWriteStream("test.png");
rs.pipe(ws); // 創建一個管道,流從r端到w端

// 可以理解為錯誤觸發的事件
rs.on("error", ex => {
    console.log("讀取失敗", ex);
});

rs.on("end", () => {
    console.log("讀取完成");
});

ws.on("error", ex => {
    console.log("寫入失敗", ex);
});

// 注意,寫入流完成不叫end
ws.on("finish", () => {
    console.log("寫入完成");
});

2.3.url(常用)

文檔:http://nodejs.cn/api/url.html or https://nodejs.org/api/url.html

url模塊之前得先說下querystring模塊

1.querystring

文檔:http://nodejs.cn/api/querystring.html or https://nodejs.org/api/querystring.html

這個是專門針對參數進行解析的,來個案例:

In [1]:
const querystring = require("querystring");
In [2]:
let jd_qs = "keyword=空氣凈化器&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&psort=3&stock=1&wtype=1&cod=1&click=2";

// 咋一看,好像挺方便,但是有坑:看下一個demo
let str = querystring.parse(jd_qs);
console.log(str)
 
{ keyword: '空氣凈化器',
  enc: 'utf-8',
  qrst: '1',
  rt: '1',
  stop: '1',
  vt: '2',
  psort: '3',
  stock: '1',
  wtype: '1',
  cod: '1',
  click: '2' }
In [3]:
// 用戶請求一般都是類似於這樣的
let jd_url = "https://search.jd.com/Search?keyword=空氣凈化器&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&psort=3&stock=1&wtype=1&cod=1&click=2";

// querystring.parse 只是對?后面(不包括`?`)的參數進行解析(以`=`和`&`分隔)
str = querystring.parse(jd_url);
console.log(str);
 
{ 'https://search.jd.com/Search?keyword': '空氣凈化器',
  enc: 'utf-8',
  qrst: '1',
  rt: '1',
  stop: '1',
  vt: '2',
  psort: '3',
  stock: '1',
  wtype: '1',
  cod: '1',
  click: '2' }
 

注意:querystring.parse 只是對?后面(不包括?)的參數進行解析(以=&分隔)

2.url

上面說下querystring只是一個鋪墊,基本上不太用,url模塊已經包含這個了:

In [1]:
const url = require("url");
In [2]:
// port=null說明是默認端口(http:80,https:443)
let jd_url = "https://search.jd.com/Search?keyword=空氣凈化器&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&psort=3&stock=1&wtype=1&cod=1&click=2";

let str = url.parse(jd_url);
console.log(str); // 發現query並沒有解析
 
Url {
  protocol: 'https:',
  slashes: true,
  auth: null,
  host: 'search.jd.com',
  port: null,
  hostname: 'search.jd.com',
  hash: null,
  search: '?keyword=空氣凈化器&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&psort=3&stock=1&wtype=1&cod=1&click=2',
  query: 'keyword=空氣凈化器&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&psort=3&stock=1&wtype=1&cod=1&click=2',
  pathname: '/Search',
  path: '/Search?keyword=空氣凈化器&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&psort=3&stock=1&wtype=1&cod=1&click=2',
  href: 'https://search.jd.com/Search?keyword=空氣凈化器&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&psort=3&stock=1&wtype=1&cod=1&click=2' }
In [3]:
// 想要解析`query`,可以多傳一個參數
str = url.parse(jd_url, true);
console.log(str); // 對query解析
 
Url {
  protocol: 'https:',
  slashes: true,
  auth: null,
  host: 'search.jd.com',
  port: null,
  hostname: 'search.jd.com',
  hash: null,
  search: '?keyword=空氣凈化器&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&psort=3&stock=1&wtype=1&cod=1&click=2',
  query: 
   { keyword: '空氣凈化器',
     enc: 'utf-8',
     qrst: '1',
     rt: '1',
     stop: '1',
     vt: '2',
     psort: '3',
     stock: '1',
     wtype: '1',
     cod: '1',
     click: '2' },
  pathname: '/Search',
  path: '/Search?keyword=空氣凈化器&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&psort=3&stock=1&wtype=1&cod=1&click=2',
  href: 'https://search.jd.com/Search?keyword=空氣凈化器&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&psort=3&stock=1&wtype=1&cod=1&click=2' }
 

PS:一般都是這么用的:

node
// {a, b} = {a:21,b=34,c=22} 只要對應即可解包,如果想取別名可以使用:{a:xx, b} = {...}
let { pathname, query } = url.parse(request.url, true);

3.前幾個模塊的綜合案例

結合上面的HTTP模塊,來個簡單的web服務器

node
const fs = require("fs");
const url = require("url");
const http = require("http");

// 創建服務
let server = http.createServer((request, response) => {
    // 請求
    // {a, b} = {a:21,b=34,c=22} 只要對應即可解包,如果想取別名可以使用:{a:xx, b} = {...}
    let { pathname, query } = url.parse(request.url, true);
    console.log(query, pathname);

    // 讀取對應文件
    fs.readFile(`www${pathname}`, (ex, data) => {
        if (ex) {
            // 返回404狀態碼,並設置編碼為UTF-8
            response.writeHeader(404, {
                "Content-Type": "text/html;charset=utf-8"
            });
            // 提示需要在 writeHeader 之后,不然訪問的是瀏覽器404頁面
            response.write("<h1>訪問的頁面不存在~</h1>");
        } else {
            response.write(data);
        }
        // 響應結束
        response.end();
    });
});
// 服務器啟動並監聽指定端口
server.listen(8080);

輸出:(www目錄就兩個文件,一個test.html,一個test.png)

2.nodejs.png

推薦寫法

node
const fs = require("fs");
const url = require("url");
const http = require("http");

let server = http.createServer((request, response) => {
    let { pathname } = url.parse(request.url, true);
    console.log(pathname);

    let rs = fs.createReadStream(`www${pathname}`);
    // `request`和`response`就是一個典型的讀寫流(`ReadStream`、`WriteStream`)
    rs.pipe(response);

    // 讀取失敗 ==> 404
    rs.on("error", ex => {
        response.writeHeader(404);
        response.write("404 Not Found");
        response.end();
    });
});

server.listen(8080);

PS:requestresponse就是一個典型的讀寫流(ReadStreamWriteStream

2.4.zlib(常用)

文檔:http://nodejs.cn/api/zlib.html or https://nodejs.org/api/zlib.html

先看個案例:(zlib是讀寫流)

node
const fs = require("fs");
const zlib = require("zlib");

// 讀寫流
let gz = zlib.createGzip();
// 讀流
let rs =fs.createReadStream("./www/jquery-2.1.1.js");
// 寫流
let ws =fs.createWriteStream("test.js.gz");
// 可以這么理解:(gz是讀寫流)
// rs水龍頭先傳給了gz,gz又當一個水龍頭傳給了ws
rs.pipe(gz).pipe(ws);

ws.on("finish",()=>{
    console.log("寫入完畢");
});

效果: 1.zlib.png

靜態服務器

結合上面再來個加強版:

node
const fs = require("fs");
const zlib = require("zlib");
const http = require("http");

let server = http.createServer((request, response) => {
    // let { pathname } = url.parse(request.url);
    console.log(request.url);
    let rs = fs.createReadStream(`www${request.url}`);
    let gz = zlib.createGzip();

    // 響應之前告訴瀏覽器是gzip的格式
    response.setHeader("Content-Encoding", "gzip");
    // 返回gzip壓縮后的文件
    rs.pipe(gz).pipe(response);

    // 讀取失敗,404錯誤
    rs.on("error", ex => {
        response.removeHeader("Content-Encoding");
        // 返回404狀態碼,並設置編碼為UTF-8
        response.writeHeader(404, {
            "Content-Type": "text/html;charset=utf-8"
        });
        // 提示需要在 writeHeader 之后,不然訪問的是瀏覽器404頁面
        response.write("<h2>您訪問的頁面不存在~</h2>");
        response.end();
    });
});

server.listen(8080, () => {
    console.log("服務器啟動成功,端口:8080");
});

輸出對比: 1.zlib2.png

2.5.path

文檔:http://nodejs.cn/api/path.html or https://nodejs.org/api/path.html

這個主要是針對路徑的模塊,看個案例:path.parse()

In [4]:
const path = require("path");
In [5]:
let file_name = "./images/png/小明.png";

// 文件路徑 ./images/png
console.log(path.dirname(file_name));

// 提取出用 `/` 隔開的 `path` 的最后一部分
// 小明.png
console.log(path.basename(file_name));

// 文件后綴 .png
console.log(path.extname(file_name));

// 文件信息 {root: "", dir: "./images/png", base: "小明.png", ext: ".png", name: "小明"}
console.log(path.parse(file_name)); // 這個經常使用

// ------------------------------
// 當前文件所在文件夾絕對路徑
console.log(path.resolve());
// 文件的絕對路徑
console.log(path.resolve(file_name));
 
./images/png
小明.png
.png
{ root: '',
  dir: './images/png',
  base: '小明.png',
  ext: '.png',
  name: '小明' }
/home/dnt/桌面/work/BaseCode/javascript/NoteBook/2.Node
/home/dnt/桌面/work/BaseCode/javascript/NoteBook/2.Node/images/png/小明.png
In [6]:
// 可以看看path的完整信息
console.log(path);
 
{ resolve: [Function: resolve],
  normalize: [Function: normalize],
  isAbsolute: [Function: isAbsolute],
  join: [Function: join],
  relative: [Function: relative],
  _makeLong: [Function: _makeLong],
  dirname: [Function: dirname],
  basename: [Function: basename],
  extname: [Function: extname],
  format: [Function: format],
  parse: [Function: parse],
  sep: '/',
  delimiter: ':',
  win32: 
   { resolve: [Function: resolve],
     normalize: [Function: normalize],
     isAbsolute: [Function: isAbsolute],
     join: [Function: join],
     relative: [Function: relative],
     _makeLong: [Function: _makeLong],
     dirname: [Function: dirname],
     basename: [Function: basename],
     extname: [Function: extname],
     format: [Function: format],
     parse: [Function: parse],
     sep: '\\',
     delimiter: ';',
     win32: [Circular],
     posix: [Circular] },
  posix: [Circular] }
 

2.6.crypto

文檔:http://nodejs.cn/api/crypto.html or https://nodejs.org/api/crypto.html

這塊主要就是用來加密(簽名)的,很簡單:

In [7]:
const cypto = require("crypto");
In [8]:
let obj = cypto.createHash("md5");
// // PS:分多次提交也一樣:
// obj.update("123");
// obj.update("456");
obj.update("123456");
// e10adc3949ba59abbe56e057f20f883e
console.log(obj.digest("hex"));
 
e10adc3949ba59abbe56e057f20f883e
In [9]:
let obj2 = cypto.createHash("sha1");
obj2.update("123456");
// 7c4a8d09ca3762af61e59520943dc26494f8941b
console.log(obj2.digest("hex"));
 
7c4a8d09ca3762af61e59520943dc26494f8941b
In [10]:
let obj3 = cypto.createHash("sha256");
obj3.update("123456");
// 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
console.log(obj3.digest("hex"));
 
8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
 

2.7.os

文檔:http://nodejs.cn/api/os.html or https://nodejs.org/api/os.html

這塊是系統相關的模塊,看個常用的案例:

In [11]:
const os = require("os");

// cup核數
console.log(os.cpus().length);

// 輸出我老電腦CPU詳細信息
console.log(os.cpus());
 
4
[ { model: 'Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz',
    speed: 2804,
    times: { user: 6287300, nice: 2400, sys: 1690400, idle: 80994500, irq: 0 } },
  { model: 'Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz',
    speed: 2661,
    times: { user: 5714300, nice: 2800, sys: 1585700, idle: 83027300, irq: 0 } },
  { model: 'Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz',
    speed: 2796,
    times: { user: 6217400, nice: 2700, sys: 1720400, idle: 82200300, irq: 0 } },
  { model: 'Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz',
    speed: 2821,
    times: { user: 5779100, nice: 1900, sys: 1589800, idle: 82863800, irq: 0 } } ]
In [ ]:
 
 

2.8.多進程

可以參考我之前講的Python多進程進行對比學習:https://www.cnblogs.com/dotnetcrazy/p/9363810.html

基礎入門

node
const os = require("os");
const process = require("process");
const cluster = require("cluster");

// 主進程:分配任務
if (cluster.isMaster) {
    console.log(`主進程PID:${process.pid}`);
    for (let i = 0; i < os.cpus().length; i++) {
        cluster.fork();
    }
} else {
    // 子進程執行任務
    console.log(`當前進程PID:${process.pid},父進程PPID:${process.ppid}`);
}

輸出:

主進程PID:20680
當前進程PID:20620,父進程PPID:20680
當前進程PID:23340,父進程PPID:20680
當前進程PID:11644,父進程PPID:20680
當前進程PID:22144,父進程PPID:20680

服務器的案例

node
const os = require("os");
const http = require("http");
const process = require("process");
const cluster = require("cluster");

// 主進程:分配任務
if (cluster.isMaster) {
    console.log(`主進程PID:${process.pid}`);
    for (let i = 0; i < os.cpus().length; i++) {
        cluster.fork();
    }
} else {
    // 子進程執行任務
    http.createServer((request, response) => {
        console.log(`當前進程PID:${process.pid},父進程PPID:${process.ppid}`);
        response.write("Fork Test");
        response.end();
    }).listen(8080, () => {
        console.log(`服務器啟動成功,當前端口:8080,進程PID:${process.pid}`);
    });
}

輸出: 2.process.png

注意點

PS:NodeJS注意下面幾地方:

  1. 子進程可以共用一個端口
  2. 並發的時候,如果不等到某個核的CPU100%,其他核的CPU就看戲(不工作)

2.9.uuid

這個是第三方模塊,如果沒安裝:

  1. cnpm init
  2. cnpm i uuid -D(開發環境) or cnpm i uuid -S(生產環境)

1.init.png 1.add.png

使用案例:

node
const uuid = require("uuid/v4");

let uid = uuid();
console.log(uid);

輸出:

65334387-110e-489a-b4c5-cb19cb3875d0


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM