入门NodeJS不看这篇文章我会伤心的OK?

234 阅读12分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

大致概念咱先统一一下

如果以工作内容对前端进行分类,那我们大概知道,前端,写界面的。

后端,提供数据的。

前端和后端的交互,就用登录来举个例子吧。

用户输入 -> 前端拿到输入数据发给后端 -> 后端接收 -> 后端拿到数据 -> 数据表操作,判断用户存在不存在 -> 如果存在,给前端返回一个结果 -> 前端拿到结果,展示对应的信息或者跳转界面

大概就是这么一个流程。那我们可以发现,前端的主要工作是页面和后端返回数据的处理,后端则是进行接收数据和数据表处理。

前端主要的开发语言是 JS ,后端开发的语言就多了,什么 PHP,Go, Java 等等。NodeJS 应该也算是一种。

那你有没有想过,为什么前端就这么一个语言,而后端有那么多种语言?为什么前端的语言不能操作数据库而后端的语言可以?NodeJS 和 JS 之间到底有什么不可告人的关系?

首先,我们先了解下这个。

Snipaste_2022-04-15_10-59-42.png

代码究竟是怎么在计算机跑起来的?

这个深入理解较复杂,我暂时设定几条既定规则。

我们现在接触到的 JS、Java 等都属于高级语言,能够提高我们的开发效率,但计算机无法直接执行。

高级语言需要转化为机器语言才能被计算机执行。

明确了以上内容,我们可以这么理解,无论是前端还是后端的代码,它都需要一个中间工具进行转换,转换成机器语言后,完成相关的运算,比如基础的加减乘除。

你说,那不对啊?我 js 写完,就直接用浏览器打开了啊?

那有没有一种可能性,浏览器中有一个转换的中间工具呢?那就是 V8 引擎。(V8是一个由Google公司开发的开源JavaScript引擎,使用C++编写

而想要运行 NodeJS ,我们得下载 node 模块搭建 node 运行的环境。我们可以大致理解为,下载的内容也是一个大大的中间工具,当我们完成文件编写点击运行后,这个工具对 NodeJS 文件内容做了一层转换,让它能够顺利被计算机执行。

Snipaste_2022-04-15_10-57-05.png


这和后端语言很多、前端语言很少有啥关系呢?

从功能上来看,JS 开发的目的是能够在浏览器中进行一些校验和数据处理,和界面紧密相连,数据的处理都交由后端来做了,场景相对单一,所以开发语言的类型单一。(我猜的)

而后端想要将数据独立于程序的运行存起来,就必须把修改的数据写入硬盘中,然后对数据进行读取、查询。

所以,后端对于计算机的硬件操作更加频繁。语言种类多,应该是为了照顾各种场景。比如有些语言读写速度快,或者编译速度快等,虽然最后都转化成了机器语言,但侧重点各不相同。(还是我猜的)

Snipaste_2022-04-15_10-55-42.png

NodeJS 和 JS 不可告人的关系

Snipaste_2022-04-15_10-57-32.png

NodeJS 和 js 到底有什么关系呢?它们的关系就是,语法一致。

比如在 c 语言里定义整数得用 int, NodeJS 和 js 里都可以用 let。

这样一来,如果前端想学 JS , 可以说是无缝对接,完全没有戒断期和适应期的。

NodeJS 虽然语法与 Js 一致,但它具备文件读写功能。这就意味着我们可以储存独立的数据文件。

那就让我们把它当作可以用 JS 语法来操作的一个后端环境吧,只要下载了 NodeJS 库,按照一定的要求去写代码,我们就能够使用它搭建服务器,操作数据表,来完成它作为后端的功能。

NodeJS 行不行

Snipaste_2022-04-15_10-55-29.png

那如果用 NodeJS 作为广义上的后端来开发项目,真的 OK 吗?

知己知彼,百战不殆。我们先来了解一下其他后端语言在开发商有什么优势。

以下内容来自于我的后端朋友,他说他编的。

C和C++,一般在嵌入式、游戏和操作系统中使用,因为这类场景对性能要求很高,C和C++性能本身就比较高;

做算法和科学技术很多使用Python,因为Python比较接近自然语言,而且够简洁,一般研究人员编程不熟练,所以选用Python比较多;

做Windows开发很多时候选用C#,.net, VB,因为这些是微软自己设计的语言,自然就比较适合进行Windows桌面程序开发;

Go 语言是谷歌 2009 年发布的第二款开源编程语言,它专门针对多处理器系统应用程序的编程进行了优化,它是一种系统语言其非常有用和强大,其程序可以媲美 C 或 C++ 代码的速度,而且更加安全、支持并行进程。


相比以上语言,NodeJS 作为后端语言的优势在哪呢?

完成服务器的搭建以后,一个服务器往往连接多个客户端。如果我们把服务端的内存看成一个房子,那么有些后端语言处理方式就是,一个客户端连接给它配一个服务人员,一对一,一对一的满足上门的任务要求(多线程处理)。

而 NodeJS 的处理方式是只派一个人,给上门的客户记录一下需求,转接到下一个处理流程,然后接待下一个客户。如果某个处理流程完了,就给对应客户通知一声,把结果给它。(单线程,异步)。

要知道,一个人就得占一个位置。这样子,NodeJS 的优势就体现出来了,就是它人少,所以它能用尽可能少的内存,连接尽可能多的客户。

所以,支持大量用户的并发连接,NodeJS 有优势。


因为它就那么一个人,不存在两个人都需要对方的资源才能执行下一步人物的情况(死锁)。但是这样有个缺点。如果使用多个人服务多个客户,如果有个客户特别难缠(计算量大),它非要你费好久才能理解它真正的需求,虽然耽误一个服务人员的时间,但是并不影响其他服务人员服务其他客户。

而 NodeJS 这个时候就麻烦了,它不得不处理,这么一处理,就耽误了后面的客户。


那么,什么软件,一起用的人又多,数据不需要经过大量计算、主要是写入读出呢?

没错,聊天软件。

还有那种专门提供后端接口的网站,也不错。主要是接到请求,返回数据。

游戏也会用到 NodeJS ,比如 Pinus, 它就是基于node.js的高性能,分布式游戏服务器框架。

毕竟。NodeJS 的底层是 C/C++ 编写的,所以它的执行速度有保障。

所以 NodeJS 行不行取决于它的应用场景,毕竟合适的才能长久。

Snipaste_2022-04-15_11-02-47.png

NodeJS 不会告诉你,它还偷偷做了些什么

我们还是从熟悉的 JS 切入。

最开始接触前端,引用 JS 都是通过 script 标签。

这种形式在开发中带来的问题是,无论是自己写,还是引用,命名冲突带来的问题只有你想不到,没有它不存在。

而 Java 早就有了类文件。大学写 java 的时候,一个类文件我能够放置指定的数据和功能,在其他文件中引入该文件,进行下一步操作。在写的过程中,不仅能够规避命名问题,也能够让开发思路更加清晰、维护更加方便。

对比之下,js 看起来就有那么一点不上档次。在大型开发、复杂开发、多人开发上落后一截。随着业务越发复杂,参与人数变多,以前那种简单的小打小闹催生的问题越来越多。在这种情况下,出现了 CommonJS。

CommonJS

什么是 CommonJS ? 它到底怎么用?它的适用场景是什么?

相信大多数人接触到这个名词的时候,脑子里都和我一样,出现了疑问三连。

先来看一段来自于 CommonJS 规范 · Webpack 中文指南 的介绍:

CommonJS 是以在浏览器环境之外构建 JavaScript 生态系统为目标而产生的项目,比如在服务器和桌面环境中。

这个介绍至少可以解答我们的一个疑问:适用场景。

即,被 浏览器解析的 js 文件 不支持。


那么,它是个什么东西?

CommonJS 是一套规范,它的创建和核准是开放的。这个规范已经有很多版本和具体实现。

这个网站上的介绍,我们把它翻译一下,就是它并不是我们平时写浏览器端 JS 所理解的那种可以引用的库,不是一种事物,而是一种规范。比如学校规定早上八点钟上课,有些人骑车上学,有些人走路上学,有些人不上学。

那么八点钟上课,就是学校出的一个规范,这些去上学的人都支持这个规范,虽然遵守方式(实现过程) 不一样,但是他们最终表的结果:八点钟到学校 是一致 的,我们可以基于 一致的结果 再进行下一步的安排。很明显,不上学的人就是不支持。

比如浏览器,你用 commonJS 规定的语法去引入模块,浏览器读不了,因为它不遵守规范,所以没有结果。

NodeJS 项目严格遵守了这种规范。我们可以理解为,NodeJS 整个环境,它提供了一个接口或者怎么样,它能够读懂我们按照规范去写的代码,然后实现我们 使用这种规范 想要达到的运行结果。


说了这么多,相信你应该差不多理解了 commonJS 和 NodeJS 之间的关系。

那它在代码中是怎么体现的呢?

// moduleA.js
module.exports = function( value ){
    return value * 2;
}
// moduleB.js
var multiplyBy2 = require('./moduleA');
var result = multiplyBy2(4);

最直观的体现是,它规定了导入模块和导出模块的写法。易于理解,也并不难记。

值得一提的是,CommonJS 是同步加载模块。就是说,如果同事写下 10 个 require,它会等到上一个模块加载完成之后再加载下一个模块。

这种看似简单的规范,意义非凡。我们不用再纠结于命名冲突,也重塑了我们模块化的思考方式 ,将功能模块化,然后分而治之。制定了这种规范后,我们可以就可以建立通用工具库,甚至基于这种规范,我们可以使用别人写好的工具库。而无论我们具体是怎么实现的,它的入口和出口是一致的。我们只需要遵从规范去调用它即可。

NodeJS 异步编程风格

同步和异步简述

什么是同步,什么是异步?

同步就是烧水的时候等着,等水烧开了我把它倒壶里了我才能扫地。任务必须等上一个完了,我才做下一个。

异步就是烧水的时候边扫地,扫着扫着,我听到水烧开的声音,我就先跑去把水倒了,完了我接着扫地。

即,当一个任务 A 需要等待的时候(发起请求),我先做点其他BCD任务。等到我得到一个信号,告诉我A任务做完了,我就接着做 A 任务。

NodeJS 的异步实现

如果你是按顺序阅读本文,那么从上文我的比喻中,我们可以意识到 NodeJS ,采用的是 异步。

那么 ,NodeJS 是怎么实现异步的呢?

主要有三种方式。

回调Promiseasync/await

前两种我就不做过多的叙述了,主要是它们的写法就是很容易让人理解这是个异步。而最后一种,它的写法,看起来像个同步。

虽然我在上文中举的例子让同步看起来像是一个不知变通的智障,不过比喻就是比喻实际是实际,比喻是为了让我们理解它的工作方式而不是鄙视之一。

在实际的编程里,还是同步好啊,好写啊,隐藏的问题少啊。

我举个例子,比如

data = 请求结果 R
拿到 R 做一个循环 得到 C
打印 C

这个看起来没啥毛病,但是!这个书写方式是同步的书写方式!同步会等到 data 拿到值以后再进行下一步,这个时候,同步其实更加符合我们的思考\书写习惯。

放异步呢?它可不管你,先把 C 打印出来再说。凡是依赖 data 的数据通通 GG!

虽然回调可以解决这个问题,但是依赖一多,回调就跟俄罗斯套娃似的,没完没了。写起来恶心看起来也恶心。

所以,出了 Promise 来解决这个问题,而 async/await 的写法则进一步贴进了同步的写法。

贴一段代码:

 const getDetailInfo = async () => {
        try {
        	const { data } = await http.post(API.WECHAT.getWeChatcfg());
            const { secret } = data || {};
			console.log(secret)
        } catch (error) {
            Toast.show(error);
        }
    };

怎么样,是不是看起来跟同步的思路非常相似?

虽然它写起来是个同步,但是并不改变 NodeJS 异步的核心哈,它只是把最开始的写法改了又改最终给我们写法上的方便,就好像经过厂家的努力,大豆吃起来具备肉的口感。

Snipaste_2022-04-15_11-03-13.png

结语

你可能说,我*,怎么就结语了呢?不是说 Talk is cheap. Show me the code.(能动手不要动口)。

但是又说了,欲练此功,必先 …… 啊不,必先 磨刀,因为磨刀不误砍柴功。

我们只有了解了关于 NodeJS 的前世今生,我们才能消除对于它的那份陌生,后面进行下一步的时候才能更顺畅不是?

刀都磨了,砍柴还会远吗?下一篇,我们直接动手!

如果这篇文章对你有帮助,记得给我点赞噢~ 不然我会伤心的 ,OK?

Snipaste_2022-04-15_10-52-16.png