掌握Node.js,迈向全能开发者之路!

发表时间: 2023-07-07 16:55

“这里是云端源想IT,帮你轻松学IT”

嗨~ 今天的你过得还好吗?

慢慢来

谁还没有一个努力的过程


- 2023.07.07 -


Node.js发布于2009年5月,由Ryan Dahl开发,是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O模型,让JavaScript 运行在服务端的开发平台,它让JavaScript成为与PHP、Python、Perl、Ruby等服务端语言平起平坐的脚本语言。

今天我们就一起来认识一下前端开发学习中的第一门后台语言:Node.js。据说学会它,成为全栈开发指日可待哦!



一、什么是Node.js

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境(后端的)。

运行环境包括:V8引擎、内置API (通俗讲:在待执行的JavaScript带,调用内置API,由V8引擎解析执行)。

注意:

  • 浏览器是 javaScript 的前端运行环境;
  • Node.js 是 JavaScript 的后端运行环境;
  • Node.js 中无法调用 DOM 和 BOM 等 浏览器内置 API。


浏览器环境与Node环境对比

从图上看,好像Node环境就只是浏览器环境的一部分,但其实不然,在浏览器环境中我们很难触控到操作系统那一层,这是因为具有浏览器安全级别的限制(不限制的话,浏览器上的网页就能直接操控操作系统,这还能了得? )



Node js作为JavaScript的服务端运行环境,它可以解析JavaScript代码 (没有浏览器安全级别的限制),并能够为其提供很多系统级别的API,如:

  • 系统文件的读写( fs)
  • 系统进程的管理( process )
  • 网络通信( HTTP/HTTPS )

但这也同样代表在Node . js中我们无法使用浏览器提供的API,如window对象,更不能操作DOM元素。


搭建Node环境

如果你曾使用过npm,或者前端框架,相信你已经搭建好了"Node的环境,这里对没有搭建过的朋友们,再次演示一下:

1)打开Node官网:

2)推荐下载这个长期维护的稳定版本,下载完成后- 路Next安装即可。

3)之后在任意位置终端运行: node -v

4)出现版本号就代表我们已经安装成功了。


二、为什么要用node.js

JavaScript语言本身是完善的函数式语言,在前端开发时,开发人员往往写得比较随意,让人感觉JavaScript就是个“玩具语言”。无法像其他编程语言一样满足工程的需要。

但是,在Node环境下,通过模块化的JavaScript代码,加上函数式编程,并且无需考虑浏览器兼容性问题,直接使用最新的ECMAScript 6标准,可以完全满足工程上的需求。


三、Node.js的特点

1、事件驱动

Node.js的设计思想中以事件驱动为核心,它提供的绝大多数API都是基于事件的、异步的风格。

事件驱动的优势在于充分利用了系统资源,执行代码无须阻塞等待某种操作完成,有限的资源可以用于其他任务。在服务器开发中,并发的请求处理是一个大问题,阻塞式的函数会导致资源浪费和事件延迟。

通过事件注册、异步函数,开发人员可以提高资源的利用率,性能也会改善。


2、异步、非阻塞I/O

在Node.js提供的支持模块中,包括文件操作在内的许多函数都是异步执行的。同时为了方便服务器开发,Node.js的网络模块特别多,包括HTTP、DNS、NET、UDP、HTTPS、TLS等。开发人员可以在此基础上快速构建Web服务器。

异步I/O的大致流程为:

1)发起I/O调用

  • 用户通过JavaScript代码调用Node核心模块,将参数和回调函数传入核心模块。
  • Node核心模块会将传入的参数和回调函数封装成一个请求对象。
  • 将这个请求对象推入I/O线程池等待执行。
  • JavaScript发起的异步调用结束,JavaScript线程继续执行后续操作。

2)执行回调

  • I/o操作完成后会将结果存储到请求对象的result属性上,并发出操作完成的通知。
  • 每次事件循环时会检查是否有完成的I/O操作,如果有就将请求对象加入I/O观察者队列中,之后当作事件处理。
  • 处理I/O观察者事件时会取出之前封装在请求对象中的回调函数,执行这个回调函数,并将result当作参数,以完成JavaScript回调的目的。


3、性能出众

Node.js在设计上以单进程、单线程模式运行。事件驱动机制是Node.js通过内部单线程高效地维护事件循环队列来完成的,没有多线程的资源占用和上下文切换。这意味着面对大规模的HTTP请求,Node.js是凭借事件驱动来完成的。


4、单线程

Node.js和Nginx一样,都是以单线程为基础的。这正是Node.js保持轻量级和高性能的关键。这里的单线程是指主线程为“单线程”,所有阻塞的部分交给一个线程池处理,然后这个主线程通过一个队列跟线程池协作。

我们写的js代码部分不用再关心线程问题,代码也主要由一堆callback回调构成,然后主线程在循环过程中适时调用这些代码。

单线程除了保证Node.js高性能之外,还保证了绝对的线程安全,使开发者不用担心同一变量同时被多个线程读写而造成的程序崩溃。


5、Node.js的应用场景

01JSON APIs

构建一个Rest/JSON API服务,Node.js可以充分发挥其非阻塞IO模型以及JavaScript对JSON的功能支持(如JSON.stringfy函数)。

02单页面、多Ajax请求应用

如Rmail,前端有大量的异步请求,需要服务后端有极高的响应速度。

03基于Node.js开发UNIX命令行工具

Node.js可以大量生产子进程,并以流的方式输出,这使得它非常适合做UNIX命令行工具。

04流式数据

传统的Web应用,通常会将HTTP请求和响应看成原子事件,而Node.js会充分利用流式数据这个特点,构建非常酷的应用,如实时文件上传系统transloadit。

05准实时应用系统

如聊天系统、微博系统,但JavaScript是有垃圾回收机制的,这意味着系统的响应事件是不平滑的(GC垃圾回收会导致系统这一时刻停止工作)。如果想要构建硬实时应用系统,Erlang是一个不错的选择。

Node能处理万条入站tweet。它能快速而又轻松地将它们写入一个内存排队机制(如memcached),另一个单独进程可以从那里将它们写入数据库。Node能处理每个连接而不会阻塞通道,从而能够捕获尽可能多的tweets。



当然了,Node.js也不是十全十美的,它也存在一些缺点,比如:CPU密集型的应用、模板渲染、压缩/解压缩、加/解密等操作。


四、node.js 中的npm

1、npm 是什么

npm(即 node package manager )是Node的包管理工具,能解决NodeJS代码部署上的很多问题,到目前为止,npm差不多收集了60万个别人写好的包。

其实每个包就是一个功能,一个需求,npm里面有关于创建服务器的包,启动电脑摄像头的包,如果正好我们有这些需求,那么我们就没必要自己去写代码,完全可以用别人已经写好的包。


npm是随同Nodejs一起安装的包管理工具,能解决Nodejs代码部署上的很多问题,常见的使用场景有以下几种:

  • 允许用户从NPM服务器下载别人编写的第三方包到本地使用。
  • 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
  • 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。



由于新版的nodejs已经集成了npm,所以之前npm也一并安装好了。同样可以通过输入" npm -v " 来测试是否成功安装。

命令如下,出现版本提示表示安装成功:

如果安装的是旧版本的 npm,可以很容易得通过 npm 命令来升级,命令为: c:\ npm install npm -g


2、npm 命令安装模块

npm 的包安装分为本地安装(local)、全局安装(global)两种,

命令如下:

npm install express //本地安装

npm install express -g //全局安装

如果出现以下错误:

npm err! Error: connect ECONNREFUSED 127.0.0.1:8087

解决办法为:

$ npm config set proxy null



1)本地安装

  • 将安装包放在 ./node_modules 下(运行 npm 命令时所在的目录),如果没有 node_modules 目录,会在当前执行 npm 命令的目录下生成 node_modules 目录。
  • 可以通过 require() 来引入本地安装的包。


2)全局安装

  • 将安装包放在 /usr/local 下或者你 node 的安装目录。
  • 可以直接在命令行里使用。
  • 如果你希望具备两者功能,则需要在两个地方安装它或使用 npm link。


3、选用 cnpm

1)cnpm 的官方介绍是:cnpm是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读),同步频率目前为 10分钟 一次以保证尽量与官方服务同步。既然都一样,那么cnpm为什么要出现呢?

由于npmjs.org的服务器在国外(即在“墙”外),国(墙)内开发者做项目的时候,很多“包”的下载速度极慢,在这种环境下阿里巴巴为了众多开发者的便捷便挺身而出推出了淘宝镜像(即cnpm),它把npm官方的“包”全部搬到国内,供广大开发者使用。

官网地址为: http://npm.taobao.org



1)使用淘宝镜像的命令:

npm install -g cnpm --registry=https://registry.npmmirror.com

2)安装cnpm,使用命令:

npm install cnpm -g --registry=https://registry.npm.taobao.org

安装完成后用 cnpm -v 查看是否安装成功。

3)cnpm的使用方法和npm完全一致,只需要把npm改成cnpm即可。


五、Node的模块化

node中的模块分为三大类:

  • 内置模块:由 node.js 官方提供的,例如 fs, path, http 等。
  • 自定义模块:用户创建的每一个 .js 文件,都是自定义模块。
  • 第三方模块:由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要下载。

1、 使用 require() 方法,可加载三大类模块

当使用 require() 方法加载其它模块时,会执行被加载模块中的代码。

// 加载内置的模块

const fs= require('fs')

// 加载用户的自定义模块

const custom = require('./custom.js')

// 加载第三方模块

const moment= require('moment')



2、 module.exports 对象

在自定义模块中,可以使用 module.exports 对象,将模块内的成员共享出去,供外界使用,外界用 require() 方法导入自定义模块时,得到的就是 module.exports 所指向的对象

注意:使用 require() 方法导入模块时,导入的结果永远以 module.exports 指向的对象为准。

例如:

// 在一个自定义模块中,默认情况下,module.exports = {}


// 向module.exports 对象上挂载 username属性

module.exports.username = '张三'


// 向module.exports 对象上挂载 say方法。 【 module.exports === exports】

exports.say = function(){

console.log('Hello');

}


// 让 module.exports 指向一个全新的对象

module.exports = {

nickname: '法外狂徒',

say2(){

console.log('word')

}

}


3、exports 对象

由于 module.exports 单词写起来比较复杂,为了简化向外共享成员的代码, Node 提供了exports对象。默认情况下,exports 和 module.exports 指向同一个对象。最终共享结果,还是以 module.exports 指向的对象为准。


4、module.exports 和 exports 的使用误区

第一个图是因为给 module.exprots 赋值了一个新的对象。


5、Node.js 中的模块化规范

Node.js 遵守了 CommonJS 模块化规范,CommonJS 规定了 模块的特性 和 各模块之间如何相互依赖。

CommonJS 规范:

  • 每一个模块内部,module 变量代表当前模块。
  • module 变量是一个对象,它的exports 属性(即module.exports)是对外的接口。
  • 加载某一个模块,其实是加载该模块的 module.exports 属性,require() 方法用于加载模块。


6、模块的加载机制

模块在第一次加载后会被缓存,多次调用 require() 不会导致模块的代码被执行多次。

注意:不论是内置模块、用户自定义模块、还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率。

1)内置模块的加载机制

内置模块是由Node.js 官方提供的模块,内置模块的加载优先级最高。

例如,require('fs')始终返回内置的 fs模块,即使在node_modules目录下有名字相同的包也叫做fs。


2)自定义模块的加载机制

使用 require() 加载自定义模块时,必须指定以 ./ 或 ../ 开头的路径标识符。在加载自定义模块时,如果没有指定 ./ 或 ../ 这样的路径标识符,则node 会把它当作内置模块或第三方模块进行加载。

同时,在使用require()导入自定义模块时,如果省略了文件的扩展名,则Node.js 会按顺序分别尝试加载以下的文件:

  • 按照确切的文件名进行加载
  • 补全.js扩展名进行加载
  • 补全.json扩展名进行加载
  • 补全.node扩展名进行加载
  • 加载失败,终端报错


3)第三方模块的加载机制

如果传递给require()的模块标识符不是一个内置模块,也没有以‘『”或‘./”开头,则Node.js 会从当前模块的父目录开始,尝试从/node_modules文件夹中加载第三方模块。



4)目录作为模块

当把目录作为模块标识符,传递给require()进行加载的时候,有三种加载方式:

  • 在被加载的目录下查找一个叫做package.json的文件,并寻找 main属性,作为 require()加载的入口。
  • 如果目录里没有package.json文件,或者main入口不存在或无法解析,则 Node.js 将会试图加载目录下的 index.js 文件。
  • 如果以上两步都失败了,则Node.js 会在终端打印错误消息,报告模块的缺失:Error: Cannot find module 'xxx'。


本篇文章主要介绍了什么是Node.js、Node的特点以及nmp、模块化的基础知识,对于想要发展为全栈大佬的小伙伴,深入学习Node能够让你快速过渡并且为之后的学习发展奠定基础。


我们下期再见!


END


文案编辑|云端学长

文案配图|云端学长

内容由:云端源想分享