本文参考自 《Nodejs 开发指南》 和 《Nodejs 实战》 两本书。
异步 I/O
Node.js 最大的特点就是采用了异步 I/O 与事件驱动的架构设计。
|
|
这一小段代码是 jQuery 中的 Ajax 请求,我们知道这个结果函数是异步的,等服务器响应结果后才会执行。
对于一些高并发的需求,传统的解决方法是多线程模型,每个业务提供一个线程系统,通过切换和调度线程来实现高并发中的等待开销。但是这样做无疑又增加了线程管理和调度的开销。
Nodejs 采用的是单线程模型,对于所有的 I/O 操作(阻塞操作)都采用类似上面 Ajax 请求的异步的请求方式,避免了频繁切换线程。

Nodejs 的异步机制是基于事件的,所有的磁盘 I/O、网络通讯、数据库查询都以非阻塞方式请求,返回的结果由事件循环来处理。Nodejs 进程在同一时刻只会处理一个事件,完成后立即进入事件循环检查并处理后面的事件。这样做的好处是,CPU 和内存在同一时间集中处理一件事,同时尽可能让耗时的 I/O 操作并行执行。
Nodejs 架构
Nodejs 用异步式 I/O 和事件驱动代替多线程,带来了客观的性能提升。Nodejs 除了用 V8 作为 JavaScript 引擎外,还使用了高效的 libev 和 libeio 库支持事件驱动和异步式 I/O.

Nodejs 的开发者在 libev 和 libeio 的基础上抽象出了 libuv.
Nodejs 安装
Nodejs 的下载和安装请访问官网:https://nodejs.org/zh-cn/
因为我的系统是deepin的,所以下面是Ubuntn下的安装
|
|
npm 是 Node 包管理器,是一个由 Nodejs 官方提供的第三方包管理工具,就像 PHP 中的 Pear、Python 中的 PyPI 一样。npm 是一个完全由 JavaScript 实现的命令行工具,通过 Nodejs 执行。
如果我们有需要安装多个版本的 Nodejs 的需求,可以安装多版本管理器。n 是一个十分简洁的 Node 多版本管理器,他的名字就一个字母。
|
|
安装完 n 后,可以使用 n --help
查看使用说明。详细可以访问n主页
Nodejs 的 Helloword
新建一个 hw.js
文件
|
|
然后执行下面命令
|
|
这就是 Nodejs 的 helloword 程序。
建立HTTP服务器
新建一个 app.js
的文件
|
|
接下来运行 node app.js
命令,打开浏览器访问 http://127.0.0.1:3000

用 Nodejs 实现最简单的 http 服务器就这样诞生了。这个程序中调用了 Nodejs 提供的 http 模块,对所有 HTTP 请求答复同样的内容并监听 3000 端口。
本文出自水寒的个人博客,转载请说明出处:https://dp2px.com
模块和包
模块(Module)和包(Package)是 Nodejs 最重要的支柱。开发一个具有一定规模的程序不可能只用一个文件,通常需要把各个功能拆分、封装、然后组合起来。Nodejs 提供了 require 函数来调用其他模块,而且模块都是基于文件的,机制十分简单。
模块
在 Nodejs 中模块和文件是一一对应的,一个 Nodejs 文件就是一个模块,这个文件可能是 JavaScript 代码、JSON 或者编译过的 C/C++ 扩展。
上面我们使用 var http = require('http')
,其中 http 是 Nodejs 的一个核心模块,其内部是用 C++ 实现的,外部用 JavaScript 封装。
Nodejs 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于获取模块的接口。我们在同一个目录下创建两个文件。
module.js
文件内容:
|
|
getmodule.js
文件内容:
|
|
运行 node getmodule.js
,结果是:
|
|
创建包
包是在模块基础上更深一步的抽象,Nodejs 的包类似于 C/C++ 的函数库或 Java 中的 jar 包。它将某个独立的功能封装起来,用于发布、更新、依赖管理和版本控制。
Nodejs 的包是一个目录,其中包含了一个 JSON 格式的包说明文件 package.json
.
Nodejs 在调用某个包时,会首先检查包中 package.json
文件的 main 字段,将其作为包的接口模块,如果 package.json
或 main 字段不存在,会尝试寻找 index.js
或 index.node
作为包的接口。
示例说明, 创建如下目录和文件:
|
|
|
|
可以看到上面的 lib
目录下的index.js中引入了 lib1
和 lib2
并使用 exports
暴露给外部,然后使用 require('./lib')
引入 lib
目录下暴露的方法。
如果我们要更改这个入口文件名称或路径,可以使用 package.json
文件来配置,在 lib
目录新增一个 package.json
文件:
|
|
package.json
文件内容如下:
|
|
接下来就可以使用 name
所对应的 main
路径了:
|
|
module.exports
事实上上面的 exports
对象指向的是 module.exports
, Nodejs 为每一个模块提供了一个这样的变量。
|
|
所以,在你的代码里面千万不要出现将 exports 变量指向一个值,因为这样等于切断了 exports 与 module.exports
的联系。这个联系正是它们不同的地方。
修改上面的 rukou.js
代码如下:
|
|
然后就可以在 main.js
中这样使用了, 注意调用方法的不同:
|
|
结论:如果要输出一个键值对象 {}
,可以利用 exports
这个已存在的空对象 {}
,并继续在上面添加新的键值;如果要输出一个函数或数组,必须直接对 module.exports
对象赋值。所以我们可以得出结论:直接对 module.exports
赋值,可以应对任何情况。
在这篇文章之后我还写了一篇如何模块化和结构化网络请求的文章 《Nodejs实现的一个网络请求模块化基础案例》
本系列源码在 GitHub 仓库:https://github.com/lxqxsyu/NodejsStudy