Nodejs的核心组成
- Natives modules 内置核心模块
- 当前层内容由JS实现
- 提供应用程序可直接调用库,例如fs, path, http等
- JS语言无法直接操作底层硬件设施之
- Builtin modules 胶水层
- c/c++编写
- 底层
- V8: 执行JS代码,提供桥梁接口
- Libuv: 事件循环、事件队列、异步IO
- 第三方模块: zlib、http、c-cares、http parser等 事件驱动
const EventEmitter = require('events')
const myEvent = new EventEmitter()
myEvent.on('event1', () => {
console.log('event1执行了...')
})
myEvent.on('event1', () => {
console.log('event11111执行了...')
})
myEvent.emit('event1')
单线程
const http = require('http')
function sleepTime(time) {
const sleep = Date.now() + time * 1000
while (Date.now() < sleep) { }
return
}
sleepTime(4)
const server = http.createServer((res, req) => {
res.end('server starting...')
})
server.listen(8080, () => {
console.log('服务启动了...')
})
nodejs实现API服务
- ts-node
- tsconfig.json
- express
- @types/express
全局对象 global
- 全局对象可以看做是全局变量的宿主
- 默认情况this是空对象,和global并不是一样的
全局变量
__filename: 返回正在执行脚本文件的绝对路径__dirname: 返回正在执行脚本所在的目录(不包含当前文件名)- timer类函数:执行顺序与事件循环间的关系
- process: 提供与当前进程互动的接口
- require: 实现模块的加载
- module、exports: 处理模块的导出
process
- 资源: CPU 内存
- process.memoryUsage() 内存信息
- process.cpuUsage() CPU信息
- 运行环境:运行目录、node环境、cpu架构、用户环境、系统平台
- process.cwd() 运行目录
- process.version node版本
- process.versions 各种版本
- process.arch x64
- process.env.NODE_ENV 环境
- process.env.PATH 系统环境变量
- process.env.USERPROFILE C:\Users\admin 管理员目录(windows平台,Mac平台用HOME)
- process.platform win32
- 运行状态:启动参数、PID、运行时间
- process.argv [node.exe路径、当前文件路径、参数1、参数2...]
- process.argv0 只有argv0
- process.pid/ppid
- process.uptime() 运行开始到结束消耗的时间
- 事件
process.on('exit', (code) => {
//只能写入同步代码
console.log('exit ' + code)
})
process.on('beforeExit', (code) => {
//可以写异步代码
console.log('beforeExit ' + code)
})
console.log('代码执行完了')
//执行结果
代码执行完了
beforeExit 0
exit 0
process.exit()
- 标准输出、输入、错误
console.log = function(data){
process.stdout.write('----' + data + '\n')
}
console.log(1)
console.log(11)
//读出文件的内容
const fs = require('fs')
fs.createReadStream('test.txt')
.pipe(process.stdout)
process.stdin.pipe(process.stdout)
process.stdin.on('readable', () => {
let chunk = process.stdin.read()
if(chunk !== null){
process.stdout.write('data ' + chunk)
}
})
Buffer
Buffer让JavaScript可以操作二进制
- 无需require的一个全局变量
- 实现Nodejs平台下的二进制数据操作
- 不占据V8堆内存大小的内存空间
- 内存的使用由Node来控制,由V8的GC回收
- 一般配合Stream流使用,充当数据缓冲区
创建BUffer实例
- alloc: 创建指定字节大小的buffer
- allocUnsafe: 创建指定大小的buffer(不安全)
- from: 接收数据,创建buffer
const b1 = Buffer.alloc(10)
const b2 = Buffer.allocUnsafe(13)
console.log(b1)
console.log(b2)
const b1 = Buffer.from('1')
console.log(b1)
const b2 = Buffer.from([0xe4, 0xb8, 0xad])
console.log(b2)
console.log(b2.toString()) //中
const b3 = Buffer.alloc(3)
const b4 = Buffer.from(b3)
console.log(b3)
console.log(b4)
b3[0] = 1 //修改b3不影响b4
console.log(b3)
console.log(b4)
Buffer实例方法
- fill: 使用数据填充buffer
- write: 向buffer中写入数据
- toString: 从buffer中提取数据
- slice: 截取buffer
- indexOf:在buffer中查找数据
- copy:拷贝buffer中的数据
fill 会重复写入
let buf = Buffer.alloc(7)
buf.fill('123', 1, 3) // [1, 3) 填充位置
console.log(buf)
console.log(buf.toString())
write 不会重复写入
let buf = Buffer.alloc(7)
buf.write('123', 1, 4) // 写入开始位置,写入长度
console.log(buf)
console.log(buf.toString())
toString
let buf = Buffer.alloc(7)
buf = Buffer.from('高级编程')
console.log(buf)
console.log(buf.toString('utf-8', 3, 9)) //起始位置, 结束位置
slice
let buf = Buffer.alloc(7)
buf = Buffer.from('高级编程')
let b1 = buf.slice(3, 9) // 级编 [3, 9)
// let b1 = buf.slice(-3) // 程
console.log(b1)
console.log(b1.toString()) //起始位置, 结束位置
indexOf
let buf = Buffer.alloc(7)
buf = Buffer.from('a高级b高级')
console.log(buf)
console.log(buf.indexOf('高', 7)) //8 检索起始位置
copy
let b1 = Buffer.alloc(6)
let b2 = Buffer.from('高级编程')
b2.copy(b1, 3, 3, 6) // b1的第3个位置开始写入 b2的[3, 6)为选择的字节
console.log(b1.toString()) // 级
console.log(b2.toString())
Buffer静态方法
let b1 = Buffer.from('高级')
let b2 = Buffer.from('编程')
let b3 = Buffer.concat([b1, b2], 9) //限制合并后的长度
console.log(b3) // 级
console.log(b3.toString()) //高级编
let b4 = '123'
console.log(Buffer.isBuffer(b4)) //fasle
自定义buffer-split
Buffer.prototype.split = function (sep) {
let len = Buffer.from(sep).length
let ret = []
let start = 0
let offset = 0
while ((offset = this.indexOf(sep, start)) !== -1) {
ret.push(this.slice(start, offset))
start = offset + len
}
ret.push(this.slice(start))
return ret
}
let buf = Buffer.from('gfgfdfdgfgf')
let bufArr = buf.split('g')
console.log(bufArr)
内置模块 path
用于处理文件/目录的路径
- .basename() 获取路径中的基础名称
- .dirname() 获取路径中的目录名称(路径)
- .extname() 获取路径中的扩展名称
- .isAbsolute() 判断路径是否为绝对路径
- .join() 拼接多个路径片段
- .resolve() 返回绝对路径
- .parse() 解析路径
- .format() 序列化路径
- .normalize() 规范化路径
basename
//basename 返回路径的最后一部分
const path = require('path')
console.log(path.basename('/a/b/c')) //c
console.log(path.basename('/a/b/c/')) //c 结尾处的路径分隔符会被忽略
console.log(path.basename('/a/b/c/d.js')) //d.js
console.log(path.basename('/a/b/c/d.js', '.js')) //d
console.log(path.basename('/a/b/c/d.js', '.css')) //d.js
dirname
//dirname 返回路径中最后一部分的上一层目录所在路径
const path = require('path')
console.log(path.dirname('/a/b/c')) // /a/b
console.log(path.dirname('/a/b/c/')) // a/b
console.log(path.dirname('/a/b/c/d.js')) // /a/b/c
extname
//extname 返回响应文件的后缀名
// 存在多个点,匹配的是最后一个点到结尾的位置
const path = require('path')
console.log(path.extname('/a/b/c')) // ''
console.log(path.extname('/a/b/c/d.js')) // 'js'
console.log(path.extname('/a/b/c/d.js.css.html')) // html
console.log(path.extname('/a/b/c/d.js.')) // .
parse 解析路径
const path = require('path')
const obj = path.parse('/a/b/c/index.html')
console.log(obj)
//打印结果
{
root: '/',
dir: '/a/b/c',
base: 'index.html',
ext: '.html',
name: 'index'
}
const path = require('path')
const obj = path.parse('/a/b/c')
//const obj = path.parse('/a/b/c/')
console.log(obj)
//打印结果
{
root: '/',
dir: '/a/b',
base: 'c',
ext: '',
name: 'c'
}
const path = require('path')
const obj = path.parse('./a/b/c')
//const obj = path.parse('/a/b/c/')
console.log(obj)
//打印结果
{
root: '',
dir: './a/b',
base: 'c',
ext: '',
name: 'c'
}
format 序列化路径
const path = require('path')
const obj = path.parse('./a/b/c/')
console.log(path.format(obj)) // ./a/b\c
isAbsolute
const path = require('path')
console.log(path.isAbsolute('foo')) //false
console.log(path.isAbsolute('/foo')) //true
console.log(path.isAbsolute('///foo')) //true
console.log(path.isAbsolute('')) //false
console.log(path.isAbsolute('.')) //false
console.log(path.isAbsolute('../bar')) //false
join
const path = require('path')
console.log(path.join('a/b', 'c', 'index.html')) // a/b/c/index.html
console.log(path.join('/a/b', 'c', 'index.html')) // /a/b/c/index.html
console.log(path.join('/a/b', 'c', '../', 'index.html')) // /a/b/index.html
console.log(path.join('/a/b', 'c', './', 'index.html')) // /a/b/c/index.html
console.log(path.join('/a/b', 'c', '', 'index.html')) // /a/b/c/index.html
console.log(path.join('')) // .
normalize
const path = require('path')
console.log(path.normalize('')) // .
console.log(path.normalize('a/b/c/d')) // a\b\c\d
console.log(path.normalize('a///b/c../d')) // a\b\c..\d
console.log(path.normalize('a//\\/b/c\\/d')) // a\b\c\d
console.log(path.normalize('a//\b/c\\/d')) // a\c\d
resolve
const path = require('path')
console.log(path.resolve()) // D:\study\nodejs_study
console.log(path.resolve('a', 'b')) // D:\study\nodejs_study\a\b
console.log(path.resolve('a', '/b')) // D:\b
console.log(path.resolve('/a', 'b')) // D:\a\b
console.log(path.resolve('/a', '/b')) // .D:\b
console.log(path.resolve('/a', '../b')) // .D:\b
内置核心模块 fs
提供文件系统操作的API
常见flag操作符
+ r: 表示可读
+ w:表示可写
+ s:表示同步
+ +:表示执行相反操作
+ x:表示排它操作
+ a: 表示追加操作
fd就是操作系统分配给被打开文件的标识
文件操作API
- readFile:从执行文件中读取数据(路径不存在会报错)
- writeFile:向指定文件中写入数据(路径不存在,会创建文件)
- appendFile:追加的方式向指定文件中写入数据
- copyFile:将某个文件中的数据拷贝至另一文件
- watchFile:对指定文件进行监控
const fs = require("fs")
const path = require("path")
//readFile
fs.readFile(path.resolve('tests.txt'), 'utf-8', (err, data) => {
if (!err) {
console.log(data)
}
})
//writeFile
fs.writeFile('test.txt', '123', {
mode: 438,
flag: 'r+', //r+:不清空,直接从头写入, w+: 先清空,再从头开始写入
encoding: 'utf-8'
}, (err) => {
if (!err) {
fs.readFile('test.txt', 'utf-8', (err, data) => {
console.log(data)
})
}
})
//appendFile
fs.appendFile('test.txt', '高级编程', (err) => {
console.log('追加成功')
})
//copyFile 操作小内存文件
fs.copyFile('test.txt', 'copy.txt', () => {
console.log('copy成功')
})
//watchFile
fs.watchFile('test.txt', { interval: 20 }, (curr, prev) => {
if (curr.mtime !== prev.mtime) {
console.log('文件被修改了')
fs.unwatchFile('test.txt')
}
})
md转html
- marked
- browser-sync
文件打开与关闭
const fs = require("fs")
const path = require("path")
fs.open(path.resolve('test.txt'), 'r', (err, fd) => {
console.log(fd)
})
fs.open('test.txt', 'r', (err, fd) => {
console.log(fd)
fs.close(fd, err => {
console.log('关闭成功')
})
})
大文件读写操作
const fs = require("fs")
const path = require("path")
//read:将磁盘里的内容读取出来放在buffer缓冲区中
let buf = Buffer.alloc(10)
fs.open(path.resolve('test.txt'), 'r', (err, rfd) => {
console.log(rfd)
fs.read(rfd, buf, 0, 3, 4, (err, readBytes, data) => {
//后3个参数:buf开始执行写入起始位置,写入的长度,文件开始读取的起始位置
console.log(readBytes, data.toString())
})
})
//write 将缓冲区里的内容写入到磁盘文件中
buf = Buffer.from('1234567890')
fs.open('b.txt', 'w', (err, wfd) => {
fs.write(wfd, buf, 0, 3, 0, (err, written, buffer) => {
//后3个参数:buf开始读取起始位置,读取的长度,文件开始写入的起始位置
console.log(written) //写入字节数
console.log(buffer)
console.log(buffer.toString())
fs.close(wfd)
})
})
文件拷贝自定义实现
const fs = require("fs")
let buf = Buffer.alloc(10)
const BUFFER_SIZE = buf.length
let readOffset = 0
fs.open('test.txt', 'r', (err, rfd) => {
fs.open('b.txt', 'w', (err, wfd) => {
function next() {
fs.read(rfd, buf, 0, BUFFER_SIZE, readOffset, (err, readBytes) => {
if (!readBytes) {
fs.close(rfd, () => { })
fs.close(wfd, () => { })
console.log('拷贝完成')
return
}
readOffset += readBytes
fs.write(wfd, buf, 0, readBytes, (err, written) => {
next()
})
})
}
next()
})
})
常见目录操作API
- access:判断文件或目录是否具有操作权限
- stat: 获取目录及文件信息
- mkdir:创建目录
- rmdir:删除目录
- readdir:读取目录中的内容
- unlink:删除指定文件
const fs = require("fs")
//access
fs.access('test.txt', (err) => {
if (err) {
console.log(err)
} else {
console.log('有操作权限')
}
})
//stat
fs.stat('test.txt', (err, statObj) => {
console.log(statObj.size)
console.log(statObj.isFile())
console.log(statObj.isDirectory())
})
//mkdir 父级目录一定要存在 或者recursive: true
fs.mkdir('a/b/c', { recursive: true }, (err) => {
if (!err) {
console.log('创建成功')
} else {
console.log(err)
}
})
//rmdir 默认只能删除空目录且只删除最后一级
fs.rmdir('a/b/c', { recursive: true }, (err) => {
if (!err) {
console.log('删除成功')
} else {
console.log(err)
}
})
//readdir
fs.readdir('a/b', (err, files) => {
console.log(files)
})
//unlink
fs.unlink('copy.txt', (err) => {
if (!err) {
console.log('删除成功')
}
})
目录创建--同步
const fs = require("fs")
const path = require("path")
function makeDirSync(dirPath) {
let items = dirPath.split(path.sep)
for (let i = 1; i <= items.length; i++) {
let dir = items.slice(0, i).join(path.sep)
try {
fs.accessSync(dir)
} catch (err) {
fs.mkdirSync(dir)
}
}
}
makeDirSync('a\\b\\c')
目录创建--异步
const fs = require("fs")
const path = require("path")
const { promisify } = require('util')
function makeDir(dirPath, cb) {
let parts = dirPath.split('/')
let index = 1
function next() {
if (index > parts.length) return cb && cb()
let current = parts.slice(0, index++).join('/')
fs.access(current, (err) => {
if (err) {
fs.mkdir(current, next)
} else {
next()
}
})
}
next()
}
makeDir('a/b/c', () => {
console.log('创建成功')
})
//async方法
const access = promisify(fs.access)
const mkdir = promisify(fs.mkdir)
async function myMkdir(dirpath, cb) {
let parts = dirpath.split('/')
for (let index = 1; index <= parts.length; index++) {
let current = parts.slice(0, index).join('/')
try {
await access(current)
} catch (err) {
await mkdir(current)
}
}
cb && cb()
}
myMkdir('a/b/c', () => {
console.log('创建成功')
})
内置模块VM
创建独立的沙箱环境
内置模块 Events
通过EventEmitter类实现事件统一管理 EventEmitter常见API
- on:添加当事件被触发时调用的回调函数
- emit:触发事件,按照注册的序同步调用每个事件监听器
- once:添加当事件在注册之后首次被触发时调用的回调函数
- off:移除特定的监听器
const EventEmitter = require('events')
const ev = new EventEmitter()
ev.on('事件1', () => {
console.log('事件1执行了')
})
ev.once('事件1', () => {
console.log('事件1执行了2222')
})
ev.emit('事件1')
ev.emit('事件1')
let cbFn = (a, b) => {
console.log(a, b, '事件2执行了')
}
ev.on('事件2', cbFn)
ev.emit('事件2')
ev.emit('事件2', 1, 4)
ev.off('事件2', cbFn)
核心模块 stream
CommonJS
模块加载是同步完成的
模块引用、模块定义、模块标识符
module属性
- 任意一个js文件就是一个模块,可以直接使用module属性
- id:模块标识符,一般是一个绝对路径
- filename:返回文件模块的绝对路径
- loaded:布尔值,表示模块是否完成加载
- parent:返回对象,存放调用当前模块的模块
- children:返回当前模块调用的其他模块
- exports:返回当前模块需要暴露的内容
- paths: 返回数组,存放不同目录下的node_modules位置
require属性:接收标识符
- 基本功能是读入并且执行一个模块文件
- resolve:返回模块文件绝对路径
- extensions:一句不同后缀名执行解析操作
- main:返回主模块对象
模块分类及加载流畅
- 内置模块:Node源码编译时写入到二进制文件中
- 文件模块:代码运行时,动态加载 加载流程
- 路径分析:依据标识符确定模块位置
- 文件定位:确定目标模块中具体的文件及文件类型
- 编译执行:采用对应的方式完成文件的编译执行
exports指向module.exports,所以exports不能直接赋值,否知会断了与module.exports之间的联系
浏览器中的事件环
- 从上至下执行所有的同步代码
- 执行过程中将遇到的宏任务与微任务添加至相应的队列
- 同步代码执行完毕后,执行满足条件的微任务回调
- 微任务队列执行完毕后,执行所有满足需求的宏任务回调
- 循环事件环操作
- 注意:每执行一个宏任务之后,就会立刻检查微任务队列
setTimeout(() => {
console.log('s1')
Promise.resolve().then(() => {
console.log('p2')
})
Promise.resolve().then(() => {
console.log('p3')
})
})
Promise.resolve().then(() => {
console.log('p1')
setTimeout(() => {
console.log('s2')
})
setTimeout(() => {
console.log('s3')
})
})
// p1, s1, p2, p3, s2, s3
node事件环
NODE有6个队列
- timers:执行setTimeout与setInterval回调
- pending callbacks:执行系统操作的回调,例如tcp udp
- idle, prepare:只在系统内部进行使用
- poll:执行与I/O相关的回调
- check:执行setImmediate中的回调
- close callbacks:执行close事件的回调
Nodejs完整事件环
- 执行同步代码,将不同的任务添加至相应的队列
- 所有同步代码执行后,会去执行满足条件的微任务
- 所有微任务代码执行会会执行timer队列中满足的宏任务
- timer中的所有宏任务执行完成后就会依次切换队列
- 注意:在完成队列切换之前就会先清空微任务代码
- 注意:process.nexttick的回调是微任务,且执行优先级大于Promise.then
setTimeout(() => {
console.log('s1')
Promise.resolve().then(() => {
console.log('p1')
})
process.nextTick(() => {
console.log('t1')
})
})
Promise.resolve().then(() => {
console.log('p2')
})
console.log('start')
setTimeout(() => {
console.log('s2')
Promise.resolve().then(() => {
console.log('p3')
})
process.nextTick(() => {
console.log('t2')
})
})
console.log('end')
//start, end, p2, s1, t1, p1, s2, t2, p3 某一类型的队列执行完,就立刻去执行文任务?
//start, end, p2, s1, s2, t1, t2, p1, p3 宏队列切换完才会再去执行微任务?
补充
fs
readFile(path, 'utf-8', callback(err, dataStr))
__dirname: 当前文件的路径
http
http.createServer(): 创建web服务器的实例对象
server.on('request', (req, res) => {}):为服务器实例绑定request事件,监听客户端的请求
server.listen(80, () => {}): 启动服务器
req
req.url:
req.method:
res
res.setHeader('Content-Type', 'text/html: Charset=utf-8') 防止中文乱码
res.end: 向客户端响应内容
CommonJS
exports === module.exports
最终共享结果,永远以module.exports为准
npm config get registry
npm config set
registry=https://registry.npm.taobao.org/
开发依赖包 -D
npm i nrm -g
nrm ls
nrm use taobao
i5ting_toc: 把md转为html
require加载自定义模块的时候,必须加上./ ,否则会当作内置模块或第三方模块进行加载
js -> json -> node