Node.js基础知识

119 阅读11分钟

node的目录结构

image.png

nodeJS官方文档

COMMONJS规范

ECMAScript标准的缺陷

  • 没有模块系统
  • 标准库较少
  • 没有标准接口
  • 缺乏管理系统

CommonJS规范

1、目的:CommonJS规范为JS指定了一个美好的愿景, 希望JS能够在任何地方运行。

2、CommonJS对模块的定义十分简单: (1) 模块引用:使用require()方法将模块引入到当前运行的环境中,比如下面的例子:

  • mode1.js
console.log('这是模块一')
  • mod2.js
// 模块的返回值是一个对象,返回被引用模块中的内容
var md = require('./mode1') // 注意这里一定要加上./,不这样写相对路径会报错

效果:

image.png

(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:函数,用来引入外部的模块

modulemodule代表的是当前模块本身;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在栈内存中的变量值。

image.png

image.png

(3)结论

  • 通过exports只能使用.的方式来向暴露内部变量 exports.xxx = xxx
  • 而module.exports既可以通过.的方式,也可以直接赋值 module.exports.xxx = xxmodule.exports = {}

包(package)

包简介

1、定义:将一组相关 的模块组合到一起,形成一组完整的工具

2、CommonJS的包规范由包结构和包描述文件两个部分组成。

  • 包结构:用于组织包中的各种文件
  • 包描述:描述包的相关信息,以供外部读取分析

3、包结构:包实际上就是一个压缩文件,解压以后还原为目录。符合规范的目录,应该包含如下文件:

image.png

4、包描述文件:包描述文件用于表达非代码相关的信息, 它是一个JSON格式的文件 – package.json,位于包的根目录下,是包的重要组成部分。

  • package.json中的字段:
字段
namedescriptionversionkeywordsmaintainerscontributors
bugslicensesrepositoriesdependencieshomepageos
cpuenginebuiltindirectoriesimplementsscripts
authorbinmaindevDependencies

注意:.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('程序向下执行~~~')

效果:(注意异步执行的顺序)

image.png

简单文件写入

1、语法:

- 简单文件读取和写入(这样子写入是覆盖,不是追加)
fs.writeFile(file, data[, options], callback)
fs.writeFileSync(file, data[, options])
    - file 要操作的文件路径
    - data 要写入的数据
    - options 选项,可以对写入进行一些设置
    - callback 当写入完成以后执行的函数
    - flag
        r 只读
        w 可写
        a 追加

image.png

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);
})