前言
nodejs入门demo,涉及node模块规范,node的异步编程风格,node核心模块:events、fs、stream、http。
common.js
在编程语言中,每个文件都是一个独立的模块规范,具备独立的作用域,无法在一个文件中访问另一个文件的变量,函数,类等,在node中,采用commonjs规范来解决这一问题。
module和exports
- moudle其实就是一个对象,module.exports是对外的接口
- 通过把要导出的变量、函数、类挂到module.exports上,可以实现在其它模块的因引入
require
- require用于加载模块文件,require读取模块文件,返回该模块的exports对象。如果目标模块没有exports则会报错
自定义模块
module1.js
function sum(a,b) {
return a + b
}
module.exports = sum
module2.js
function max(a,b) {
if (a>b) return a
else return b
}
function min(a,b) {
if (a<b) return a
else return b
}
module.exports = { max, min }
main.js
const sum = require('./module1')
const {max, min } = require('./module2')
const math = require('./module2')
console.log(sum(1,2)) // 3
console.log(max(1,2),min(1,2)) // 2 1
console.log(math.max(1,2),math.min(1,2)) // 2 1
commonjs是执行时引入,不参与编译过程,因此将其放入到代码逻辑中也不会报错,这点与ES6 module不同
像下面这样使用也是可以的
const a = 1
const b = 2
if(a===b) {
const sum = require('./module1')
console.log(sum(a,b))
} else {
const { max } = require('./module2')
console.log(max(a,b))
}
异步编程风格
在上篇文章中说过node是采用异步来处理耗时的任务,因此异步在node中无处不在,异步的实现主要有以下三种:
- 回调函数
- promise
- async/await
回调函数
node中扩展的api几乎全部支持回调,但是要明确的是:虽然回调是异步的实现方法,但回调本身和异步并没有任何关系,回调函数是否触发异步取决去事件是否为异步事件
以下是一个回调函数的使用,但他不是异步的
function test(callback){
console.log('HELLO WORLD')
callback('HELLO world')
console.log('hello word')
}
test(function(data){
console.log(data)
})
console.log('123')
// HELLO WORLD HELLO world hello word 123
node中回调函数的语法几乎全部相同,前面的参数为实际需要的param,将错误对象作为回调参数来调用回调函数
以下是一个常用的异步函数,我读取一个不存在的文件,它会异步的给我抛出错误
const fs = require('fs')
fs.readFile('./test.txt',(err,data)=>{
if (err){
console.log('error',err)
} else {
console.log(data)
}
})
console.log('123')
/**
123
error [Error: ENOENT: no such file or directory, open 'D:\nodejs\Asynchronous\test.txt'] {
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path: 'D:\\nodejs\\Asynchronous\\test.txt'
}
/
promise
promise是ES6新增的语法规范,用来表示异步操作的最终执行结果
- 一个promise有三种状态:pending(初始)、fulfilled(成功)、rejected(失败)。pending状态会转为fulfilled或rejected状态,且这个过程不可逆
- promise接受一个执行函数,该函数接收resolve和reject两个回调函数,分别对应fulfilled和rejected状态对应的函数,进入fulfilled状态时,需要执行resolve函数,否则执行reject函数。
- promise的then方法和catch方法会返回一个新的promise对象,then方法用来链式调用前一个promise的resove函数,catch则用来捕获reject。
then和catch执行正常继续返回resolve,执行过程中出现异常返回reject
promise在使用时可以视为一个异步容器,将要实现的功能放到promise的执行函数中,执行成功和失败时分别调用resolve和reject即可。下面使用promise来改写文件读取函数:
function readFile(file){
return new Promise((resolve,reject) => {
fs.readFile('./test.txt',(err,data)=>{
if (err){
console.log('error',err)
reject(err)
} else {
console.log(data)
resolve(data)
}
})
})
}
使用时,到底是触发then方法还是catch方法取决于promise是触发reslove还是reject:
readFile('./test.txt')
.then(data => {
console.log(data) // 此时的data已经不再是被promise对象包裹的data,而是data本身
})
.catch(err=>{
console.log(err)
})
async/await
async/await是ES7出现的,对promise的语法糖,它能让异步代码写起来更像同步
- async和await必须成对出现,async本身会把他后面的内容包装成一个promise
- await相当于then方法,它会获取resolve状态时的数据,此时拿到的数据不再处于一个promise对象里
try-catch 相当于ctach方法,他能捕获reject函数触发的异常状态
使用时,到底是触发await方法还是进入catch方法取决于promise是触发reslove还是reject:
async function readFileAsync () {
try {
const file = await readFile('./test.txt')
console.log(file)
} catch (err) {
console.log(err)
}
}
readFileAsync()
核心模块
nodejs常用的模块很多,这里列举常见的四个:events、fs、stream、http。毫无疑问,每个模块都是通过异步实现。
events
由于node是事件驱动的,因此大多数模块都继承于events,事件模块实现步骤一般分为三步:
- 创建事件实例
- 注册事件
- 触发事件
const EventEmitter = require('events')
// 实例化
const emitter = new EventEmitter()
// 注册事件
emitter.on('click', function(param) {
console.log('click', param)
})
// once 只触发一次
// emitter.once('click', function(param) {
// console.log('clickOnce', param)
// })
// 触发事件
emitter.emit('click','hello')
emitter.emit('click','word')
fs
File Syste 是node中用来操作文件的核心模块
函数 | 含义 |
---|---|
readFile(path, [,options], callback) | 读取文件 |
writeFile(path, content, [,options], callback) | 写入文件 |
appendFile(path, content, [,options], callback) | 文件写入内容 |
unlink(path, callback) | 删除文件 |
mkdir(path, callback) | 创建文件夹 |
readdir(path, content, [,options], callback) | 读取文件夹 |
rmdir(path, callback) | 删除文件夹 |
参数
参数 | 含义 |
---|---|
path | 文件(夹)路径 |
content | 文件内容 |
options | 可选参数 |
callback | 回调函数 |
举例
const fs = require('fs')
// 读取文件
fs.readFile('./test.txt','utf8', (err,data)=>{
if (err){
console.log('error',err)
} else {
console.log(data)
}
})
// 文件写入
fs.readFile('./test.txt','hello word', {encoding:'utf-8'},
(err,data)=>{
if (err){
console.log('error',err)
} else {
console.log(data)
}
})
stream
文件IO操作是比较耗时的,所以一般采用stream流的方式进行传输,stream流是EventEmitter的一个实例。http接口的request就是采用stream流进行传输的。
stream有四种常用事件:
- data: 存在数据可读时触发
- end: 没有数据读取时触发
- error: 读取或写入出错时触发
- finish: 所有数据写入底层时触发
四种类型:
- Readable:可读
- Writable: 可写
- Duplex: 可读可写
- Transform: 数据转换
stream流操作
名称 | 特征 |
---|---|
读取流 | 以小块的方式读取文件,内存占用率低 |
写入流 | 以小块的方式写入文件 |
管道 | 从一个流将数据存到另一个流,多个管道可以并行处理,是最常用的方式 |
数据转换流 | 基于管道,将读取流通过pipe()传入转换流,实现对数据的转换 |
const fs = require('fs')
const zlib = require('zlib')
//====================创建读取流&写入流==============
const readStream = fs.createReadStream('./fs.js',{ encoding: 'utf-8'}) // 读取文件的stream对象
const writeStream = fs.createWriteStream('./streamOut2.js',{ encoding: 'utf-8'}) // 写入文件的stream对象
//====================读取流&写入流 使用==============
// 文件分块逐个读取写入
readStream.on('data',(chunk)=>{
writeStream.write(chunk)
})
// 读取完毕 写入完成
readStream.on('end',()=>{
writeStream.end()
console.log('write end')
})
// 写入完成
writeStream.on('finish',()=>{
console.log('write finish')
})
// 读取错误
readStream.on('error',(err)=>{
console.log('wirte error', err)
})
// 写入错误
writeStream.on('error',(err)=>{
console.log('wirte error', err)
})
//====================管道流 使用==============
readStream.pipe(writeStream) // 使用管道
writeStream.on('finish',()=>{
console.log('pipe write finish')
})
//====================转换流 使用==============
fs.createReadStream('./events.js',{ encoding: 'utf-8'}) // 创建文件读取流
.pipe(zlib.createGzip()) // 创建gzip转换流
.pipe(fs.createWriteStream('events.js.gz')) // 写入流
http
http模块用于创建http服务器并处理请求,http分为和客户端和服务端
- http客户端:向目标地址发送请求,回调一个读取流函数
- http服务端
客户端我以请求自己博客的一张图片并保存至本地为例
const fs = require('fs')
const https = require('https')
const req = https.request('https://img-blog.csdnimg.cn/b75770b282224c5c9858740eb83ab2c7.png#pic_center',
(res)=>{
console.log(res.statusCode)
res.pipe(fs.createWriteStream('./bolg.png'))
}
)
req.end()
服务端使用http内置模块做个简单示例
const http = require('http')
const server = http.createServer((req,res)=>{
res.end(JSON.stringify(req.headers))
})
server.listen(8899, () => console.log('listen on 8899'))
打开http://localhost:8899/接口,页面内容如下
总结
- node模块化 ——common.js
- 异步编程风格——回调、promis、async/await
- node核心模块——events、fs、stream、http