Node.js 是一个能够在服务端运行的 Javascript 运行环境,Node 大部分基本模块都用 Javascript 编写。运行在 Node.jS 中的 JS 的用途是操作磁盘文件或搭建HTTP服务器,NodeJS就相应提供了fs、http等内置对象。
NodeJs 高性能 Web 服务器、JS、单线程、异步、V8引擎、硬件要求低
V8引擎,目前速度最快的 JS引擎
🚕奇数为测试版、偶数为稳定版
可以做什么???
-
Web 服务器后台
-
命令行工具
- npm
- git
- hexo
- ......
-
第三方模块( 自己写的少,主要使用别人开发的第三方库 )
- webpack
- npm
- gulp
🥞在 Node 中有一个全局对象 (global),与浏览器全局对象 (window) 类似
模块化
CommonJS、AMD、CMD、ES6模块化
模块化优点:
- 避免命名冲突,减少命名空间污染
- 更好的文件分离,按需加载
- 更高的复用性
- 更高的维护性
CommonJS 规范
注意:🚄在 Node 中 , 每一个 js 文件中的代码都是独立运行在一个函数中的(函数包裹代码 IIEF) , 【不是全局作用域的】, 模块中的变量和函数在其它模块中是无法访问的
require
模块引入:在 node 中,通过 require 函数来引入外部的模块
require() 引入的模块【返回的是一个对象】,这个对象代表的是引入的模块内容
注意:相对路径必须以 ./ ../ 开头
exports
模块导出: 模块中向外部【暴露属性和方法 / 使用 exports 属性】
exports.xxx=yyy
缺点:加载模块是同步的,只有加载完后才能执行后面的操作;现加载现用,在服务器端编程,加载的模块一般存在本地硬盘里,加载起来比较快,不用考虑异步加载的问题,因为 CommonJS 规范比较适用。 然后,并不适用于浏览器环境,同步意味着阻塞线程,浏览器资源的加载方式是异步的。
模块的标识: 使用 require() 引入外部模块时,使用的就是模块标识 require('模块标识') ,可以通过模块标识来找到指定的模块。
模块主要分两大类: 核心模块:-由 node 引擎提供的模块,核心模块的标识就是模块的名字 require('模块名') 文件模块:-由开发者创建的模块,文件模块的标识就是模块的相对路径 require('相对路径')
模块文件结构:当 Node 在执行代码时会自动用下面函数将内容包裹
function (exports, require, module, __filename, __dirname){
.../文件内容
}
🍤注意:从这个函数可以看出,开发者使用的 exports , require , module .exports 都是这个函数提供的,在函数执行时传递进去的五个实参,而不是在全局中的,
exports:- 该对象用来将变量或函数暴露到外部
require:- 函数,用来引入外部的模块
module:- 代表的是模块本身,exports 实际上就是 module 的属性
__filename:- 当前模块的【完整路径】
__dirname:- 当前模块的所处的【文件夹名】
module
通过module对象可以访问到当前模块的一些相关信息,但最多的用途是替换当前模块的导出对象。例如模块导出对象默认是一个普通对象,如果想改成一个函数的话,可以使用以下方式。
obj.a --- module.exports
a=obj.a --- exports=module.exports
引用数据类型在堆栈中的关系
exports只能通过 . 来暴露数据
module.exports既可以通过 . 的形暴露,也可以直接赋值
错误🍳:
exports={xxx=yyy,aaa(){}}
正确🍖:exports.xxx=yyy 或者
module.exports={xxx=yyy,aaa(){}} 或者
module.exports.xxx=yyy
包 package
Common.Js 的包规范允许将一组模块组合到一起,形成一组完整的工具。
组成:包结构、包描述文件
包结构:-用于组织包中的各种文件( 压缩文件,解压以后还原为目录 )
规范目录:
-package.js -描述文件( 必须 )
-bin -可执行二进制文件
-lib -js 代码
-docc -文档
-test -单元测试
包描述文件:-描述包的相关信息,以供外部读取分析
Node 包管理器 npm
npm -v //查看版本号
npm version //查看所有依赖版本号
npm search 包名 //搜索包
npm install/i 包名 //安装包
npm remove/r 包名 //删除包
//安装包并添加到依赖中(package.json文件中),可以查看保证我们需要的包版本
//现在7.7.6版本不需要提交 --save (自动添加)
npm install 包名 --save
npm install 包名 -g //全局安装包
当把包提交到依赖中( package.json ) ,在安包时就不需要一个个去 install 安装,只需要 npm install 就可以
模块初始化
注意🥩:一个模块中的 JS 代码仅在模块第一次被使用时执行一次,并在执行过程中初始化模块的导出对象。之后,缓存起来的导出对象被重复利用。
Buffer(缓冲区)
用户发送接收的数据(2进制),在服务器端这里它存放在哪??? --- Buffer
在 Node.js中,定义了一个 Buffer 类,该类用来创建一个🍜专门存放二进制数据的缓存区。一个 Buffer 类似于一个数组,但它对应于 V8 堆内存之外的一块原始内存。 Buffer 库为 Node.js 带来了一种存储原始数据的方法。
注意🥫:在Unicode编码,存储时为二进制、显示时为十六机制(0x)、读取时为十进制(日常使用)
目前使用new Buffer()构造函数来创建对象实例已废弃(Buffer对内存的权限操作相比很大,可以直接捕获一些敏感信息),目前官方文档里面建议使用Buffer.from()接口去创建Buffer对象。
Buffer.from(str) 代替 new Buffer(str) 将一个字符串保存到Buffer中
Buffer.alloc(10) 代替 new Buffer(10) 设置10个字节的Buffer
性能🍖:Buffer.alloc(清空再分配空间) > Buffer.allocUnsafe(不会清空再分配空间,可能包含敏感数据)
Buffer 一旦大小确定,则不能修改,因为 Buffer 实际上是对底层的内存的直接操作
buffer.length -占用内存的大小
中文占3个字节(byte)内存
buffer中每个元素的范围是从 00-ff(16进制)、 0-255(10进制)、 00000000- 11111111(2进制)
只要数字在控制台或者页面输出都是一个 10进制 的数据( 自动转化 )
let str = "你好 node!";
let buffer = Buffer.from(str);
/* 将一个字符串保存到Buffer中 */
Buffer.from(str);
console.log(str.length); //8 -字符串的长度
console.log(buffer.length); //12 -占用内存的大小
// 00000000 或 11111111
// 计算机 一个0 或 一个1 我们称为1位(bit)
/*
8bit=1byte(字节)
1024byte=1kb
1024kb=1mb
1024mb=1gb
1024gb=1tb
🍕buffer 里的一个元素占用内存一个字节
*/
Buffer 实例与普通的 JavaScript 字符串之间进行相互转换 toString()
fs ( 文件系统 )
服务器的本质就是将本地的文件发送给远程的客户端,通过 Node 来操作系统中的文件,Node通过fs 模块来和文件系统进行交互。
fs 模块提供了一些标准文件访问的 API 来打开、读取、写入文件。
使用: const fs = require('fs')
同步和异步
调用 fs 模块中所有的操作都有两种形式可供选择同步和异步。
同步文件系统会阻塞程序的执行,也就是除非操作完毕,否则不会向下执行代码。
异步文件系统不会阻塞程序的执行,而是在操作完成时,通过回调函数将结果返回。
区别:🛹
同步:const fd = fs.方法名Sync ( path [, mode] )
异步:fs.方法名 ( path [, mode] , callback)
异步方法无需赋值给变量( 无返回值 ),通过回调函数的参数返回值
Sync :同步; callbck:回调;
性能:异步 > 同步
文件的写入:🚗
---手动操作的步骤
同步
1.打开文件
fs. openSync (path, flagsI, model)
-
path要打开文件的路径
-
flags打开文件要做的操作的类型
r只读的
w可写的
-
mode 设置文件的操作权限,一般不传
-
返回值:---该方法会返回一个文件的描述符作为结果,我们可以通过读描述符来对文件进行各种操作
fs. openSync ("path", "w/r")
2.向文件中写入内容
fs.writeSync(fd, string[, position[, encoding]])
-
fd:文件的描述符
-
string:写入得到内容
-
position:初始位置
-
encoding:写入的编码,默认 utf-8
-
返回: number 写入的字节数。
fs.writeSync( fd , '内容')
注意🚒:此时如果不关闭文件,文件一直存在 内存 中得不到释放
3.保存并关闭文件
fs.closeSync( fd )
- fd:文件的描述符
异步
1.打开文件
fs. open (path, flagsl, model, callback)
-用来打开一个文件一异步调用的方法,结果都是通过回调函载 的参救返回的
-回调函数两个参数:
err 错误对象,如果没有错误则为null
fd 文件的描述符 个
fs. open (path, flagsl, model, functiom( fd , err ){} )
2.向文件中写入内容
fs.write(fd, string[, position[, encoding]], functiom( err ){})
3.保存并关闭文件
fs.close( fd, functiom( err ){} )
实用(常用)写入文件操作
上面两种方式的操作并不实用
异步:
fs.writeFile(file, data[, {options}], callback)
fs.writeFile(file, data, {flag:'w'} , functiom( err ){} )
options配置: 常用 r 、 w 、 a
知识点补充:当出现路径 C:\ 时,会报错误。解决方法:
1、使用 \ 转义:C:\
2、使用 / : C:/
同步:
fs.writeFileSync(file, data[, options])
流式文件写入
可以持续写入(累加)
同步、异步或者简单文件写入( 一次性写入,消耗性能大,容易导致内存溢出 )都不适合操作大文件
使用:
--- 创建一个可写流( 接水管,往水缸放数据 )
fs.createWriteStream(path[, options])
//创建一个可写流
const ws = fs.createWriteStream("./public/test.txt");
// 可以通过监听流的open和close事件来监听流的打开和关闭
ws.once("open", function () {
console.log("流式打开了");
});
ws.once("close", function () {
console.log("流式关闭了");
});
// 往文件里添加数据,可以连续持续添加
ws.write("1!");
ws.write("2!");
ws.write("3!");
ws.write("4!");
// 关闭流
// ws.close(); ---尾部关闭
// ws.end(); ---头部关闭
ws.end();
on(事件字符串,回调函数)-可以为对象绑定一个事件
once(事件字符串,回调函数)-可以为对象绑定一个一次性的事件,该事件将会在触发一次以后自动失效
关闭流
文件的读取
fs.readFile 异步; fs.readFileSync 同步; ---参数与写入类似
返回的是一个 Buffer 类型的数据,需要使用 toString() 转化
流式文件写入
fs.createReadStream(path[, options])
如果要读取一个可读流中的数据,必须要为可读流绑定一个data事件, data事件绑定完毕,它会自动开始读取数据,读取完毕就会自动关闭流
/* 流式文件的读写 */
const fs = require("fs");
//创建一个可读流
const rs = fs.createReadStream(
"./public/#雄狮少年 18岁的他远走他乡,靠一人撑起整个家!再苦再累,他也没有放.mp4"
);
//创建一个可写流
const ws = fs.createWriteStream("./public/test.mp4");
// 可以通过监听 可写流 的open和close事件来监听流的打开和关闭
ws.once("open", function () {
console.log("可写流打开了");
});
ws.once("close", function () {
console.log("可写流关闭了");
});
// 可以通过监听 可读流 的open和close事件来监听流的打开和关闭
rs.once("open", function () {
console.log("可读流打开了");
});
//这里的close事件 可读流读取完毕会自动关闭,无需手动调用 end/close 关闭
rs.once("close", function () {
console.log("可读流关闭了");
// 关闭可写流
ws.end();
});
//当读取大文件不是一次性读取的,会一次读取xxx字节大小(累加)
rs.on("data", function (data) {
// console.log(data);
// 在这里进行写入文件操作
ws.write(data);
// ### 不能在这里将可写流关闭,因为数据是一段一段读取的
});
// ws.close(); ---尾部关闭
// ws.end(); ---头部关闭
更加简单的方法:
pipe()
pipe()可以将可读流中的内容,直接输出到可写流中
rs.pipe (ws) ;
直接 pipe 无需再绑定 data 事件再写入,自动开启关闭读/写流
🧨其它操作
fs.stat 检测是文件还是目录
fs.mkdir 创建目录
fs.writeFile 创建写入文件
fs.appendFile 追加文件
fs.readFile 读取文件
fs. readdir 读取目录
fs.unlink 删除文件
fs.rmdir(Sync) 删除目录
重命名文件和目录(剪切) fs.renmae
监视文件更改写入 fs.watchFile
\