一文入门NodeJS

NodeJS

1.环境配置

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,或者添加一下调试的配置文件

<pre>{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "启动程序",
            "program": "${workspaceFolder}/${relativeFile}"
        }
    ]
}
</pre>

nodejs用法和js基本一样,只是多了些服务器的模块,eg:

1.vscode.png

配置NodeJS和HTML

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

<pre>{
    "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}"
        }
    ]
}
</pre>

配置完成后:想运行HTML就选择谷歌浏览器
1.浏览器.png

想运行nodejsjs文件就选择启动程序(现在运行JS文件的时候,F5即可自动切换成node

1.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 配置

<pre># 如果nodejs和npm没安装可以先安装下
sudo apt-get install nodejs npm

把ijavascript安装到全局环境中

sudo npm install -g ijavascript

安装

ijsinstall
</pre>

然后就和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是同步方式,不建议使用)

const fs = require("fs");


// 文件读取
fs.readFile("test.txt",(ex,data) => {
    // 如果文件不存在就输出错误信息
    if(ex){
        console.log(ex);
    }else{
        console.log(data.toString());
    }
});

console.log("[ReadFile异步验证]我出现就是异步");
</pre>

<pre>[ReadFile异步验证]我出现就是异步
{ Error: ENOENT: no such file or directory, open 'test.txt' errno: -2, code: 'ENOENT', syscall: 'open', path: 'test.txt' }
</pre>

<pre>// 创建一个文件
fs.writeFile("test.txt","文本内容",ex => {
    if(ex){
        // 出错就输出info
        console.log(ex);
    }
});

console.log("[WriteFile异步验证]我出现就是异步");
</pre>

<pre>[WriteFile异步验证]我出现就是异步
</pre>

<pre>// 文件追加
fs.appendFile("test.txt","追加内容",ex => {
   if(ex){
        // 出错就输出info
       console.log(ex);
   } 
});

console.log("[AppendFile异步验证]我出现就是异步");
</pre>

<pre>[AppendFile异步验证]我出现就是异步
</pre>

<pre>// 现在再读着看看
fs.readFile("test.txt",(ex,data) => {
    // 如果文件不存在就输出错误信息
    if(ex){
        console.log(ex);
    }else{
        console.log(data.toString());
    }
});
</pre>

<pre>文本内容追加内容
</pre>

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 ... >


图片照常打开: ![公众号:逸鹏说道](https://upload-images.jianshu.io/upload_images/20814339-31793207737469ea.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

简单小结一下:

1.  读取文件:`fs.readFile("xxx", (ex, data) => { });`
2.  创建文件:`fs.writeFile("xxx", data, ex => {});`
3.  追加文件:`fs.appendFile("xxx", data, ex => {});`
4.  `data`是`buffer`类型,内置了:
    *   `toString()`:buffer转换成字符串
    *   `toJSON()`:buffer转化成Json

<pre>// 看个案例
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());
    }
});
</pre>

<pre><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]
</pre>

#### 注意点[](https://www.cnblogs.com/dotnetcrazy/p/10118756.html#注意点)

上面几个方法(eg:`readFile`)都是先把数据都缓存到内存中,然后才回调,这样比较浪费内存,对于大文件不友好,so ==> 流走起

#### Stream(常用)[](https://www.cnblogs.com/dotnetcrazy/p/10118756.html#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(常用)[](https://www.cnblogs.com/dotnetcrazy/p/10118756.html#2.3.url(常用))

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

说`url`模块之前得先说下`querystring`模块

#### 1.querystring[](https://www.cnblogs.com/dotnetcrazy/p/10118756.html#1.querystring)

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

这个是专门针对参数进行解析的,来个案例:

<pre>const querystring = require("querystring");
</pre>

<pre>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)
</pre>

<pre>{ keyword: '空气净化器',
  enc: 'utf-8',
  qrst: '1',
  rt: '1',
  stop: '1',
  vt: '2',
  psort: '3',
  stock: '1',
  wtype: '1',
  cod: '1',
  click: '2' }
</pre>

<pre>// 用户请求一般都是类似于这样的
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);
</pre>

<pre>{ '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' }
</pre>

注意:**querystring.parse 只是对?后面(不包括`?`)的参数进行解析(以`=`和`&`分隔)**

#### 2.url[](https://www.cnblogs.com/dotnetcrazy/p/10118756.html#2.url)

上面说下`querystring`只是一个铺垫,基本上不太用,`url`模块已经包含这个了:

<pre>const url = require("url");
</pre>

<pre>// 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并没有解析
</pre>

<pre>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' }
</pre>

<pre>// 想要解析`query`,可以多传一个参数
str = url.parse(jd_url, true);
console.log(str); // 对query解析
</pre>

<pre>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' }
</pre>

PS:一般都是这么用的:

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


#### 3.前几个模块的综合案例[](https://www.cnblogs.com/dotnetcrazy/p/10118756.html#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](https://upload-images.jianshu.io/upload_images/20814339-da60b0732a6805f9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

**推荐写法**:

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:**`request`和`response`就是一个典型的读写流(`ReadStream`、`WriteStream`)**

### 2.4.zlib(常用)[](https://www.cnblogs.com/dotnetcrazy/p/10118756.html#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](https://upload-images.jianshu.io/upload_images/20814339-8a22d14169b5edf9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

#### 静态服务器[](https://www.cnblogs.com/dotnetcrazy/p/10118756.html#静态服务器)

结合上面再来个加强版:

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](https://upload-images.jianshu.io/upload_images/20814339-973c7ebbe70d730d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

### 2.5.path[](https://www.cnblogs.com/dotnetcrazy/p/10118756.html#2.5.path)

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

这个主要是针对路径的模块,看个案例:`path.parse()`

<pre>const path = require("path");
</pre>

<pre>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));
</pre>

<pre>./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
</pre>

<pre>// 可以看看path的完整信息
console.log(path);
</pre>

<pre>{ 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] }
</pre>

### 2.6.crypto[](https://www.cnblogs.com/dotnetcrazy/p/10118756.html#2.6.crypto)

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

这块主要就是用来加密(签名)的,很简单:

<pre>const cypto = require("crypto");
</pre>

<pre>let obj = cypto.createHash("md5");
// // PS:分多次提交也一样:
// obj.update("123");
// obj.update("456");
obj.update("123456");
// e10adc3949ba59abbe56e057f20f883e
console.log(obj.digest("hex"));
</pre>

<pre>e10adc3949ba59abbe56e057f20f883e
</pre>

<pre>let obj2 = cypto.createHash("sha1");
obj2.update("123456");
// 7c4a8d09ca3762af61e59520943dc26494f8941b
console.log(obj2.digest("hex"));
</pre>

<pre>7c4a8d09ca3762af61e59520943dc26494f8941b
</pre>

<pre>let obj3 = cypto.createHash("sha256");
obj3.update("123456");
// 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
console.log(obj3.digest("hex"));
</pre>

<pre>8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
</pre>

### 2.7.os[](https://www.cnblogs.com/dotnetcrazy/p/10118756.html#2.7.os)

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

这块是系统相关的模块,看个常用的案例:

<pre>const os = require("os");

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

// 输出我老电脑CPU详细信息
console.log(os.cpus());
</pre>

<pre>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 } } ]
</pre>

### 2.8.多进程[](https://www.cnblogs.com/dotnetcrazy/p/10118756.html#2.8.多进程)

可以参考我之前讲的Python多进程进行对比学习:[https://www.cnblogs.com/dotnetcrazy/p/9363810.html](https://www.cnblogs.com/dotnetcrazy/p/9363810.html)

#### 基础入门[](https://www.cnblogs.com/dotnetcrazy/p/10118756.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


#### 服务器的案例[](https://www.cnblogs.com/dotnetcrazy/p/10118756.html#服务器的案例)

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](https://upload-images.jianshu.io/upload_images/20814339-d671b61049c3fb56.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

#### 注意点[](https://www.cnblogs.com/dotnetcrazy/p/10118756.html#注意点)

PS:NodeJS注意下面几地方:

1.  **子进程可以共用一个端口**
2.  **并发的时候,如果不等到某个核的CPU100%,其他核的CPU就看戏(不工作)**

### 2.9.uuid[](https://www.cnblogs.com/dotnetcrazy/p/10118756.html#2.9.uuid)

这个是第三方模块,如果没安装:

1.  `cnpm init`
2.  `cnpm i uuid -D`(开发环境) or `cnpm i uuid -S`(生产环境)

![1.init.png](https://upload-images.jianshu.io/upload_images/20814339-325908bfa08aa8f6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

 ![1.add.png](https://upload-images.jianshu.io/upload_images/20814339-1f0bf1388e80826c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

使用案例:

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

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


输出:

65334387-110e-489a-b4c5-cb19cb3875d0

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,681评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,710评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,623评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,202评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,232评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,368评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,795评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,461评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,647评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,476评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,525评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,226评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,785评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,857评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,090评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,647评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,215评论 2 341