一文简单入门Node.js
前言
JavaScript语言是网站开发必不可少的语言,只要有浏览器就能运行起来JavaScript代码。另外JavaScript还能单独运行在Node.js环境中,本文章重点介绍Node.js相关的基础内容,为深入了解Node.js铺路,后续会更新Node.js深入介绍。
Node.js安装及使用
安装很简单,在官网下载安装就行了,地址是 https://nodejs.org/zh-cn 。安装完后在控制台输入node -v 后能有显示则证明安装成功,这个时候可以开始各种倒腾了。
root@localhost:~$ node -v
v16.17.0
可以创建一个JavaScript文件,用node命令来执行。
root@localhost:~$ echo 'console.log("hello world!")'> helloworld.js
root@localhost:~$ node helloworld.js
hello world!
Node.js源码初步分析
Node.js是怎么实现JavaScript代码执行的呢?Node.js源码地址是 https://github.com/nodejs/node/tree/main ,查看源码我们可以看到deps文件夹下面有很多依赖组件,其中最为核心的依赖组件是v8和uv文件夹。
├── deps
│ ├── acorn
│ ├── base64
│ ├── brotli
│ ├── cares
│ ├── cjs-module-lexer
│ ├── corepack
│ ├── googletest
│ ├── histogram
│ ├── icu-small
│ ├── llhttp
│ ├── nghttp2
│ ├── ngtcp2
│ ├── npm
│ ├── openssl
│ ├── undici
│ ├── uv
│ ├── uvwasi
│ ├── v8
│ └── zlib
组件名 | 功能描述 |
---|---|
v8 | V8 是 Google 发布的开源 JavaScript 引擎,采用 C++ 编写,在 Google 的 Chrome 浏览器中被使用。用于编译和执行JavaScript源代码,处理对象的内存分配,以及进行垃圾回收。 |
uv | uv即为libuv,libuv 是一个多平台支持库,专注于异步 I/O,集成该组件能够更简单的实现异步I/O的服务端开发,同时该组件还为Node.js提供线程池,文件系统等功能。它本来就是为Node.js开发的,但它也被Luvit,Julia,uvloop和其他集成使用。 |
nghttp2 | nghttp2 是一个用 C 实现的 HTTP/2 库,支持 h2c,为其提供 HTTP/2 相关功能。 |
cares | c-ares 是一个异步DNS解析器库。它为Node.js提供异步DNS并行查询功能。 |
Node.js基于v8 JavaScript 引擎和libuv实现了单线程/异步IO的JavaScript运行环境,显然没有线程资源抢夺场景,但是Node.js核心亮点不在于提供单线程JavaScript运行环境,而在于实现了跨平台的异步IO,其中linux系统下通过IO多路复用(epoll)+线程池来实现,Mac OS X系统下则是通过IO多路复用(kqueue)+线程池来实现,由于IO多路复用的实现依赖系统底层的实现,所以实现方法不一样。严格来说在linux和Mac OS X使用的都是同步IO,因为IO多路复用(select,poll,epoll等)就是同步IO,只是引入了线程池模拟实现了异步IO,让大家能够轻松使用异步语法(回调函数的处理),以下是官网提供的libuv实现异步IO的技术架构图。
由于JavaScript程序是单线程执行,代码肯定要有一定的执行顺序逻辑,定时器以及异步的回调函数何时执行也有讲究,Node.js提供了event_loop功能(事件循环机制)来判断什么时候执行。
比如下面的代码,输出的顺序是1,4,2,3。
console.log('1');
setTimeout(() => {
console.log('2');
}, 1);
setTimeout(() => {
console.log('3');
}, 1);
console.log('4');
比如下面的代码,输出的顺序是2,1。
setTimeout(() => {
console.log(1);
}, 1);
/*模拟费时间的cpu计算,也可以用其他真实耗时的算法,比如计算素数和DFS全排列等*/
let now = new Date();
while(new Date () - now < 5*1000){}
从以上2个case可以总结出以下执行规则。
1.同步代码执行优先级都比setTimeout高。
2.setTimeout的定时不精准,会被其他同步代码影响。单线程定时器出现这个问题不出意外。
要了解更多的执行顺序的规则,就必须了解Node.js的事件循环机制。
Node.js事件循环机制
根据上面的case,可以大概得出Node.js的执行代码逻辑。
1.主线程执行同步代码。
2.主线程将异步任务插入到待执行任务队列中。
3.经过事件循环逻辑处理,按优先级执行队列里面的任务。
上面的逻辑应该比较容易理解,以下是官网提供的事件循环具体执行细节,包含了6个执行阶段。
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
阶段概述
定时器:本阶段执行已经被 setTimeout() 和 setInterval() 的调度回调函数。
待定回调:执行延迟到下一个循环迭代的 I/O 回调。
idle, prepare:仅系统内部使用。
轮询:检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞。
检测:setImmediate() 回调函数在这里执行。
关闭的回调函数:一些关闭的回调函数,如:socket.on(‘close’, …)。
这些概述来自官网:https://nodejs.org/zh-cn/docs/guides/event-loop-timers-and-nexttick/。
Node.js工程实践
Node.js版本管理
平时开发过程可能涉及不同的Node.js版本切换,可以通过安装nvm来进行管理,下载地址是https://github.com/nvm-sh/nvm ,下面是一些相关的命令例子。
root@localhost:~$ nvm use 16
Now using node v16.15.1 (npm v8.11.0)
root@localhost:~$ node -v
v16.15.1
root@localhost:~$ nvm install 12
Downloading and installing node v12.22.12...
Downloading https://nodejs.org/dist/v12.22.12/node-v12.22.12-darwin-x64.tar.gz...
########################################################################################################################### 100.0%
Computing checksum with shasum -a 256
Checksums matched!
Now using node v12.22.12 (npm v6.14.16)
root@localhost:~$ node -v
v12.22.12
Node.js 工程依赖包管理
Python工程可以通过pip工具来管理依赖包,JAVA工程可以通过maven工具来管理依赖包,自然Node.js也得有个依赖包管理工具。Node.js则是用npm来管理工程依赖包。
创建一个文件夹,然后cd进去执行npm init就是初始化Node.js工程,同时会在本地生成package.json文件。
mkdir app && cd app && npm init
package.json文件内容如下
{
"name": "app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
可以通过npm install来安装其他的依赖包,如下安装koa(基于Node.js的web开发框架)。
npm install koa --save-dev
package.json文件更新如下
{
"name": "app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"koa": "^2.13.4"
}
}
No de => De no?
和Node一样提供JavaScript运行环境的还有一个叫Deno的程序,从名字上来看就是Node按No de的倒序,Deno是一个简单、现代和安全的 JavaScript、TypeScript 和 WebAssembly 运行时,它使用 V8 并在 Rust 中构建,具体可以查看官网安装和使用。
https://deno.land/
未完,持续更新。