nodejs模块

nodejs模块:相当于封装的js文件

一、定义

  1. 在nodejs中,一个js文件就是一个模块
  2. 每个模块,都有一个独立的作用域,在自己 js文件中声明的变量和函数,只能在自己所在的模块中使用

二、分类

模块共有三种:核心模块(nodejs自带模块)、第三方模块(其他人封装好的模块,需要先下载再引入)、自定义模块(可自行编写的灵活模块)

三、模块的导入

1. 语法:

1
2
3
const fs = require('fs')  // 核心模块的导入
const moment = require('moment') // 第三方模块的导入,方法应参考官方文档
const lalala = require('./lalala') // 自定义模块的导入

导入的时候应该使用一个变量去接收

2. 模块导入的顺序:

① 先看是否是路径格式,如果是路径格式那就当作自定义模块,根据路径查找对应的文件进行加载;
② 再看是不是模块名格式,是模块名格式的话
③ 最后看是不是核心模块,是的话按照核心模块导入;如果不是核心模块,就当作第三方模块处理(会去当前文件夹中的node_modules中查找对应的包,如果当前文件夹内没有,会往上一级目录查找,直到根目录,根目录也没有的话就会报错)

所以 const fs = require('./fs') 会当作自定义模块查找

四、模块的导出

  • 模块一定要导出才有封装的意义,不然模块中声明的变量和函数都是无法使用的

  • 每个模块有一个独立的module全局变量(对象),在module对象中有一个exports的属性,代表当前模块的导出内容

语法:

  1. module.exports.属性名 = 函数
  2. module.exports = fn (只需要导出一个内容时可以直接覆盖module)
  3. module.exports = {fn, num, str} (以对象的形式覆盖)

module.exports和exports的区别

虽然在模块中,module.exports和exports指向的是同一个对象(module.exports === exports),但是一个模块的导出内容只是module.exports对应的内容

  1. 直接添加属性的导出方法,exports也可以使用
  2. 覆盖方法对exports无效(覆盖了exports的指向,但是导出的依然是module.exports)

五、部分核心模块的使用

1. fs模块(文件读写模块)

 ① 写入文件

语法:writeFile(文件路径, 写入的内容, 回调函数(err))

1
2
3
4
5
6
7
8
9
10
11
const fs = require('fs')
fs.writeFile('xxx.txt', 'hello world', err => {
if (err) {
// console.log(err)
// return
throw err // 抛出异常,并中断整个程序的运行,严重错误中配合try...catch处理
// throw会中断整个程序的运行
// return只会中断该函数的运行
}
console.log('写入成功')
})

特点:

  • 如果文件存在,会覆盖原本内容

  • 如果文件不存在,会创建一个文件

  • 如果文件路径不存在,会报错,不会新建文件夹去写入

    ② 读取文件

语法:fs.readFile(文件路径, 'utf8', 回调函数(err, data))

1
2
3
4
5
6
7
8
9
10
const fs = require('fs')
fs.readFile('xxx.txt', (err, data) => {
if (err) {
return console.log(err);
}
console.log(data.toString());
})
// err:错误信息,null表示没有错误,node中应该优先处理错误(加一个判断)
// utf8: 编码,写utf-8一样,读取字符串的时候使用,如果读取的是图片、视频等二进制数据不要加
// data:读取的数据,如果参数里没有'utf8',那么为二进制编码(buffer)(显示为16进制),需要用toString方法

​ ③ 追加文件

语法:fs.appendFile(文件路径, 追加内容, 回调函数(err))

1
2
3
4
5
const fs = require('fs')
fs.appendFile('xxx.txt', '哈哈哈', err => {
if (err) return console.log('追加失败');
console.log('追加成功');
})

通常文件的读写都是异步的,不会阻塞主线程代码的执行
fs.readFileSync('xxx.txt', 'utf8')
fs.writeFileSync('xxx.txt', data)
fs.appendFileSync('xxx.txt', data)
而这三个都是同步的,代码可读性好,但是性能差(不需要回调函数)

2. path模块

读写文件的规范:一律使用绝对路径
因为只要是相对路径,就是相对于node命令执行的路径,一旦命令行路径改变就会报错
要解决这个问题,可以使用 __dirname + '/xxx.txt'来作为路径使用
cd ..👉回到上一级

语法:path.join(路径A, 路径B, 路径C)

join方法会根据不同的系统(Windows、mac、linux)自动按照对应的分隔符进行路径合并

1
2
3
4
const path = require('path')
const filePath = path.join('aa', 'bb', 'cc.js')
console.log(filePath);// aa\bb\cc.js 👉 windows系统下的格式
const filePath_join = path.join(__dirname, 'xxx.txt') // 这才是通常使用的格式

如果要回到上一级目录的话,可以使用path.resolve方法

1
2
3
4
5
const path = require('path')
// 目前文件目录:/server/app.js
const filePath = path.resolve(__dirname, '..', 'assert', 'favicon.ico')
// 可以获取到/assert/favicon.ico
// 相当于用每个参数执行了cd命令,所以中间如果有/开头的目录则会回到根目录

3. http模块(建立服务器)

响应请求的简单步骤:

​ ① 建立服务器,监听端口

1
2
3
4
5
const http = require('http')
const server = http.createServer()
server.listen(8080, () => {
console.log("服务器已启动,端口号8080")
})

​ ② 给服务器添加请求事件(请求报文,响应报文)

​ ③ 根据请求报文的内容做判断,返回数据或者404错误

​ ④ 返回数据的话先找到文件,然后发送响应报文(状态行,响应头,响应体)

1
2
3
4
5
6
7
8
9
10
11
12
13
server.on('request', (req, res) => {
// 设置响应头必须放在响应体的前面
res.setHeader('content-type', 'text/html; charset=utf-8')
if (req.url === '/' || req.url === '/index.html') {
res.write('first')
res.write('second')
res.write('third')
res.end('end') // 如果只需要响应简单信息那么写在这里面就好
} else {
res.statusCode = 404
res.end('404')
}
})

4. url模块(用于获取url中包含的所有信息 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const url = require('url')  // 引入url模块
url.parse(filePath, true)
// url.parse(路径,布尔值)
// 参数一:需要解析的路径地址(http://localhost:8080/delete?id=xx&username=xx)
// 参数二:true表示需要对query属性值进行解析,false时显示id=xx&username=xx
// 输出结果:
{
protocol : 'http',
auth : null,
host : 'localhost:8080',
port : '8080',
hostname : 'localhost',
hash : null,
search : '?id=xx&username=xx',
query : 'id=xx&username=xx',
pathname : '/delete',
path : '/delete?id=xx&username=xx',
href : 'http://localhost:8080/delete?id=xx&username=xx'
}

5. queryString模块(用于将url模块获取的query转化为对象)

处理POST请求的注意点:
req.method用来获取请求方式,其值GET和POST只会是大写的
② node默认不会解析请求体内容(恶意post请求会消耗大量性能),需要自行获取信任的内容
③ POST请求的参数可能很大,一次性拿不完,需要逐步获取,所以需要用到data和end事件

data和end的使用:

1
2
3
4
5
6
7
8
9
let result = ''
req.on('data', chunk => {
result += chunk
console.log('已获取数据') // 因为会逐步接受,所以会显示很多次
})
req.on('end', () => {
console.log('数据获取结束')
})
// result便是最终获取的请求体内容

​ ④ 这样就可以使用queryString模块内的方法解析post请求内容了

1
2
3
// result = 'name=xxx&title=xxx&content=xxx'
const querystring = require('querystring')
const query = querystring.parse(result)