写在前面

Node.js,顾名思义,是基于Chrome V8引擎的异步/非阻塞JavaScript运行环境(解析器).
讲简单点就是跑在服务器端的JavaScript.


0x01 安装与REPL使用

.1 安装

Linux下安装

用自带的包管理器安装即可.
这里以Ubuntu 20.04 LTS为例:

apt install nodejs && apt install npm

然后执行 node -vnpm -v,看到输出就说明安装成功.
然后鉴于apt默认源里NodeJs并非最新版,另外官方源速度太慢,我们可以安装NodeJs的版本管理器和软件源工具:

npm cache clean --force #清除缓存
npm install -g n
npm install -g nrm

然后执行

n stable # 安装最新稳定版
nrm use taobao # 将软件源设定为淘宝源(https://registry.npm.taobao.org/)

Windows下安装

由于种种原因并不推荐在Windows环境下直接安装NodeJs进行开发和调试
所以啊,亲爱的少年——
给我乖乖地装好WSL在Linux下code吧!
才不是我懒得在Windows下安装然后截图呢

.2 使用

NodeJs REPL(交互式环境)

NodeJs像Python一样提供了基于V8(不是Vtuber吧)引擎的交互式编程环境
真是开发的好工具呢(赞赏)
在命令行环境下输入node即可进入REPL
推荐第一次进入REPL时执行.1 + .2
REPL中可以定义并启动服务器:

var http = require('http')
http.createServer(function(req, res){  
...    res.writeHead(200, {'Content-Type':'text/html'});
...    res.end('hello world');       
...  }).listen(8080, "127.0.0.1")

此外,REPL还有一些基本的快捷键与命令:

  • Ctrl+C: 退出当前终端
  • 两次Ctrl+C: 退出REPL。
  • Ctrl+D: 退出REPL.
  • : 查看历史命令
  • Tab: 列出当前命令
  • .help : 帮助
  • .break: 退出多行表达式
  • .clear: 退出多行表达式
  • .save [name]: 保存当前的 Node REPL 会话到指定文件,[name]为文件名,可变
  • .load [name]: 载入保存过的REPL会话的文件,[name]为文件名,可变

0x02 Node.js函数、回调函数与事件循环

.1 函数

Node.js中的函数定义与JavaScript无异:

function myFunction(x) {
    return x * x;
}

特性(?)也与JavaScript类似:

function myFunction(x) {
    return x * x;
}

function debug(functionName, value) {
  functionName(value);
}

debug(myFunction,1);
debug(function(x){ return x * x},2);  //这是一个匿名函数

.2 回调函数

前置概念

这里写的好乱...而且错误不断...等我过几天单独整理一篇文章出来,你可以暂时跳过这部分...

在了解回调函数之前,我们先来复习一下同步/异步、阻塞/非阻塞和回调的概念:
首先:

  • A "callback" is any function that is called by another function which takes the first function as a parameter. (回调就是在一个函数中调用另一个函数)
  • JavaScript是一门单线程的语言,

同步/异步

同步的概念很好理解: 调用者主动等待这个调用的结果,例如:

function myFunction(x){
    console.log(x);
}

myFunction("Hello World!");

在你调用myFunction到输出"Hello World!"的这段时间,你在处理完毕之前等待系统处理这个调用,然后得到返回值.
而异步则是调用后直接返回(没有返回数据),待到处理完通过状态或回调函数等通知调用者
老师给我们举过这么一个例子:

你打电话给书店老板,问这个月的《知乎故事汇》到了没,书店老板的回应方式:
同步: "我找找,你先别挂",然后开始找书,经过了可能是两秒,也可能是一天,找到了,在电话中回应你.
异步: "我找找,找到了给你打电话",然后开始找书,找到了之后电话通知你.

所以同步和异步关注的是书店老板(被调用者)的状态.
异步函数必须指定回调函数,异步编程依托于回调函数实现

阻塞/非阻塞

还是书店老板的例子:
在书店老板开始找书让你等着的时候,你选择了:

  • 阻塞: 等待直到老板通知你
  • 非阻塞: 去做其他事情

阻塞/非阻塞关注的是你(调用者)的状态
阻塞/非阻塞与同步/异步无关

前面提到过Node.js异步编程,而异步编程的直接体现就是回调函数,回调函数一般作为函数的最后一个参数.
下面提供了一个非阻塞编程的实例:
创建文件file.txt,内容如下:

Hello World!!

app.js,内容如下:

const fs = require('fs');  //引入文件系统

fs.readFile('file.txt', function (err, data) {
    if (err) return console.error(err);
    console.log(data.toString());
});


console.log("Finish(?");

运行结果如下:

> node app.js
Finish(?
Hello World!!

不需要等待文件读取完,在读取文件时执行接下来的代码.

.3 事件循环

当一个异步事件发生时,将会生成一个事件观察者,然后线程将进入一个类似while(true)的循环之中,直到没有事件观察者时退出.
例子如下:

var events = require('events');
var eventEmitters = new events.EventEmitter();
var connectHandle = function connected(){
    console.log('再执行eventHandle');
    eventEmitters.emit('data-receive')
}

eventEmitters.on('data-receive',function(){
    console.log('最后接收数据');
})

eventEmitters.on('connection',connectHandle);
console.log('先执行connection');
eventEmitters.emit('connection');
console.log('程序处理完成');

0x03 GET/POST请求

util模块下篇再介绍...(咕咕预定)

.1 GET请求:

//引入模块
var http = require('http');
var url = require('url');
var util = require('util');
 
http.createServer(function(req, res){
    res.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'}); //向请求写入相应头
    res.end(util.inspect(url.parse(req.url, true))); //解析URL内容
    var params = url.parse(req.url, true).query;
    res.write(params.a);
    res.end();
}).listen(8080);

你现在可以向http://localhost:8080/?a=Hello World!发送一个GET请求查看输出

.2 POST请求

var http = require('http');
var querystring = require('querystring');
var util = require('util');
 
http.createServer(function(req, res){
    // 定义了一个post变量,用于暂存请求体的信息
    var post = '';     
 
    // 通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中
    req.on('data', function(chunk){    
        post += chunk;
    });
 
    // 在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。
    req.on('end', function(){    
        post = querystring.parse(post);
        res.end(util.inspect(post));
    });
}).listen(8080);

Node.js默认不会解析POST请求体