Node 从入门到实践开发

Node 概述

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。

Node.js 和浏览器上 JS 的区别

Node.js VS 浏览器上的 JS

运行环境不同
API 不同
全局变量不同 window vs global
事件循环

Node.js VS 浏览器上的 JS
Node.js VS 浏览器上的 JS

Node.js 语法概述

Node.js语法是完全基于JavaScript的,下列内容与JS完全一样

  • 数据类型
  • 声明变量和常量
  • 运算符
  • 逻辑结构
  • 函数作用域和闭包
  • 对象和原型
  • 对象分类
  • JS和Node.js的最大不同点在由解释器所提供的扩展对象上!。

数据类型

  • 原始类型:string、number、boolean、null、undefined
  • 引用类型:ES核心对象:Global、String、Number、Boolean、Date、Math、Array、Error、Function、Object、RegExp
  • Node.js对象:Buffer、ReadStream、ClientRequest
  • 自定义对象

Node.js的特点

  • 简单,避免过度设计
  • 单线程逻辑处理
  • 非阻塞的异步I/O处理
  • 事件驱动编程
  • 无锁机制,不会产生死锁
  • 支持数万个并发链接

Node适用于IO密集型的应用,而不适合于CPU密集的应用。

Node全局对象

嵌入在浏览器中的V8引擎,支持ES原生对象、BOM和DOM对象,全局对象为window
Node中的全局对象是global,所有全局变量都是global对象的属性。

global

在交互模式下声明的全局变量和函数都是global对象的成员——全局对象污染
在脚本模式下声明的”全局变量和函数”不是global对象的成员——避免了全局对象污染
脚本模式下,每个.js文件都是一个独立的模块对象,其中创建的“全局变量和函数”都是该对象构造方法内的局部成员

process

当操作系统启动一个程序时,会将必须的可执行文件和数据文件从文件系统调入内存,分配必须的内存空间,执行其中的代码——称为创建了一个执行进程。
— 全局对象global.process就是这个进程的代码表示;
— process对象获取当前操作系统及运行时信息,并操作脚本所在执行进程。

Buffer

Buffer:缓冲区,专用于存储数据的内存区域,用于存储读写的文件数据、网络上要传输的数据等。
Buffer对象的实例,可以直接构建,也可以通过数据读写获得。
能存储字符数据,二进制的字节数据

1
var buf1 = new Buffer.alloc(32);

将 Buffer 内容转换为特定编码的string

1
console.log(buf3.toString('utf8'));

缓冲区:内存的一部分。在内存空间中预留了一定的存储空间,存储空间是用来缓冲输入或输出的数据。
缓冲区分为输入缓冲区和输出缓冲区。

高速设备与低速设备的不匹配,势必会让高速设备花时间等待低俗设备,可以在两者之间设立一个缓存区。

缓冲区的作用:
1.可以解除两者的制约关系,数据可以直接送往缓冲区,高速设备不用在等待低速设备,提高了计算机的效率。
2.可以减少数据的读写次数,如果每次数据只传输一点数据,就需要传送很多次,这样会浪费很多时间,因为开始读写与终止读写所需的时间很长,如果将数据送往缓冲区,待缓冲区满后再进行传送会大大减少读写次数,这样就可以节约很多时间。

缓冲区就是一块内存区,它用在输入输出设备和CPU之间,用来存储数据。它使得低速的输入输出设备和高速的CPU能够协调工作,避免低速的输入输出设备占用CPU,解放出CPU,使其能够高效率工作。

buffer偏重于写
cache偏重于读

全局函数

定时器
global.setTimeout():一次性定时器
global.setInterval():周期性定时器
process.nextTick():本次事件循环结束时立即执行的定时器
global.setImmediate():下次事件循环立即执行的定时器

模块

Node.js中每一个.js文件对应一个模块对象;
模块内声明的变量或函数的作用域是“模块作用域”——默认只能在当前JS文件(即当前模块)中使用,而不是全局作用域;
每个模块可以导出(exports)自己内部的成员提供其它模块使用,也可以引入(require)并使用其他模块中导出的成员;
Node.js启动时运行的第一个模块称为主模块(main module)

模块分类

核心模块
Node.js提供,被编译进二进制执行文件,可以被解释器直接使用,加载速度最快

第三方模块
基于node.js核心模块,第三方扩展应用模块
常用的第三方模块:express、less、webpack

自定义模块
文件式自定义模块
目录式自定义模块

Node.js 的安装运行

在官网上下载,安装点击下一步下一步即可完成安装。

检验是否安装成功,打开命令行窗口。

1
2
3
4
node -v
v10.16.0
npm -v
6.13.7

以上显示为正常。

如果显示没该命令,
你可以尝试在系统变量的 Path 中找寻 node的路径如下图所示。

node 的环境变量配置

Node 的运行方式

可以在命令窗口中运行例如

编译模式

1
2
node
console.log('Hello World')
1
node -e "console.log('呵呵')";

也可以运行文件。

1
node hello.js

Node.js 最重要的几个概念

Node.js 模块
Node.js 包管理 NPM Yarn

Node.js 模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
* @file a
* @author Max
*/
// a.js
// require 引入 b 模块
// require('b') 会到 node_modules 中找模块
const b = require('./b');

console.log(b);

// c.js
var hello = require('./d');
var he = new hello();
he.setName('marico');
he.sayHello();

var he2 = new hello();
he2.setName('yfc');
he2.sayHello();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
* @file module example
* @author Max
*/
// b.js

console.log(1);

// 导出内容
module.exports = 'hello world'

// d.js
function hello(){
var name;
this.setName = function(thyName){
name = thyName;
}
this.sayHello = function(){
console.log('hello' + name);
}
}
module.exports = hello;
require 导入是同步的,当导入完成后,才能往下执行代码。

Node.js 包管理 NPM Yarn

npm 常用来下载安装包

File system

读写文件

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const fs = require("fs");
const path = require("path");

// path.resolve(__dirname) 当前所在的目录

const data = new Uint8Array(Buffer.from("hello world"));

// writeFile 异步执行

fs.writeFile(path.resolve(__dirname, "hello.txt"), data, err => {
if (err) throw err;
console.log("文件已被保存");
});

// 同步
// fs.writeFileSync()

fs.readFile(path.resolve(__dirname, "hello.txt"), (err, data) => {
if (err) throw err;
// 二进制转成文字
console.log(data.toString());
});

// 捕获错误
try {
} catch (error) {}

// 捕获错误
process.on("uncaughtException", e => {
console.log(e.message);
});

HTTP

HTTP Server

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
const http = require("http");
const net = require("net"); // net 模块提供了异步的网络 API,用于创建基于流的 TCP 或 IPC 服务器和客户端。
const { URL } = require("url");

// 创建 HTTP 隧道代理。
const proxy = http.createServer((req, res) => {
// req.headers 请求头
console.log(req.headers);
// res.writeHead 写响应头
res.writeHead(200, {
"Content-Type": "text/plain",
"x-myself-http-header": "max"
});

// 跟上面 writeHead 实现效果一样。
/*
res.statusCode = 400;
res.setHeader("Content-Type", "text/html");
res.setHeader("x-myself-http-header", "max");
*/

// 会写入页面中
//res.write("hello world");
res.end("响应内容");
});

proxy.on("connect", (req, cltSocket, head) => {
// 连接到原始服务器。
const { port, hostname } = new URL(`http://${req.url}`);
// 创建 socket 服务
const srvSocket = net.connect(port || 80, hostname, () => {
cltSocket.write(
"HTTP/1.1 200 Connection Established\r\n" +
"Proxy-agent: Node.js-Proxy\r\n" +
"\r\n"
);
srvSocket.write(head);
srvSocket.pipe(cltSocket);
cltSocket.pipe(srvSocket);
});
});

proxy.on("clientError", (err, socket) => {
socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
});

// 代理正在运行。
proxy.listen(1337, "127.0.0.1", () => {
// 向隧道代理发出请求。
const options = {
port: 1337,
host: "127.0.0.1",
method: "CONNECT",
path: "nodejs.cn:80"
};

// 发出 HTTP 请求
const req = http.request(options);
req.end();

req.on("connect", (res, socket, head) => {
console.log("已连接");

// 通过 HTTP 隧道发出请求。
socket.write(
"GET / HTTP/1.1\r\n" +
"Host: nodejs.cn:80\r\n" +
"Connection: close\r\n" +
"\r\n"
);
// 返回响应信息
socket.on("data", chunk => {
console.log(chunk.toString());
});
// 结束关闭
socket.on("end", () => {
proxy.close();
});
});
});

Events 事件

1
2
3
4
5
6
7
8
9
10
11
const EventEmitter = require('events');
// 继承
class MyEmitter extends EventEmitter {}
// 声明事件对象
const myEmitter = new MyEmitter();
// 注册事件
myEmitter.on('event', () => {
console.log('an event occurred!');
});
// 触发事件
myEmitter.emit('event');

爬虫

  • 采用node,js中的 superagent+request + cheerio
  • 使用Nightmare自动化测试工具。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

app.get('/', function (req, res, next) {
// 用 superagent 去抓取 https://cnodejs.org/ 的内容
superagent.get('https://cnodejs.org/') // superagent 发送 get 或 post 请求
.end(function (err, sres) {
// 常规的错误处理
if (err) {
return next(err);
}
// sres.text 里面存储着网页的 html 内容,将它传给 cheerio.load 之后
// 就可以得到一个实现了 jquery 接口的变量,我们习惯性地将它命名为 `$`
// 剩下就都是 jquery 的内容了
var $ = cheerio.load(sres.text);
var items = [];
$('#topic_list .topic_title').each(function (idx, element) {
var $element = $(element);
items.push({
title: $element.attr('title'),
href: $element.attr('href')
});
});

res.send(items);
});
});

nightmare

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const Nightmare = require("nightmare");
const nightmare = Nightmare({ show: true }); // show设置为true,可以看到弹出浏览器出来

nightmare
.goto("https://www.baidu.com") // 打开的页面,这里百度举例
.type("#kw", "giscafer webgis入门实战") // 搜索框的 id 输入的文字
.click("#su") // 搜索按钮的id
.wait(2000) // 等待时间
.evaluate(() => {
let text_list = [];
const nums_text = document.querySelectorAll(".c-container .t a"); // 爬取数据的元素
nums_text.forEach((value) => {
text_list.push(value.textContent);
});
return text_list;
})
.end() // 结束
.then(console.log)
.catch((error) => {
console.error("Search failed:", error);
});

Koa2

基础篇

Koa2 源码解读

知识点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const Koa = require('koa');
const app = new Koa();
app.use((ctx, next) => {
ctx.body = [
{
name: 'tom'
}
]
next()
})
app.use((ctx, next) => {
console.log('url' + ctx.url);
if (ctx.url === '/html') {
ctx.type = 'text/html;charset=utf-8'
ctx.body = `<b>我的名字是:${ctx.body[0].name}</b>`
}
}
app.listen(3000)
// 路由
const router = {}
router['/html'] = ctx => {
ctx.type = 'text/html;charset=utf-8'
ctx.body = `<b>我的名字是:${ctx.body[0].name}</b>`
}
router[ctx.url](ctx)

常⻅见的中间件操作

  • 静态服务
    app.use(require(‘koa-static’)(__dirname + ‘/‘))
  • 路路由
1
2
3
4
5
6
7
8
9
10
const router = require('koa-router')();
router.get('/string', async (ctx, next) => {
ctx.body = 'koa2 string'
})
router.get('/json', async (ctx, next) => {
ctx.body = {
title: 'koa2 json'
}
})
app.use(router.routes())
  • 日志
1
2
3
4
5
6
7
app.use(async (ctx,next) => {
const start = new Date().getTime()
console.log(`start: ${ctx.url}`);
await next();
const end = new Date().getTime()
console.log(`请求${ctx.url}, 耗时${parseInt(end-start)}ms`)
})

koa 原理

express

formidable:用于解析表单数据的模块,尤其是文件上传。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const express = require('express');
const formidable = require('formidable');

const app = express();

app.get('/', (req, res) => {
res.send(`
<h2>With <code>"express"</code> npm package</h2>
<form action="/api/upload" enctype="multipart/form-data" method="post">
<div>Text field title: <input type="text" name="title" /></div>
<div>File: <input type="file" name="someExpressFiles" multiple="multiple" /></div>
<input type="submit" value="Upload" />
</form>
`);
});

app.post('/api/upload', (req, res, next) => {
const form = formidable({ multiples: true });

form.parse(req, (err, fields, files) => {
if (err) {
next(err);
return;
}
res.json({ fields, files });
});
});

app.listen(3000, () => {
console.log('Server listening on http://localhost:3000 ...');
});

body-parser:推荐写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var express = require('express')
var bodyParser = require('body-parser')

var app = express()

// create application/json parser
var jsonParser = bodyParser.json()

// create application/x-www-form-urlencoded parser
var urlencodedParser = bodyParser.urlencoded({ extended: false })

// POST /login gets urlencoded bodies
app.post('/login', urlencodedParser, function (req, res) {
res.send('welcome, ' + req.body.username)
})

// POST /api/users gets JSON bodies
app.post('/api/users', jsonParser, function (req, res) {
// create user in req.body
})

eggjs

框架介绍

Egg.js 是基于 Node.js 和 Koa 的一个企业级应用开发框架,可以帮助团队降低开发成本和维护成本。

eggjs 特性

  • 提供基于 Egg 的定制上层框架的能力
  • 高度可扩展的插件机制
  • 内置多进程管理
  • 基于 Koa 开发性能优异
  • 框架稳定,测试覆盖率高
  • 渐进式开发

搭建 eggjs

1
2
3
4
5
mkdir egg-example && cd egg-example
// simple 模板
npm init egg --type=simple
npm i
npm run dev

目录介绍

  • app 核心目录、业务逻辑、数据库操作
  • config 针对插件配置
  • test 单元测试

thinkjs

nestjs

工具

nvm:node 版本管理工具 ,之前的篇幅有详细介绍过安装以及应用

nvm-windows:Windows 的 node.js 版本管理

pm2:类似 windows 任务管理器

nodemon:检测目录文件更改重启应用

node-supervisor:检测文件重启应用


相关资料
[视频]Node.js 入门
[视频]node.js 入门

Windows下安装及使用NVM
windows上NVM安装与使用
nvm 安装npm不成功,一直没反应

node-lessons/lesson4 at master · alsotang/node-lessons · GitHub (fastgit.org)

Node.js 入门 - cnode

搭建 Node.js 环境 - 开发者实验室 - 腾讯云 (tencent.com)

基于 NodeJs 打造 Web 在线聊天室 - 开发者实验室 - 腾讯云 (tencent.com)

Linux 下部署 Express 环境 - 开发者实验室 - 腾讯云 (tencent.com)

Express 快速入门 - 开发者实验室 - 腾讯云 (tencent.com)

基于 CentOS 搭建 Koa2 框架 - 开发者实验室 - 腾讯云 (tencent.com)

Koa2 免费视频教程
Koa2+Vue2.x 实战电商系统
Nodejs 教程 Nodejs+Koa2 入门实战视频教程
Nodejs 教程 Koa 教程 Koa2 MongoDb 入门实战视频教程

Koa2 进阶学习笔记
Koa.js 设计模式-学习笔记

十个Node.js开发易犯错误之一:不使用开发工具

Egg.js 框架入门与实战

node实现爬虫的几种简易方式
使用 Nightmare 进行浏览器自动化测试
awesome-webgis
爬虫集合 InfoSpider
爬虫集合 awesome-spider

javascript standard style 一种 JavaScript 的代码风格
Uint8Array对象处理二进制数据
Buffer
自定义根证书颁发机构 CA 生成自签名证书
Redis + NodeJS 实现一个能处理海量数据的异步任务队列系统

Node 在有赞的实践 (youzan.com)

《深入浅出Node.js》

《狼书》

作者

Fallen-down

发布于

2020-03-24

更新于

2021-08-11

许可协议

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

评论

You forgot to set the shortname for Disqus. Please set it in _config.yml.
You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.