Node.js是什么
- Node.js是一个能够在服务器端运行JavaScript的开放源代码、跨平台JavaScript运行环境
-
Node采用Google开发的V8引擎运行js代码,使用事件驱动、非阻塞和异步I/O模型等技术来提高性能,可优化应用程序的传输量和规模。
-
Node大部分基本模块都用JavaScript编写。在Node出现之前,JS通常作为客户端程序设计语言使用,以JS写出的程序常在用户的浏览器上运行。
-
Node主要用于编写像Web服务器一样的网络应用,这和PH 和Python是类似的。
-
但是Node与其他语言最大的不同之处在于,PHP等语言是阻塞的而Node是非阻塞的。
-
Node是事件驱动的。开发者可以在不使用线程的情况下开发出一个能够承载高并发的服务器。其他服务器端语言难以开发高并发应用,而且即使开发出来,性能也不尽人意。
- Node把JS的易学易用和Unix网络编程的强大结合到了一起。
-
Node.js允许通过JS和一系列模块来编写服务器端应用和网络相关的应用。
-
核心模块包括文件系统I/O、网络(HTTP、TCP、UDP、DNS、TLS/SSL等)、二进制数据流、加密算法、数据流等等。Node模块的API形式简单,降低了编程的复杂度。
-
使用框架可以加速开发。常用的Node框架有Express.js、Socket.IO和Connect等。Node.js的程序可以在Microsoft Windows、Linux、Unix、Mac OS X等服务器上运行。
- Node.js也可以使用CoffeeScript、TypeScript、Dart语言,以及其他能够编译成JavaScript的语言编程。
Node的用途
-
Web服务API,比如REST
-
实时多人游戏
-
后端的Web服务,例如跨域、服务器端的请求
-
基于Web的应用
- 多客户端的通信,如即时通信
node目录结构
COMMONJS规范
- CommonJS是一种规范,NodeJS是这种规范的实现。
-
CommonJS规范的提出,主要是为了弥补当前JavaScript没有模块化标准的缺陷。
- ECMAScript标准的缺陷
- 没有模块系统
- 标准库较少
- 没有标准接口
- 缺乏管理系统
- 模块化
- 如果程序设计的规模达到了一定程度,则必须对其进行模块化。
- 模块化可以有多种形式,但至少应该提供能够将代码分割为多个源文件的机制。
- CommonJS 的模块功能可以帮我们解决该 问题。
- ECMAScript标准的缺陷
-
CommonJS规范为JS指定了一个美好的愿景,希望JS能够在任何地方运行。
-
CommonJS对模块的定义十分简单:
-
模块引用
- 在规范中,定义了require()方法,这个方法接手模块标识,以此将一个模块引入到当前运行环境中。
- var fs = require('fs')
-
模块定义
- 在运行环境中,提供了exports对象用于导出当前模块的方法或者变量,并且它是唯一的导出的出口。
- 在模块中还存在一个module对象,它代表 模块自身,而exports是module的属性。
- 在Node中一个文件就是一个模块。
- exports.add = function(){ };
-
模块标识
- 模块标识就是传递给require方法的参数,必须符合小驼峰命名的字符串,或者以.、..开头的相对路径,或者绝对路径。它可以没有文件名后缀.js。
- 模块的定义十分简单,接口也十分简洁。 每个模块具有独立的空间,它们互不干扰, 在引用时也显得干净利落。
-
包 package
- CommonJS的包规范允许我们将一组相关的模块组合到一起,形成一组完整的工具。
- CommonJS的包规范由包结构和包描述文件两个部分组成。
包结构
- 用于组织包中的各种文件
- 包实际上就是一个压缩文件,解压以后还原为符合规范的目录,应该包含如 下文件:
- package.json --- 描述文件
- bin --- 可执行二进制文件
- lib --- js代码
- doc --- 文档
- test --- 单元测试
包描述文件
- 描述包的相关信息,以供外部读取分析
- 包描述文件用于表达非代码相关的信息,它是一个JSON格式的文件 – package.json,位于包的根目录下,是包的重要组成部分
- package.json中的字段
- name:必须,npm包的名称
- version:必须,版本号
- main:用于描述npm包项目的主要入口文件。
- 比如包名称为 foo ,package.json中的main字段为foo时,当别人通过npm下载并安装了这个包,可直接通过 require('foo') 引入这个包。
- dependencies:这个包需要的依赖项
NPM(Node Package Manager)
- CommonJS包规范是理论,NPM是其中一种实践。
- 对于Node而言,NPM帮助其完成了第三方模块的发布、安装和依赖等。借助NPM, Node与第三方模块之间形成了很好的一个生态系统。
NPM常用命令
- npm –v --- 查看版本
- npm search 包名 --- 搜索模块包
- npm install 包名 --- 在当前目录安装包
- npm install 包名 –g --- 全局模式安装包
- npm remove 包名 --- 删除一个模块
- npm install 文件路径 --- 从本地安装
- npm install 包名 –registry=地址 --- 从镜像源安装
- npm config set registry 地址 --- 设置镜像源
module(模块)
-
在Node中,一个js文件就是一个模块
- 在Node中,每一个js文件中的js代码都是独立运行在一个函数中(非全局作用域),所以一个模块的中的变量和函数在其他模块中无法访问
- 如果想要在其他模块访问另一个模块的变量,要将需要该变量或方法设置为exports的属性
-
module代表的是当前模块本身
模块封装器
- 在执行模块代码之前,Node.js 会使用一个如下的函数封装器将其封装:
(function(exports, require, module, __filename, __dirname) { // 顶部添加该行 // 模块实际代码 }); // 底部添加该行
- 因此,在模块中使用 var a = 123; 声明的变量都是局部变量,原因如下
function(exports, require, module, __filename, __dirname) { var a = 123; // 局部变量 b = '你好'; // 全局变量
module.require()
- 在node中,通过require()函数来引入外部的模块
- require()可以传递一个文件的路径作为参数,node将会自动根据该路径来引入外部模块。如果使用相对路径,必须以.或..开头
- 使用require()引入模块以后,该函数会返回一个对象,这个对象代表的是引入的模块
var math = require("./math"); // 使用相对路径引入外部模块 var fs = require("fs"); // 使用模块标识引入外部模块
module.exports
-
该对象用来将变量或函数暴露到外部
-
既可以使用 exports 也可以使用 module.exports 导出变量或函数
// module1.js
exports.a = "123"// module2.js
var module1 = require('./module1.js')
console.log(module1) // 运行module2.js,可输出123
Buffer(缓冲器)
- 从结构上看Buffer非常像一个数组,它的元素为16进制的两位数。
-
数组中不能存储二进制的文件,而buffer就是专门用来存储二进制数据的
- 实际上一个Buffer中的元素就表示内存中的一个字节。
- Buffer中的内存不是通过JavaScript分配的,而是在底层通过C++申请的。它是对底层内存的直接操作,其大小一旦确定则不能修改
- 我们可以直接通过Buffer来创建内存 中的空间。
-
使用buffer不需要引入模块,直接使用即可在buffer中存储的都是二进制数据,但是在显示时都是以16进制的形式显示,buffer中每一个元素的范围是从00 - ff (即二进制中的:00000000 - 11111111)
使用Buffer保存字符串
- Buffer.from(string[, encoding])
- string --- 要编码的字符串
- encoding --- string的字符编码,默认值为‘utf-8’
var str = "abc"; var buf = Buffer.from(str , "utf-8");
console.log(buf.length); // 占用内存的大小 --- 3
console.log(str.length);// 字符串的长度 --- 3
console.log(buf); // Buffer --- <Buffer 61 62 63>
创建指定大小的Buffer对象
//创建一个10个字节的buffer var buf1 = new Buffer(10); // buffer构造函数不推荐使用的 var buf2 = Buffer.alloc(10); // 推荐使用
// 可以通过索引操作Buffer中的元素
Buffer的转换
- 将字符串转换为Buffer
- Buffer.from(str)
- Buffer转字符串
- buf.toString([encoding] , [start] , [end]);
写入操作
- 向缓冲区中写入字符串
- buf.write(string[, offset[, length]][, encoding])
- 替换指定索引位置的数据
- buf[index]
- 将指定值填入到缓冲区的指定位置
- buf.fill(value[, offset[, end]][, encoding])
读取操作
- 将缓冲区中的内容,转换为一个字符串返回
- buf.toString([encoding[, start[, end]]])
- 读取缓冲区指定索引的内容
- buf[index]
其他操作
- 复制缓冲区 buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])
- 对缓冲区切片 buf.slice([start[, end]])
- 拼接缓冲区 Buffer.concat(list[, totalLength])
fs(文件系统)
- 在Node中,与文件系统的交互是非常重要的,服务器的本质就将本地的文件发送给远程的客户端
- Node通过fs模块来和文件系统进行交互
- 该模块提供了一些标准文件访问API来打开、读取、写入文件,以及与其交互
-
简单的说,文件系统就是通过Node来操作系统中的文件
-
使用文件系统,需要先引入fs模块,fs是核心模块,可以直接引入不需要下载var fs = require('fs')
同步和异步调用
- fs模块中所有的操作都有两种形式可供选择同步和异步。
- 同步文件系统会阻塞程序的执行,除非操作完毕,不会向下执行代码。 效率较低。
- 异步文件系统不会阻塞程序的执行,它会在操作完成时,通过回调函数将结果返回。
同步文件写入
-
同步打开文件 fs.openSync(path, flags[, mode])
-
该方法会返回一个文件的描述符作为结果,我们可以通过该描述符来对文件进行各种操作
-
参数
-
path:要打开文件的路径
-
flags:打开文件要做的操作的类型
-
r 只读的
-
w 可写的
- 更多请点击查看官方文档
-
-
mode:设置文件的操作权限,一般不传
-
-
-
同步写入内容fs.writeSync(fd, string[, position[, encoding]])
- 参数
-
fd:文件的描述符,需要传递要写入的文件的描述符
-
string:要写入的内容
-
position:写入的起始位置
-
encoding:写入的编码,默认utf-8
-
- 参数
-
保存并关闭文件 fs.closeSync(fd)
- 参数
- fd:文件的描述符,需要传递要写入的文件的描述符
- 参数
var fs = require("fs"); //打开文件 var fd = fs.openSync("hello.txt" , "w"); //向文件中写入内容 fs.writeSync(fd , "12345678", 2); //关闭文件 fs.closeSync(fd);
异步文件写入
注:异步调用的方法,结果都是通过回调函数的参数返回的
- 异步打开文件 fs.open(path, flags[, mode], callback)
- 回调函数的两个参数
-
err:错误对象,如果没有错误则为null
-
fd:文件的描述符
-
- 回调函数的两个参数
- 异步写入文件 fs.write(fd, string[, position[, encoding]], callback)
-
异步关闭文件 fs.close(fd, callback)
var fs = require("fs"); //打开文件 fs.open("hello2.txt","w",function (err , fd) { //判断是否出错 if(!err){ //如果没有出错,则对文件进行写入操作 fs.write(fd,"这是异步写入的内容",function (err) { if(!err){ console.log("写入成功"); } //关闭文件 fs.close(fd , function (err) { if(!err){ console.log("文件已关闭"); } }); }); }else{ console.log(err); } });
简单文件写入
- 同步 fs.writeFileSync(file, data[, options])
- 异步 fs.writeFile(file, data[, options], callback)
- 参数
- file:要操作的文件的路径
-
data:要写入的数据
-
options 选项:可以对写入进行一些设置
-
flag
-
r 只读
-
w 可写
-
a 追加
-
-
var fs = require("fs"); fs.writeFile("hello.txt","这是通过writeFile写入的内容",{flag:"w"} , function (err) { if(!err){ console.log("写入成功~~~"); }else{ console.log(err); } });
流式文件写入
-
同步、异步、简单文件的写入都不适合大文件的写入,性能较差,容易导致内存溢出,可写流适合大文件的写入
-
创建一个可写流 fs.createWriteStream(path[, options])
-
path:文件路径
-
options:配置的参数
-
-
可以通过监听流的open和close事件来监听流的打开和关闭 ws.once("open",function () { console.log("流打开了"); });
var fs = require("fs"); var ws = fs.createWriteStream("hello3.txt"); // 创建一个可写流 ws.once("open",function () { // 监听流的打开 console.log("流打开了"); }); ws.once("close",function () { // 监听流的关闭 console.log("流关闭了"); }); ws.write("通过可写流写入文件的内容"); // 写入内容 ws.end(); // 关闭流
文件的读取
-
同步文件读取(略)
-
异步文件读取(略)
-
简单文件读取(略)
-
异步: fs.readFile(path[, options], callback)
- 同步: fs.readFileSync(path[, options])
- 参数
-
path:要读取的文件的路径
-
options:读取的选项
-
callback:回调函数,通过回调函数将读取到内容返回(err , data)
-
err:错误对象
-
data:读取到的数据,会返回一个Buffer
-
-
-
var fs = require("fs"); var path = "C:/Users/lilichao/Desktop/笔记.mp3"; fs.readFile("an.jpg" , function (err , data) { if(!err){ //console.log(data); //将data写入到文件中 fs.writeFile("C:/Users/lilichao/Desktop/hello.jpg",data,function(err){ if(!err){ console.log("文件写入成功"); } } ); } });
流式文件的读取
// 流式文件读取也适用于一些比较大的文件,可以分多次将文件读取到内存中 var fs = require('fs'); var rs = fs.createReadStream('b.mp3') var ws = fs.createWriteStream('a.mp3') rs.once('open', function () { console.log('可读流打开了') }) rs.once('close', function () { console.log('可读流关闭了') // 数据读取完毕,关闭可写流 ws.end() }) ws.once('open', function () { console.log('可写流打开了') }) ws.once('close', function () { console.log('可写流关闭了') }) // pipe 可把可读流中的内容直接输出到可写流中 rs.pipe(ws);