node的目录结构
COMMONJS规范
ECMAScript标准的缺陷
- 没有模块系统
- 标准库较少
- 没有标准接口
- 缺乏管理系统
CommonJS规范
1、目的:CommonJS规范为JS指定了一个美好的愿景, 希望JS能够在任何地方运行。
2、CommonJS对模块的定义十分简单: (1) 模块引用:使用require()方法将模块引入到当前运行的环境中,比如下面的例子:
- mode1.js
console.log('这是模块一')
- mod2.js
// 模块的返回值是一个对象,返回被引用模块中的内容
var md = require('./mode1') // 注意这里一定要加上./,不这样写相对路径会报错
效果:
(2) 模块定义:使用exports对象用于导出当前模块的方法或者变量(module对象是模块本身,exports是module的一个属性)
Note:在Node中一个文件就是一个模块
- 模块定义语法:
exports.xxx = function() {};
module.exports = {};
案例:
- mode1.js
module.exports = {
msg:"欢迎学习NodeJS",
add(a,b){
return a+b
}
}
- mode2.js
var md = require('./mode1')
console.log(md.msg)
console.log(`计算123+45=:${md.add(123,45)}`)
(3) 模块标识:模块标识其实就是模块的名字,也就是传递给require()方法的参数,它必须是符合驼峰命名法的字符串,或者是以.、..开头的相对路径、或者绝对路径。
3、模块分为两大类
(1)一类是底层由C++编写的内建模块;
(2)一类是Node提供的核心模块;(核心模块的标识就是模块的名字,比如访问fs模块var fs = require("fs"))
(3)还有一类是用户编写的模块,称为文件模块;(文件模块的标识是文件的路径)
4、global
在node中有一个全局对象 global,它的作用和网页中的window类似;
在全局中创建的变量都会作为global的属性保存;
在全局中创建的函数都会作为global的方法保存;
a = 12 // 定义一个全局变量
console.log(global.a)
5、node模块的原理:在node执行模块中的代码时。它会将代码放到如下函数中:
function (exports, require, module, __filename, __dirname){
模块中的内容....
}
函数的参数含义分别为:
exports:该对象用来将变量或函数暴露到外部
require:函数,用来引入外部的模块
module:module代表的是当前模块本身;exports就是modules的属性;既可以使用exports导出,也可以使用module.exports导出
__filename:当前模块的完整路径
__dirname:当前模块所在文件夹的完整路径
6、module.exports与exports的不同:
(1)一个问题,下面的写法会出错,会返回一个空对象;
exports = {
msg:'有内鬼,终止交易',
add(a,b){
return a+b
}
}
(2)解释:exports与module.exports在栈中的值都指向堆空间中的同一个对象;通过module.exports = {}是修改了exports指向的对象,而exports = {}是修改了exports在栈内存中的变量值。
(3)结论
- 通过exports只能使用.的方式来向暴露内部变量
exports.xxx = xxx - 而module.exports既可以通过.的方式,也可以直接赋值
module.exports.xxx = xx或module.exports = {}
包(package)
包简介
1、定义:将一组相关 的模块组合到一起,形成一组完整的工具
2、CommonJS的包规范由包结构和包描述文件两个部分组成。
- 包结构:用于组织包中的各种文件
- 包描述:描述包的相关信息,以供外部读取分析
3、包结构:包实际上就是一个压缩文件,解压以后还原为目录。符合规范的目录,应该包含如下文件:
4、包描述文件:包描述文件用于表达非代码相关的信息, 它是一个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 |
注意:.json文件中不能写注释
npm简介
1、npm(Node Package Manager):npm是CommonJS包规范理论的一种实践。对于Node而言,NPM帮助其完成了第三方模块的发布、安装和依赖等。借助NPM,Node与第三方模块之间形成了很好的一个生态系统。
2、npm常用命令:
npm -v 查看npm的版本
npm version 查看所有模块的版本
npm search 包名 搜索包
npm install / i 包名 安装包
npm remove / r 包名 删除包
npm install 包名 --save 安装包并添加到依赖中 *****
npm install 下载当前项目所依赖的包(自动根据依赖中的内容下载所需要的包)
npm install 包名 -g 全局安装包(全局安装的包一般都是工具)
3、举例:在执行npm i math --save后package-lock.json文件中的内容,可以发现多了依赖"dependencies";之后在文件中执行npm install时npm就会自动根据依赖中的内容下载所需要的包。
- package-lock.json
{
"name": "mode",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"math": {
"version": "0.0.3",
"resolved": "https://registry.npmmirror.com/math/-/math-0.0.3.tgz",
"integrity": "sha512-xyNJxsEwpYBabFmCgwg7TFRljf5oGgV2h1TqP0H9RnykScaSKgoVlBaEz+Gov8NOdxFagoTzRg1aEBfayi8qQQ=="
}
}
}
4、npm淘宝镜像
(1)npm淘宝镜像地址:npmmirror.com/
(2)npm安装命令:
npm install -g cnpm --registry=https://registry.npmmirror.com
(3)安装后,今后再使用可以用cnpm来执行,速度更快。
Buffer 缓冲区
1、Buffer的结构和数组很像,操作的方法也和数组类似;但数组中不能存储二进制文件,而Buffer就是专门用来存储二进制数据
2、使用Buffer不需要引入模块,直接使用即可;
3、在buffer中存储的都是二进制文件,以16进制的形式显示;
4、实际上Buffer中的内存不是通过JavaScript分配的,而是在底层通过C++申请的。也就是我们可以直接通过Buffer来创建内存中的空间。
5、Buffer中每一个元素的范围都是从0~ff 0-255;实际上一个元素就表示内存中的一个字节;
6、Buffer的官方文档
7、Buffer的大小一旦确定则不能更改
Buffer使用演示
var str = "Hello world"
// 将一个字符串保存在Buffer中
var buf = Buffer.from(str);
// console.log(buf)
// 创建一个10位的Buffer缓冲区
var buf2 = Buffer.alloc(10);
//通过索引,来操作buf2中的元素
buf2[0] = 88
buf2[1] = 255
buf2[2] = 0xaa
buf2[10] = 15 // 不会生效,因为Buffer直接操作底层,索引值超限不会改变Buffer大小
buf2[3] = 257 // 257 超过255,转换为二进制,只取前八位
console.log(buf2)
console.log(buf2[2]) // 在页面或控制台输出是10进制
// Buffer.allocUnsafe(size) 创建一个指定大小的buffer,但buffer中可能含有敏感数据(即allocUnsafe没有清空内存中原来的数据,而是直接使用这块内存,而alloc进行了清空数据)
var buf3 = Buffer.allocUnsafe(10);
console.log(buf3);
fs(文件系统)
fs简介
1、fs模块:在Node通过fs模块来对系统中的文件进行操作,fs模块是node中已经继承好了,不需要在使用npm下载,直接引入即可。
2、fs的使用方法
(1)引入fsvar fs = require("fs");
(2)同步和异步调用:fs模块中的大部分操作都提供了两种方法,同步方法和异步方法
- 同步方法带sync
- 异步方法没有sync,都需要回调函数
(3)写入文件:
- 同步写入
- 异步写入
- 简单写入
- 流式写入
(4)读取文件:
- 同步读取
- 异步读取
- 简单读取
- 流式读取
(5)方法:
- 打开文件
fs.open(path, flags[, mode], callback)
fs.openSync(path, flags[, mode])
- 读写文件
fs.write(fd, string[, position[, encoding]], callback)
fs.writeSync(fd, string[, position[, encoding]])
fs.read(fd, buffer, offset, length, position, callback)
fs.readSync(fd, buffer, offset, length, position)
- 关闭文件
fs.close(fd,callback)
fs.closeSync(fd);
- 简单文件读取和写入
fs.writeFile(file, data[, options], callback)
fs.writeFileSync(file, data[, options])
fs.readFile(path[, options], callback)
fs.readFileSync(path[, options])
- 流式文件读取和写入
- 流式读取和写入适用于一些比较大的文件
fs.createWriteStream(path[, options])
fs.createReadStream(path[, options])
同步文件写入(带Sync)
1、语法:
- 打开文件
fs.openSync(path, flags[, mode])
- path 要打开文件的路径
- flags 打开文件要做的操作类型
r 只读的
w 可写的
- mode 设置文件的操作权限,一般不传
返回值:
- 该方法会返回一个文件的描述符作为结果,我们可以通过该描述符来对文件进行各种操作
- 读写文件
fs.writeFileSync(file, data[, options])
- fd 文件的描述符,需要写入文件的描述符
- string 要写入的内容
- 关闭文件
fs.closeSync(fd);
- fd 要关闭的文件的描述符
2、基本使用案例:
// 导入fs
var fs = require('fs');
// 打开文件
var fd = fs.openSync('hello.txt','w');
// 写入文件
var tp = fs.writeSync(fd, '今天天气真不错~~~');
// console.log(tp1,tp2) //返回的tp是写入内容的字节数
// 关闭文件
fs.closeSync(fd);
异步文件写入(不带Sync)
1、语法:
异步方法不可能有返回值,有返回值的一定是同步方法;异步文件的操作一般都要写callback回调函数
- 打开文件
fs.open(path, flags[, mode], callback)
- 用来打开一个文件
- 异步调用的方法,结果都是通过回调函数返回的
- 回调函数有两个参数:
err 错误对象,如果没有错误则为null
fd 文件描述符
- 读写文件
fs.write(fd, string[, position[, encoding]], callback)
- 关闭文件
fs.close(fd,callback)
2、基本使用案例:
// 引入fs模块
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)
}
})
console.log('程序向下执行~~~')
效果:(注意异步执行的顺序)
简单文件写入
1、语法:
- 简单文件读取和写入(这样子写入是覆盖,不是追加)
fs.writeFile(file, data[, options], callback)
fs.writeFileSync(file, data[, options])
- file 要操作的文件路径
- data 要写入的数据
- options 选项,可以对写入进行一些设置
- callback 当写入完成以后执行的函数
- flag
r 只读
w 可写
a 追加
2、案例:
fs = require('fs')
fs.writeFile("hello3.txt","这是通过writeFile写入的内容",function(err) {
if(!err){
console.log("写入成功~~~");
}
});
流式文件写入
1、背景:同步、异步、简单文件的写入都不适合大文件的写入,性能较差,容易导致内存溢出
2、语法:
- 流式文件读取和写入
- 流式读取和写入适用于一些比较大的文件,因为流式文件支持将一个大文件一部分一部分的写入
fs.createWriteStream(path[, options])
- path 文件路径
- options 配置的参数(注意,遇见options一般写成)
3、案例:
var fs = require("fs")
// 创建一个可写流
var ws = fs.createWriteStream("hello3.txt");
// 可以通过监听流的open和close事件来监听流的打开和关闭
ws.once("open",function(){ // 这里用on和once都可以,因为open事件只执行一次,所以调用once会更好一些
console.log('流打开了~~~');
});
ws.once("close",function(){
console.log('流关闭了~~~');
})
// 通过ws向文件中写入内容
ws.write("通过可写流写入文件的内容");
ws.write("通过可写流写入文件的内容");
// 关闭流,可以等待流文件写完后关闭流文件
ws.end()
简单文件读取
1、语法:
- 简单文件的读取
fs.readFile(path[, options], callback)
fs.readFileSync(path[, options])
- path 要读取的文件的路径
- options 读取的选项
- callback 回调函数,通过回调函数将读取到的内容返回,回调函数的参数:(err,data)
2、案例:
var fs = require("fs")
fs.readFile("hello3.txt",function(err,data){
if(!err){
// console.log(data.toString());
// 将data写入到文件中
fs.writeFile("hello4.txt",data,function(err){
if(!err){
console.log("文件写入成功~~");
}
})
}
})
流式文件读取
1、语法
- 流式文件读取
- 流式读取适用于一些比较大的文件
fs.createReadStream(path[, options])
2、案例:
- 绑定data属性,获取data数据,将数据从可读流传入可写流
// 导入一个文件
var fs = require("fs")
// 创建一个可读流
var rs = fs.createReadStream("hello3.txt");
// 创建一个可写流
var ws = fs.createWriteStream("hello3_write.txt");
// 监听流的开启和关闭
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("可写流关闭了~~");
});
// 如果要读取一个可读流中的数据,必须要为可读流绑定一个data事件,data事件绑定完毕,它会自动开始读取数据
rs.on("data",function(data){
// 这里会将数据作为回调函数来进行输出
// console.log(data.length)
ws.write(data);
});
- 也可以不绑定data,使用pipe()函数
// 导入一个文件
var fs = require("fs")
// 创建一个可读流
var rs = fs.createReadStream("hello3.txt");
// 创建一个可写流
var ws = fs.createWriteStream("hello3_write2.txt");
// 监听流的开启和关闭
rs.once("open",function() {
console.log("可读流打开了~~");
});
rs.once("close",function() {
console.log("可读流关闭了~~");
});
ws.once("open",function() {
console.log("可写流打开了~~");
});
ws.once("close",function() {
console.log("可写流关闭了~~");
});
// 自动将可读流中的内容输出到可写流中
rs.pipe(ws);
fs模块的其他方法
- 验证文件是否存在
fs.existsSync(path)
- 获取文件状态信息
fs.stat(path, callback)
fs.statSync(path)
- 删除文件
fs.unlink(path, callback)
fs.unlinkSync(path)
- 案例演示:
var fs = require("fs");
var isExist = fs.existsSync("./hello.txt");
console.log(isExist);
fs.stat('hello2.txt',function(err,stat){
console.log(stat.isDirectory()); // isDirectory 判断是否是一个文件夹
});
// 删除文件
// fs.unlinkSync("hello.txt");
// 查看文件的结构,. 表示当前文件所在的目录
fs.readdir('.', function(err, files){
if(!err){
// files就是一个字符串数组,每一个元素就是一个文件夹或文件的名字
console.log(files)
}
});
// 截断文件,将文件修改为指定的大小
fs.truncateSync("hello2.txt",2); // 把文件设置为2个字节
// 创建一个目录
//fs.mkdirSync('hello');
// 删除一个目录
// fs.rmdirSync("hello");
// 对文件进行重命名
// fs.rename("hello3.txt","hello33.txt",function(err){
// if(!err){
// console.log("重命名成功");
// }
// })
// 监视文件的修改,间隔时间是1s,每隔1s检查一次
fs.watchFile("hello2.txt",{interval:1000},function(curr, prev){
/*
curr 表示当前文件的状态
prev 表示修改前文件的状态
*/
console.log("当前文件的大小:"+curr.size);
console.log("修改前文件的大小:"+prev.size);
})