Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时。
应用程序运行于单个主线程中,具有线程安全性。Node.js 在其标准库中提供了一组异步的 I/O 原生功能(用以防止 JavaScript 代码被阻塞),并且 Node.js 中的库通常是使用非阻塞的范式编写的(从而使阻塞行为成为例外而不是规范)。
1、模块
独立的高度封装的node.js指令集,就是一个模块。在 node.js 看来,每个 .js 文件都是一个模块
简单来说:一个封装好的软件包,开箱即用。
fs(file system)文件系统模块
node.js中的文件系统,是一个工具,用于在操纵系统上对文件和目录进行操作,基本操作如:创建文件夹,删除文件,读取文件,写入文件等。
const fs = require('fs');
const path = require('path');
// 读文件
// 第一个参数: 文件路径
// 第二个参数: 回调函数
fs.readFile(path.join(__dirname, './x/y.txt'), (err, data) => {
// err: 异常对象,若读文件时没有异常则 err 为 null
// data: 读取出的数据 是一个 byte数组
})
// 写文件
fs.writeFile(path.join(__dirname, './test.txt'), 'this is test.txt', err => {
// err: 异常对象,若写文件时没有异常则 err 为 null
})
// 同步读文件
let data = fs.readFileSync(path.join(__dirname, './x/y.txt'))
// 同步写文件
fs.writeFileSync(path.join(__dirname, './test.txt'), 'this is test.txt')
// 读取文件状态
// 作用: 用于判断路径是否存在文件或文件夹; 判断路径下的是文件还是目录
fs.stat(path.join(__dirname, 'x/y.txt'), (err, stat) => {})
//fs.statSync()//同步读取文件状态
// 读取目录
fs.readdir(path.join(__dirname, 'x'), (err, files) => {})
创建目录
fs.mkdir(path.join(__dirname, 'f'), (err) => {})
// 删除目录
// 目录不为空是删不掉的
fs.rmdir(path.join(__dirname, 'f'), err => {})
// 删除文件
fs.unlink(path.join(__dirname, 'haha'), err => {})
//需要递归删除时也可使用 其中配置对象force:是否强制删除,recursive:是否递归删除
fs.rm(path.join(__dirname,'x'),{force:true,recursive:true})
// 查看路径是否存在
fs.access(path.join(__dirname, 'x'), err => {})
fs.rm
http协议通信模块
let http = require('http')
let server = http.createServer()
os(operating system)操作系统模块
let os = require('os')
// 获取当前几期的CPU信息
console.log(os.cpus())
event 事件模块
该模块可以创建收发事件的实体
const events = require('events');
// 创建一个事件发射器
const eventEmitter = new events.EventEmitter();
class A {
constructor() {
// 监听 eventEmitter 的事件
eventEmitter.on('sayHello', (a, b, c, d, e, f, g) => {
console.log('接收到 sayHello 事件')
console.log(a, b, c, d, e, f, g)
})
}
}
class B {
sendSayHelloEvent() {
// 发送 sayHello 事件
eventEmitter.emit('sayHello', 1, 2, 3, 4, 'z', 'b', 'c')
}
}
const a = new A();
const b = new B();
b.sendSayHelloEvent()
path 路径模块
const path = require('path');
const p = 'd:/xyz/abc.config.png'
// 目录名称
console.log(path.dirname(p))
// 文件全称
console.log(path.basename(p))
// 扩展名
console.log(path.extname(p))
// 结合路径
console.log(path.join(p, '../', 'qwe'))
// join 函数,用于拼接文件路径,通常用来得到一个绝对路径
console.log(path.join('d:/test', '../abc', 'c:/efg', 'hello.js'))
// resolve 函数,用于处理文件路径
// resolve 函数相当于模拟了 cmd 读取路径的过程
console.log(path.resolve('c:/abc', 'd:/demo', 'ok', 'hello.js'))
assert 断言(断言多用于检测参数)
当断言的内容为真时,什么都不会发生
当断言失败时,会抛出一个 AssertionError 的异常并显示第二个参数的消息
深度相等 assert.deepStrictEqual(stu1, stu2, '对象不相等')
正则判断 assert.match('bcd', /^ab/, '正则表达式不匹配')
ok 相当于直接调用 assert() assert.ok()
cluster 集群模块
集群的含义就是一个事物的多个重复个体形成的群落,例如:蚂蚁,蜜蜂形成的蚂蚁群,蜜蜂群就是集群;
多台服务器可以组成服务器主机集群;
多个相同的应用程序也能组成应用程序集群,例如服务器应用集群,数据库集群等
node.js 的集群模块就是创建应用集群用的
集群的作用:集群模块可以让js代码在多个cpu核心上运行多个副本 可以更好的利用cpu,同时执行多份代码
集群中有主从的概念
2、promise
promise中文意思是承诺,承诺一定在做完promise自己的事后,然后再做后面的事情。
作用:promise工具让异步调用看上去像同步一样,减少开发者在异步开发时的负担,少出异步错误。
Promise 的基本语法;Promise 里面的回调函数,将在promise对象实例化完成后被调用
const promise = new Promise((resolve, reject) => {
// 此回调函数就是promise承诺要完成的事情
// resolve: 当promise对象正确的处理完自己的事情时,一定要调用 resolve()
// reject: 本意是驳回,在此处代表promise承诺的事情,出现异常
// resolve 和 reject 在 promise内部 必须从中选择一个进行调用,不要同时调用
setTimeout(() => {
console.log('a')
// 完成promise承诺的事情后
// 调用 resolve
resolve('resolve的参数')
// 此处调用reject 代表promise 异常
// reject 的参数就是 异常对象
// reject(new Error('异常'))
}, 1000)
});
then: 代表然后,当promise内部执行resolve的时候就会执行 then 中的回调函数,then 的回调函数中可以获取到 resolve 的参数
catch: 代表捕获异常,当promise内发生异常,或promise调用了reject,就会执行catch中的回调函数
finally: 代表不管promise的承诺是否兑现,都会执行finally中的代码
链式调用:不停通过点运算符调用方法的过程叫链式调用 通常链式调用的方法返回的是对象本身
promise 链式调用是连续调用 then 方法,为了形成promise的链式调用 then 的回调函数中需要返回一个promise对象
catch 和 finally 应该放在链式调用的最后面
promise的静态方法
//创建一个能够被resolve的promise对象
let promise = Promise.resolve(123)
// 创建一个能够被reject的promise对象
let promise = Promise.reject('异常')
// 所有的promise都执行了resolve后,就会执行all的then方法
// 参数是 promise 数组
let promise = Promise.all([])
//多个promise进行竞赛,第一个promise调用了resolve后,race所返回的promise就会调用then后的内容
let promise = Promise.race([])
//此处的promise就是race返回的promise
promise.then(() => {
console.log('比赛结束')
})
异步函数:异步函数本质上就是一个返回promise对象的函数
声明异步函数:使用 async 关键字
异步函数中 return 的内容:本质上就是 resolve 函数的参数
调用异步函数
情况一: 直接调用
add(1, 2).then(data => {
console.log(data)
});
情况二: 在另一个异步函数中调用,配合 await 关键字进行假同步调用
(async () => {
// await 关键字只能在 async 声明的函数中使用
// 直接使用 await 关键字调用异步函数
// 这样的话就不需要调用 promise 的 then 方法了
// 此处的返回值result就是异步函数add的返回值
let result = await add(3, 4)
console.log(result)
result = await _add(5, 9)
console.log(result)
// 用 await 调用的异步函数,可以当作是同步调用
// 此处: 也就是说 add 方法没有返回值之前 不会调用 _add
})()
3、http服务器
简易http服务器
// 引入网络通信模块
const http = require('http');
// 创建一个服务器对象
const app = http.createServer()
// 声明端口号
const port = 1024
// 绑定request事件
app.on('request', (req, res)=>{
// req: 请求对象,客户端发送来的数据封装到该对象中
// res: 响应对象,服务器用于做出回应的对象
// 服务器向客户端写数据
res.write('hello\n')
// 结束响应并传递数据
// 设置响应头
// 告诉客户端返回数据的类型
res.setHeader('Content-Type', 'text/plain;charset=utf-8')
// local 本地: 指的是当前服务器所在的地方
res.write(`本地ip: ${req.socket.localAddress}\n`) // 当前服务器ip地址
res.write(`本地port: ${req.socket.localPort}\n`) // 当前服务器端口号
// remote 远程: 指的就是客户端
res.write(`远程ip: ${req.socket.remoteAddress}\n`) // 远程ip
res.write(`远程port: ${req.socket.remotePort}\n`) // 远程端口号
res.end('world')
})
// 启动服务器
// 第二个参数是服务器绑定的ip地址
// 127.0.0.1: 本机地址
// 192.168.124.48: 本机在路由器内网中的ip
// 0.0.0.0: 尽可能多的绑定本机支持的所有ip
app.listen(port, () => {
// 当服务器启动成功后,就会调用此函数
console.log(`server start: http://127.0.0.1:${port}`)
})
静态资源服务器
不会发生变化的资源就是静态资源,静态资源一般指不会变化的文件
静态资源服务器:用于访问静态资源的服务器
定义静态资源文件夹
const staticDirPath = path.join(__dirname, 'public')
4、express服务器
const express = require('express'); // 引入 express 模块
const app = express(); // 创建服务器应用程序对象
const port = 80
// 注册接口
// 接口: 网络接口,一个 url 路径,对应服务器上一个提供服务的功能,这个功能就是服务器上的一个接口
// 第一个参数: url
// 第二个参数: 接口回调
app.get('/greeting', (req, res) => {
res.end('hello world')
})
app.get('/', (req, res) => {
res.end('ok123')
})
// 启动服务器
app.listen(port, () => {
console.log(`server start on: http://127.0.0.1`)
})
创建静态资源服务器
静态资源文件夹的声明放在所有接口的上面
使用use安装中间件(插件)
xpress.static 声明一个静态资源文件夹的路径
第一个参数: 访问静态资源时 url 前缀(含义是: 将 /static 路径 映射到 public 文件夹)
app.use('/static', express.static(path.join(__dirname, 'public')))
express 重定向
res.redirect('/hello')
express路由
const express = require('express')
const app = express();
// 引入路由器
const userRouter = require('./routers/userRouter');
// 安装路由器
// 第一个参数: 路由前缀
app.use('/user', userRouter)
// 路径参数
// :goodsId 是路径参数
app.get('/goods/:goodsId/user/:userId', (req, res) => {
res.json(req.params)
})
app.listen(80, () => {
console.log('server start on: http://127.0.0.1')
})
5、跨域与中间件
中间件与异常处理
// 声明中间件
app.use((req, res, next) => {
// req, res 就是接口中的请求对象和响应对象
// next 函数在中间件中必须调用
// 调用next有两种情况
// 情况一: 不带参数的调用
// 含义: 放行
console.log('中间件1')
next()
// 情况二: 带参数
// 含义: 直接将参数返回给页面
// next(new Error(`这是next返回给页面的内容,强制被认为是异常,所以状态码为500`))
})
app.get('/test1', async (req, res, next) => {
// 接口中的next作用和中间件中一样,但是接口中的next必须传参数
// next(new Error('test1 error'))
// 接口内抛出的异常也能被最后个中间件捕获
// throw new Error('test1 error')
res.json({msg: 1})
})
在所有中间件的末尾,添加一个异常处理的中间件
此异常捕获中间件参数必须以 err 开头
此中间件的触发条件有:
1. 任意接口抛出异常
2. 任意接口或中间件的next调用后传了参数
跨域
添加一个中间件拦截所有请求
app.all('*', (req, res, next) => {
// 设置允许跨域的响应头
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Methods', '*')
res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
next()
})
若不记得跨域响应头有哪些,可以使用 cors 包
npm i cors 安装,引入并使用 app.use(cors())