Node.js
Node.js是一个让JavaScript
运行在服务端的开发平台,它的出现使得JavaScript
成为与PHP、Python、Perl、Ruby等服务端语言平起平坐!无论是安装前端插件、或是作为脚手架的工具都已成为开发人员必备的技能。Node.js
发布于2009年5月,由Ryan Dahl开发实际上是对Chrome V8
引擎进行了封装。使得v8在非浏览器运行的更好、运行JavaScript
更快~
官方中文文档:http://nodejs.cn/api/
1、初识Node.js
1.1、Node.js简介
1、Node.js
是一个能够在服务器端运行JavaScript的开放源代码、跨平台JavaScript运行环境。
2、Node采用Google开发的V8引擎运行js代码,使用事件驱动
、非阻塞
和异步I/O模型
等技术来提高性能,可优化应用程序的传输量和规模。
3、Node大部分基本模块都用JavaScript编写。在Node出现之前,JS通常作为客户端程序设计语言使用,以JS写出的程序常在用户的浏览器上运行。
4、目前,Node已被IBM、Microsoft、Yahoo!、Walmart、Groupon、SAP、 LinkedIn、Rakuten、PayPal、Voxer和GoDaddy等企业采用。
5、核心模块包括文件系统I/O、网络(HTTP、TCP、UDP、DNS、TLS/SSL等)、二进制数据流、加密算法、数据流等等。Node模块的API形式简单,降低了编程的复杂度。
Node.js的用途:
- Web服务API,比如REST
- 实时多人游戏
- 后端的Web服务,例如跨域、服务器端的请求
- 基于Web的应用
- 多客户端的通信,如即时通信
Node.js的历史:
1.2、Node.js之父
每一项技术都有自己的发明者,node.js
也不例外,由瑞安·达尔(Ryan Dahl)在2009年5月年发布。Ryan Dahl并非科班出身,在2004年的时候他还在纽约的罗彻斯特大学数学系读博士,直到2006年,也许是厌倦了读博的无聊,他产生了【世界那么大,我想去看看】的念头。于是做出退学的决定,毅然决然的来到了智利的Valparaiso小镇,从那时起他开始学习网站开发,走上了码农
的道路~
Ryan Dahl经过两年的工作后,成为了高性能Web服务器的专家,从接开发应用到变成专门帮客户解决性能问题的专家。期间他开始写一些开源项目帮助客户解决Web服务器的高并发性能问题,他尝试了很多种语言,但是最终都失败了。
在他快绝望的时候,V8引擎来了。V8满足他关于高性能Web服务器的想象。于是在2009年2月它开始着手编写
Node.js
如今nodo.js已有庞大的生态,无论是作为服务端开发(单线程),还是作为插件使用,成为了前端开发必不可少缺少的技术。
1.3、Node.js的安装
下载地址:https://nodejs.org/zh-cn/download/releases/
1、Node.js的安装,在任意命令行窗口使用
// 1.官网下载对应版本安装包
// 2.安装好找的目录下
// 3.配置环境变量,如NODE_PATH=E:\node_js Path=%NODE_PATH%
// 4.node安装比较简单,这里就不详细说明了
2、全局插件的安装,在任意命令窗口中使用
// 1.首先node安装好后,会有如下默认配置:
npm config get prefix
C:\Users\laizhenghua\AppData\Roaming\npm
npm config get cache
C:\Users\laizhenghua\AppData\Roaming\npm-cache
// 2.在当前用户的环境变量path里,会自动增加这么一条C:\Users\Administrator\AppData\Roaming\npm
// 3.隐藏的环境变量 NODE_PATH 值为 C:\Users\Administrator\AppData\Roaming\npm\node_modules
// 4.修改默认配置也就是修改以上信息如:
npm config set prefix npm目录
npm config set cache cache目录
修改环境变量(path + NODE_PATH)
node -v // 查看node版本
npm config list // 查看配置信息
npm config set prefix"D:\dev\nodejs\node_modules\npm\node_gloval_modules" // 设置全局模块插件存放路径
npm config set cache "D\dev\nodejs\\node_modules\npm\node_cache" // 设置缓存
npm install -g cnpm --registry=https://registry.npm.taobao.org // 安装cnpm
npm config set registry https://registry.npm.taobao.org // 设置默认源
cnpm install express --save // 安装Express
cnpm install body-parser --save // node.js中间件,处理JSON,Raw,Text和url编码的数据
cnpm install cookie-parser --save // 解析Cookie工具,通过req.cookies获取传过来的cookie,并转换成对象
cnpm install multer --save // node.js中间件,处理enctype="multipart/form-data"的表单数据
https://www.runoob.com/nodejs/nodejs-tutorial.html
node 文件名 // 启动服务
2、Node.js基础-模块化
2.1、COMMONJS规范
说到模块化,程序设计的规模达到了一定程度,则必须对其进行模块化。模块化不仅能降低应用程序的耦合度,还能方便代码复用。这也是COMMONJS规范产生的一个原因:
ECMAScript标准的缺陷:
- 没有模块系统(ES6已完善)
- 标准库较少
- 没有标准接口
- 缺乏管理系统
模块化:模块化可以有多种形式,但至少应该提供能够将代码分割为多个源文件的机制。在node.js中一个js文件就是一个模块。
服务端系统规模是非常庞大的,不能以现有的JavaScritp
模式去管理代码,会导致服务器代码极难维护,所以到了Node.js
这个不得不不去解决模块化问题,所以提出了CommonJS规范
。CommonJS 的模块功能可以帮我们解决该
问题。
CommonJS规范:
- CommonJS规范的提出,主要是为了弥补当前JavaScript没有模块化标准的缺陷。
- CommonJS规范为JS指定了一个美好的愿景,希望JS能够在任何地方运行。
- CommonJS对模块的定义十分简单:
– 模块引用
– 模块定义
– 模块标识
2.2、模块引用
前面我们已经知道,在node.js
中一个js文件就是一个模块,那么在其他模块中如何引入呢?
在规范中,定义了require()
方法,这个方法接手模块标识,以此将一个模块引入到当前运行环境中。
// 引入其他模块
/*
1.在node中,通过 require() 函数来引入外部的模块
2.函数的参数可以是文件的路径,node会根据该路径自动引入外部模块
3.文件路径如果是相对路径,必须以 ./ 或 ../ 开头
4.使用require()引入模块以后,该函数会返回一个对象,这个对象代表的是引入的模块
*/
let test = require("./test.js"); // 执行 test.js 里的代码并文件后缀可以省略
// 我们在 test.js 中声明一个变量如:let name = "alex";
console.log(test.name); // undefined
/*
为何?
1.在node中,每一个js文件中的js代码都是独立运行在一个函数中,而不是全局作用域
2.所以一个模块中的变量和函数在其他模块中无法访问
3.模块需要通过 exports 来向外部暴露变量和方法,其他模块中引入后才能访问,如在test.js中添加 exports.name = "alex";
*/
console.log(test); // { name: 'alex' }
/*
// test.js
exports.add = function (a, b) {
return a + b;
};
*/
console.log(test.add(1,2)); // 3
2.3、模块标识
我们使用require()
引入外部模块时,使用的就是模块标识:
我们可以通过模块标识来找到指定的模块!
模块分成三大类:
/*
核心模块
- 由node引擎提供的模块
- 核心模块的标识就是模块的名字
文件模块
- 由用户自己创建的模块
- 文件模块的标识就是文件的路径(绝对路径、相对路径)
内建模块
- 底层由C++编写的内建模块
*/
let fs = require("fs"); // 核心模块
console.log(fs);
扩展:
/*
在node中有一个全局对象 global,它的作用和网页中window类似
- 在全局中创建的变量都会作为global的属性保存
- 在全局中创建的函数都会作为global的方法保存
*/
a = 123; // 全局变量
console.log(global.a); // 123
// 如何证明?模块化代码是在函数中执行的?
// Javascript中每个函数都会有一个Arguments对象实例arguments
console.log(arguments); // 有输出就证明代码是在函数中运行的
// 解释
/*
当node在执行模块中的代码时,它会首先在代码的最顶部添加如下代码
function (exports, require, module, _filename, _dirname) {
...
在代码的最底部添加(补全大括号)
}
*/
// 可以输出这个几个参数,你就懂了模块中的代码都是包装在一个函数中执行的
2.4、模块的定义
我们知道exports
对象可以导出当前模块的一些变量与函数,并且它是唯一的导出出口。在模块中还存在一个module
对象,它代表模块自身,而exports是module的属性。
使用module.exports
也可以导出当前模块的变量与方法:
// test.js
exports.name = "alex";
module.exports.age = "21";
// myApp.js
let test = require("./test");
console.log(test.name); // alex
console.log(test.age); // 21
时刻谨记:在Node中一个文件就是一个模块,每个模块具有独立的空间,它们互不干扰,在引用时也显得干净利落。
那么exports
与module.exports
有什么区别呢?
// 他们的关系就好比如下例子
let obj = {};
obj.a = {};
let a = obj.a;
// obj.a 和 a 指向同一个对象
console.log(obj.a === a); // true
a.name = "alex";
console.log(obj.a.name); // alex
// 值得注意的是 exports = module.exports
module.exports = {
...
}
// module.exports可以导出一个对象,而 exports 不行
// 因为 exports = {} 的时候已经不再指向 module.exports,所以exports只能使用 exports.xxx 的形式导出变量或函数
// 如
module.exports = {
name: "alex".
age: 21
}
exports.name = "alex";
exports.age = "21";
2.5、包 package
CommonJS的包规范允许我们将一组相关的模块组合到一起(也可以理解成一组模块的集合),形成一组完整的工具。CommonJS的包规范由包结构
和包描述文件
两个部分组成。
/*
包结构:
- 用于组织包中的各种文件
- 包实际上就是一个压缩文件,解压以后还原为目录。符合规范的目录,应该包含如下文件
- package.json 描述文件
- bin 可执行二进制文件
- lib js代码
- doc 文档
- test 单元测试
包描述文件:
- 描述包的相关信息,以供外部读取分析
- 包描述文件用于表达非代码相关的信息,它是一个JSON格式的文件 – package.json,位于包的根目录下,是包的重要组成部分。
package.json中的字段:
- name、description、version、keywords
- maintainers、contributors、bugs
- licenses、repositories、dependencies
- homepage、os、cpu、engine、builtin
- directories、implements、scripts、author
- bin、main、devDependencies
*/
例如node目录就是一个包:
3、NPM
NPM
(Node Package Manager)是随同Node.js一起安装的包管理工具,能解决Node.js代码部署上的很多问题,常见的使用场景有以下几种:
- 允许用户从NPM服务器下载别人编写的第三方包到本地使用。
- 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
- 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。
对于Node而言:NPM帮助其完成了第三方模块的发布、安装和依赖等。借助NPM,Node与第三方模块之间形成了很好的一个生态系统。
由于新版的Node.js
已经集成了npm,所以之前npm也一并安装好了。同样可以通过输入 npm -v
来测试是否成功安装。命令如下,出现版本提示表示安装成功:
C:\Users\laizhenghua>npm -v
6.14.5
3.1、npm常用命令
npm -v // 查看版本
npm // 帮助说明
npm search packageName // 搜索模块包
npm init // 初始化一个包
npm init -y // 初始化时跳过提示
npm install 包名 // 在当前目录安装包
npm install 包名 -g // 全局模式安装包
npm install 包名 --save // 安装包并添加到依赖中
npm install 文件路径 // 从本地安装
npm install 包名 -registry=地址 // 从镜像源安装
npm config set registry 地址 // 设置镜像源
npm remove 包名 // 删除一个模块
趁热打铁:任意目录下新建一个文件夹,cmd打开命令行窗口,执行npm init -y,初始化成功后,执行npm install math --save。在根目录下新建index.js,书写如下代码
let math = require("math");
// console.log(math);
console.log(math.sum([1, 2])); // 3
通过npm
下载的包都放到node_modules
文件夹中,我们通过npm下载的包,直接通过包名引入即可!
node
在使用模块名字来引入模块时,它会首先在当前目录的node_modules
中寻找是否含有该模块
- 如果有则直接使用,如果没有则上去一级目录的
node_modules
中寻找 - 如果有则直接使用,如果没有则再去上一级目录寻找,直到找到为止
- 直到找到磁盘的根目录。如果依然没有,则报错
3.2、安装CNPM
默认的镜像源是在国外,在国内下载第三方包的速度极其之慢,也会造成经常下载失败现象。我们可以设置国内的镜像源,加快下载速度如:
淘宝 NPM 镜像地址:https://npm.taobao.org/
安装CNPM:
npm install -g cnpm --registry=https://registry.npm.taobao.org
4、文件系统
4.1、Buffer(缓冲区)
操作文件时候,JavaScript
数组是不能存储二进制文件的,而buffer就是专门用来存储二进制数据。从结构上看Buffer
非常像一个数组,它的元素为16进制的两位数。
实际上一个元素就表示内存中的一个字节。
Buffer
中的内存不是通过JavaScript分配的,而是在底层通过C++申请的。也就是我们可以直接通过Buffer来创建内存
中的空间。另外使用buffer不需要引入模块,直接使用即可。
Buffer的使用示例:
// Buffer的使用
let str = "hello world";
let buffer = Buffer.from(str); // 转换成二进制unicode编码
console.log(buffer); // <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
console.log(buffer.length); // 11
console.log(str.length); // 字符串长度
// 在buffer中存储的都是二进制数据,但是在显示时都是以16进制的形式显示,buffer中每一个元素的范围是从00 - ff,即一个元素占一个字节
// 创建一个指定大小的buffer,注意Buffer构造函数大多是都弃用了,不推荐使用
let buf = new Buffer(1024); // 1042个字节
console.log(buf.length); // 1024
// 创建指定大小Buffer推荐使用Buffer.alloc()/Buffer.allocUnsafe()等方法。更多内容可查看官方文档
let buffer2 = Buffer.alloc(10);
console.log(buffer2.length); // 注意buffer的大小一旦确定,则不能修改,Buffer实际上是对底层内存直接操作
// 通过索引操作元素
buffer2[0] = 0;
buffer2[1] = 255;
console.log(buffer2[1]); // 255
更多内容请参照官方文档:http://nodejs.cn/api/buffer.html
4.2、fs文件系统
fs(File System)
:在Node中,与文件系统的交互是非常重要的,服务器的本质就将本地的文件发送给远程的客户端。Node通过fs模块
来和文件系统进行交互,该模块提供了一些标准文件访问API来打开、读取、写入文件,以及与其交互。
// 要使用fs模块,首先需要对其进行加载
let fs = require("fs");
同步和异步调用:
/*
- fs模块中所有的操作都有两种形式可供选择 同步 和 异步。
- 同步文件系统会 阻塞程序 的执行,也就是除非操作完毕,否则不会向下执行代码。
- 异步文件系统不会阻塞程序的执行,而是在操作完成时,通过回调函数将结果返回。
*/
官方文档:http://nodejs.cn/api/fs.html
文件的基本操作: