本文为 Node.js 系列笔记第一篇。文章参考:nodejs 教程 —— 大地;《深入浅出 Node.js》;阮一峰 nodejs 博客
在 Node 的官方网站( http://nodejs.org )之外,Node 具有很多别称:Nodejs 、NodeJS 、 Node.js 等。本文使用 Node 这个名称。
Node 是一个 JavaScript 运行环境(runtime)。它让 JavaScript 可以开发后端程序,它几乎能实现其他后端语言能实现的所有功能。
Node 在浏览器之外运行 V8 JavaScript 引擎(Google Chrome 的内核)。 这使得 Node 的性能非常好。
Node 应用程序在单个进程中运行,无需为每个请求创建新的线程。 Node 在其标准库中提供了一组异步的 I/O 原语,以防止 JavaScript 代码阻塞,通常,Node 中的库是使用非阻塞范式编写的,使得阻塞行为成为异常而不是常态。
Node.js 下载和环境变量配置方法不再详细介绍,可参考 Nodejs nodejs安装与VScode配置 。
配置完成后,创建测试文件 01-app.js
,在终端输入 node 01-app.js
可以实现输出。
如果我们使用 PHP 来编写后端的代码时,需要 Apache 或者 Nginx 的 HTTP 服务器来处理客户端的请求相应。不过对 Node 来说,概念完全不一样了。使用 Node 时,不仅仅是在实现一个应用,同时还实现了整个 HTTP 服务器 。
下面简单说一下如何创建 Web 服务器。
首先,引入 http 模块
var http = require("http");
为了防止修改,可以指定为 const
const http = require('http');
接下来使用 http.createServer()
方法创建服务器,并使用 listen 方法绑定 3000 端口。函数通过 request, response 参数来接收和响应数据。
http.createServer((req, res) => {
// req: 获取客户端传过来的信息
// res:给浏览器的响应信息
// console.log(req.url); // 获取 url
// 设置响应头: 状态码:200 文件类型:html 字符集:utf-8
res.writeHead(200, { 'Content-Type': 'text/html;charset="utf-8"' });
res.write('this is nodejs');
// 结束响应,必须写,不然浏览器一直响应
res.end();
}).listen(3000); // 建议 3000 以上
// 终端打印如下信息
console.log('Server running at http://127.0.0.1:3000/');
注意:如果改变了代码内容,如response.end('Hello node.js');
,网页不会立即生效,需要在终端 ctrl + c
结束应用,之后重新运行。
下载插件后,之间输出 node 会出现提示,点击后可快速实现代码。
如果我们本地写一个 JS 代码,无论如何都不能直接拖入浏览器运行,但是有了 Node,任何一个 JS 文件,都可以通过它来运行。也就是说,Node.js 就是一个 js 的执行环境。
Node 中,将很多的功能,划分为了一个个 module(模块),Node 中的很多功能都是通过模块实现。
此处对 http 模块只是简单引入,更详细的介绍可以参考此文 Node.js「三」创建静态 WEB 服务器 。
//引用模块
var http = require("http");
//创建一个服务器,回调函数表示接收到请求之后做的事情
var server = http.createServer(function(req,res){
//req 参数表示请求,res 表示响应
console.log("服务器接收到了请求" + req.url);
res.end(); // End 方法使 Web 服务器停止处理脚本并返回当前结果
});
//监听端口
server.listen(3000,"127.0.0.1");
设置一个响应头:
res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' });
supervisor 会不停地 watch 应用下面的所有文件,如果发现有文件被修改,就重新载入程序文件。这样就实现了部署,修改了程序文件后马上就能看到变更后的效果,不需要重新启动 node.js。
npm install -g supervi
注意:要以管理员身份运行 cmd
来个小插曲,简单介绍一下 npm
npm 是随同 nodejs 一起安装的包管理工具,能解决 nodejs 代码部署上的很多问题,
常见的使用场景有以下几种:
- 允许用户从 NPM 服务器下载别人编写的第三方包到本地使用。
- 允许用户从 NPM 服务器下载并安装别人编写的命令行程序到本地使用。
- 允许用户将自己编写的包或命令行程序上传到 NPM 服务器供别人使用。
由于新版的 nodejs 已经集成了 npm,所以之前 npm 也一并安装好了。同样可以通过输入
npm -v
来测试是否成功安装。
安装不成功的话,可以点此网站安装 cnpm 代替 npm —— CNPM 中国 npm 镜像的客户端
如果安装过后,使用 VS Code 运行时报错,可参考此文:VSCODE:supervisor : 无法加载文件
这样一套操作下来,以后修改完代码内容,只需要保存代码,重新刷新页面即可显示更改后内容。
JavaScript 是一个强大面向对象语言,它有很多快速高效的解释器。然而,JavaScript 标准定义的 API 是为了构建基于浏览器的应用程序。并没有制定一个用于更广泛的应用程序的标准库。
CommonJS 规范的提出,主要是为了弥补当前 JavaScript 没有标准库的缺陷。它的终极目标是:提供一个类似 Python、Ruby 和 Java 语言的标准库,而不只是让 JavaScript 停留在小脚本程序的阶段。
用 CommonJS API 编写出的应用,不仅可以利用 JavaScript 开发客户端应用,还可以编写以下应用:
总的来说,CommonJS 就是模块化的标准,Node 就是 CommenJS(模块化)的实现。Node 借鉴 CommonJS 的 Modules 规范实现了一套非常易用的模块系统。
CommonJS 对模块的定义十分简单,主要分为 模块引用、模块定义 和 模块标识 3 个部分。
var math = require('math');
在 CommonJS 规范中,存在 require()
方法,这个方法接受模块标识,以此引人一个模块的 API 到当前上下文中。
对应引入的功能,上下文提供了 exports 对象 用于导出当前模块的方法或者变量,并且它是唯一导出的出口。
在模块中,还存在一个 module 对象,它代表模块自身,而 exports 是 module 的属性。在 Node 中,一个文件就是一个模块,将方法挂载在 exports 对象上作为属性即可定义导出的方式:
exports.add = function (a, b) {
return a + b;
}
在另一个文件中通过 require()
引入模块后,就能调用定义的属性和方法了。
var request = require('./module/request');
console.log(request.add(1, 2)); // 3
模块标识其实就是传递给 require()
方法的参数,它必须是符合小驼峰命名的字符串,或者以.、…开头的相对路径,或者绝对路径。它可以没有文件名后缀.js。
所有代码都运行在模块作用域,不会污染全局作用域。
模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
Node.js 应用由模块组成,采用 CommonJS 模块规范。
Node.js 有三类模块,即内置模块、第三方模块、自定义模块。当然,因为第三方模块也是自定义的模块上传到了 npmjs.org 中,你可以下载引入。所以,也可以理解为有两类模块,即 内置模块、自定义模块。
Node.js 内置模块又叫 核心模块,Node.js 安装完成可直接使用。如:
const path = require('path')
var extname = path.extname('index.html')
console.log(extname)
自定义的 Node.js 模块,也叫 文件模块,是我们自己写的供自己使用的模块。同时,这类模块发布到 npmjs.org 上就成了开源的第三方模块。
自定义模块是在运行时动态加载,需要完整的路径分析、文件定位、编译执行过程、速度相比核心模块稍微慢一些,但是用的非常多。
根据参数的不同格式,require()
命令去不同路径寻找模块文件
如果参数字符串以 /
开头,则表示加载的是一个位于绝对路径的模块文件。比如,require('/home/marco/foo.js')
将加载 /home/marco/foo.js
。
如果参数字符串以 ./
开头,则表示加载的是一个位于相对路径(跟当前执行脚本的位置相比)的模块文件。比如,require('./circle')
将加载当前脚本同一目录的 circle.js
。
如果参数字符串不以 ./
或 /
开头,则表示加载的是一个默认提供的核心模块(位于 Node 的系统安装目录中),或者一个位于各级 node_modules 目录的已安装模块(全局安装或局部安装)。
举例来说,脚本 /home/user/projects/foo.js 执行了 require('bar.js')
命令,Node会依次搜索以下文件:
/usr/local/lib/node/bar.js
/home/user/projects/node_modules/bar.js
/home/user/node_modules/bar.js
/home/node_modules/bar.js
/node_modules/bar.js
这样设计的目的是,使得不同的模块可以将所依赖的模块本地化。
./
或 /
开头,而且是一个路径,比如require('example-module/path/to/file')
,则将先找到 example-module
的位置,然后再以它为参数,找到后续路径。.js
、.json
、.node
后,再去搜索。.js
文件会以文本格式的 JavaScript 脚本文件解析,.json
文件会以 JSON 格式的文本文件解析,.node
文件会以编译后的二进制文件解析。通常,我们会把相关的文件会放在一个目录里面,便于组织。这时,最好为该目录设置一个入口文件,让 require()
方法可以通过这个入口文件,加载整个目录。
在目录中放置一个package.json文件,并且将入口文件写入 main 字段。如下:
// package.json
{
"name" : "some-library",
"main" : "./lib/some-library.js"
}
require()
发现参数字符串指向一个目录以后,会自动查看该目录的 package.json 文件,然后加载 main 字段指定的入口文件。如果 package.json 文件没有 main 字段,或者根本就没有 package.json 文件,则会加载该目录下的 index.js 文件或 index.node 文件。
此处可参考:nodejs文件引用规则;CommonJS 规范
既然提到了 package.json 文件,顺便来介绍一下。
package.json 定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)。
npm init
或者
npm init --yes
所下所示
可以看到 package.json 文件中代码 "main" : "db.js"
,指明了模块入口文件为 db.js
。
{
"name": "test",
"version": "1.0.0",
"description": "test",
"main": "main.js",
"keywords": [
"test"
],
"author": "wade",
"license": "MIT",
"dependencies": {
"express": "^4.10.1"
},
"devDependencies": {
"jslint": "^0.6.5"
}
}
npm install babel-cli --save-dev
npm install 模块 --save
npm install 模块 --save-dev
npm install node_module –save // 自动更新 dependencies 字段值
npm install node_module –save-dev // 自动更新 devDependencies 字段值
"dependencies": {
"ejs": "^2.3.4",
"express": "^4.13.3",
"formidable": "^1.0.17"
}
^
:表示第一位版本号不变,后面两位取最新的~
:表示前两位不变,最后一个取最新*
:表示全部取最新
Node 组织了自身的核心模块,也使得第三方文件模块可以有序地编写和使用。但是在第三方模块中,模块与模块之间仍然是散列在各地的,相互之间不能直接引用。而在模块之外,包和 NPM 则是将模块联系起来的一种机制。
完全符合 CommonJS 规范的包目录一般包含如下文件:
在 Node 中可以通过 npm 命令来下载第三方的模块(包)。
NPM 是世界上最大的开放源代码的生态系统。我们可以通过 NPM 下载各种各样的包,这些源代码(包)我们可以在 https://www.npmjs.com 找到。
对于 Node 而言,NPM 帮助完成了第三方模块的发布、安装和依赖等。借助 NPM,Node 与第三方模块之间形成了很好的一个生态系统。
NPM 的使用可参考 NPM 使用介绍 | 菜鸟教程 。
{
"name": "day03",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
// 对 123 进行 md5 加密
var md5 = require("md5");
console.log(md5(123)); // 5289df737df57326fcdd22597afb1fac
注意:
在安装时建议使用 npm install md5 --save
,它会将对应包信息写入 package.json 中,方便他人阅读和配置依赖。(现在最新的 node.js 使用 npm 安装时无需写 --save
,也会将包信息写入 package.json 文件中;但是如果使用 cnpm 安装的话,不写 --save
的包信息并不会写入)
因为将代码发送给别人时,一般不会发送 node_modulues 文件夹,也就是别人要先配置好你项目中所用的模块才可以运行你的项目。如果你的 package.json 中给出了相关信息,别人只需要终端执行 npm i
即可将相应模块导入。
npm -v
npm install <Module Name> // 本地安装
npm install <Module Name> -g // 全局安装
注意:install 可以缩写为 i
本地安装:将安装包放在 ./node_modules 下(运行 npm 命令时所在的目录)如果没有 node_modules 目录,会在当前执行npm 命令的目录下生成node_modules 目录,可以通过 require() 来引入本地安装的包
全局安装:将安装包放在 /usr/local 下或者你 node 的安装目录,可以直接在命令行里使用
npm install <Module Name> --save
- 在 npm 5 之前的版本
使用 npm install 默认选项安装包时,仅仅会把包下载到 node_modules/ 中,并不会同时修改 package.json。而使用--save
选项就可以在安装包的同时,修改 package.json 文件。
- 在 npm 5 之后的版本
npm install 安装包时,默认便会修改 package.json 文件,所以--save
选项已经不再需要了。
npm uninstall <Module Name>
npm list
npm info <Module Name>
npm install jquery@1.8.0
npm 官网: https://www.npmjs.com
淘宝 NPM 镜像:https://npmmirror.com/
淘宝 NPM 镜像是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读),同步频率目前为 10 分钟 一次以保证尽量与官方服务同步。
可以使用 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm,进而大大加快下载速度。如果要使用 cnmp,首先要进行安装:
npm install -g cnpm --registry=https://registry.npmmirror.com