1、前言
接触 node.js 有好几年了,也开发了好几个项目了;但每次提起 node.js,始终不敢说自己入门了,归其原因,大概就是如下所示
为了进一步加强理解,系统的梳理相关知识以作备忘,本节将从以下几个方面介绍 node.js
- 为什么前端需要掌握?
- 什么是 node.js?
1.1、为什么前端需要掌握
Node.js 单线程高并发的特性非常适合用来创建后端服务;虽然但是,好像不会使用 Node.js 根本不影响我写前端页面嘛~。
no no no,作为一个新时代的三有
前端,要本着知其然并知其所以然的精神彻底掌握 Node.js,因为它不仅仅是用来写后端,更重要的用途是提升我们项目工程化和效率。
- 操作文件系统;在日常开发,经常会涉及到文件夹操作;比如某个需求,给你几百个资料,需要你做成资料下载页,妈妈呀,手都要复制麻了,赶紧写一个 DOS 批处理命令,尴尬了,还是上学那会学过一点,早忘了七七八八了;不慌,Node.js 来帮忙,fs 模块轻松解决。
- 开发自己的命令行工具;典型的就是 vue2 store 中在仓库增加一项,需要同时在 state、mutation、action 增加对应的模型操作,非常繁琐,如果你掌握了 Node.js,就可以开发一个自己的命令,一键添加。
- 编写打包脚本工具,如 webpack 的 loader,嘿嘿,这个熟悉吧
- 发布自己的 npm 包,搭建各项目通用基座,告别 Ctrl + C、V
- 实现自己的脚手架,再也不用整一个模板项目然后每次复制了
你自己也会写 npm 包、脚手架、打包脚本了,是不是很受诱惑,嘿嘿…
2、node.js 的概念和特性
Node官网对其进行了简单的不能再简单的描述
Node.js 是一个开源的、跨平台的 JavaScript 运行时环境。
翻阅官网教程,大致对其概念和特性有了一些认识:
- Node.js 是一个服务端运行环境,它使用 Google 的 V8 虚拟机来解释和执行 JavaScript 代码。
- Node.js 同时又是一个库,具有许多有用的模块,可以简化操作。
- 当 Node.js 执行 I/O 操作时,如从网络读取、访问数据库或文件系统,Node.js 不会阻塞线程,而是通过事件回调机制来完成回调处理。
- Node.js 应用在单个进程中运行,无需为每个请求创建新线程,这使得 Node.js 在构建高并发应用时非常得心应手(如实时聊天、秒杀、抢购等)。
2.1 Node.js VS 浏览器
同样是解析 JavaScript,为啥 Node.js 与浏览器的功能差别这么大?
在 Node.js 之前,JavaScript 主要运行在浏览器宿主环境中,主要是操作 DOM、BOM,浏览器提供了 document、window 等对象;
Node.js 则允许在服务端运行 JavaScript,不具有浏览器才有的 window、document 等对象,主要是操作磁盘文件、搭建 http 服务等等等等…
这么牛逼?提供各种模块,还能在各大平台运行的飞起,这是我了解的 JavaScript?其本质不是 JavaScript 有多牛逼,而是它封装了许多强大的依赖,以下是 Node 源码的依赖列表
最典型的依赖就是 v8 和 uv
- v8 引擎: 将 js 代码转变成可以在各个平台和机器上运行的机器码
- libuv:C 语言编写的库,调用平台上的各种系统特性,如文件系统;同时负责 Node API 的执行,它将不同的任务分配给不同的工作线程(work threads),通过多线程同步阻塞执行,模拟异步处理机制,成功后将回调函数放入 Event Queue
- npm: Node.js 包管理和分发工具,提供各种非官方的 Node 模块
上图是 libuv 大致的结构,很明显,Node.js 的网络 I/O、文件 I/O、DNS 操作、还有一些用户代码都是在 libuv 工作的
2.2 异步 I/O
libuv 的存在,使得 Node.js 可以是单线程的。每当有 I/O 请求发生时,node 就会扔给 libuv,由 libuv 为该请求分配给不同的工作线程(libuv 默认维护 4 个线程供 Node 调用,可以通过 UV_THREADPOOL_SIZE 环境变量在 Node 启动时设置,为了性能,不能超过 128 个),同步阻塞性的执行,执行完成后将回调函数放入事件队列中。
而 node 则不会管这个 I/O 的操作过程,继续执行主线程上的事件,只需要在该请求返回回调时在处理即可,从而实现非阻塞性的异步 I/O 处理。
2.3 单线程&高并发
与 JavaScript 一样,Node 是单线程,它将阻塞性的操作丢给 libuv 提供的线程池。
单线程的优点:
- 避免了多线程编程中可能出现的竞态条件、锁和死锁等问题,简化了编码过程
- 由于每个线程都将暂用一定的内存,在
服务器内存受限
的情况下,具有更好的高并发
单线程的缺点:
- 不适合大量计算,会占用 CPU 导致应用阻塞(在主线程上避免大量计算)
- 错误会引起整个应用的退出(可以利用 pm2 或 nginx 来监测服务状态,自动重启)
- 容易受到阻塞性操作的影响;Node 主线程上如果有同步阻塞性 I/O 操作,可能会导致阻塞,这也就要求我们尽可能的使用 Node.js 的异步编程,异步编程是 Node.js 的核心
2.3.1 线程和进程
概念:线程是进程内的最小执行单元。一个进程可以有一个或多个线程。所有线程共享该进程的地址空间和资源。
进程与进程之间相互独立,通讯困难;而线程与线程则共享进程的资源,通讯简单;
Node.js 本身运行在一个进程中,当我们说 Node.js 是单线程时,是指它在这个进程中只使用一个线程来执行 JavaScript 代码
Node.js 只有一个主线程,是单线程;但并不是说 Node 只支持单线程;如果想要创建新的线程,可以使用 Node.js 内置的 Worker threads 模块
如果想要创建新的进程,可以使用 Node.js 内置的 Cluster 、Child process 模块。
2.3.2 为什么单线程并发高?
传统的服务端语音中(如 Java),每一个客户端连接都会为其创建一个新的线程,而每个线程都需要一定的内存(1~2M),受服务器内存限制,成为了高并发连接的瓶颈。
Node.js 则只有一个线程,大大的节省了服务器内容,它通过为每一个用户连接在 Node.js 内部,触发一个事件,Node.js 高并发的特性使得它在构建在线聊天、秒杀、抢购等应用时具有非常大的优势.
当然,这是建立在内存受限的情况下,如果你有足够大的服务器内存;那么传统的服务端语言将拥有更快的响应速度,Node 的事件循环机制将可能出现等待,即主线程有同步阻塞任务,事件循环将被阻塞
2.4 事件循环
Node.js 通过异步回调来处理,那么 Node 如何知道请求或操作完成了添加回调;又是如何执行回调的?这是由于 Node.js 的另一特性:事件循环机制
,主线程通过 Event Loop 事件循环触发的方式来运行程序
注意,Node.js 的事件循环与浏览器下 JavaScript 的事件循环机制是不一样的,具体参考浏览器事件循环 VS Node.js 事件循环
3、结束语
好消息:今天周三了
坏消息:今天才周三
按道理,我没记错的话,朋友,今天应该是周五的吧~
有多少软件工程的小伙伴被戳中了,哈哈