nodejs高级

https://www.bilibili.com/video/BV1sA41137qw?p=2open in new window

Natives modules

  • 当前层内容由JS实现
  • 提供应用程序可以直接调用库,例如fs,path,http等
  • JS语言无法直接操作底层硬件设备

Builtin modules 胶水层

底层:

  • V8: 执行js代码,提供桥梁接口
  • Libuv:时间循环,事件队列,异步io
  • 第三方模块:zlib,http,c-cares等

image

  • Reactor 模式,单线程完成多线程工作
  • Reactor模式下实现异步io,事件驱动
  • Nodejs更适用于io密集型高并发请求

轮询技术

  • read
  • select
  • poll linux下的
  • kqueue
  • event ports

libuv库

image

image

异步io总结

  • io是应用程序的瓶颈所在
  • 异步io提高性能无采原地等待结果返回
  • io操作属于操作系统级别,平台都有对应实现
  • Nodejs单线程配合事件驱动架构及libuv实现了异步io
事件驱动架构是软件开发中的通用模式
  • 事件驱动
  • 发布订阅
  • 观察者

require('events')

const EventEmitter = require('events')
const myEvent = new EventEmitter()
//订阅事件
myEvent.on('事件1',()=>{
    console.log('事件1执行了');
})
myEvent.on('事件1',()=>{
    console.log('事件1-1执行了');
})
//触发事件  按照注册的事件执行
myEvent.emit('事件1');

// 事件1执行了
// 事件1-1执行了

nodejs主线程是单线程

  • 通过异步非阻塞io配合事件回调通知

class集群多线程问题

const http = require('http')

function sleepTime(time){
    const sleep = Date.now()+time * 1000
    while(Date.now()<sleep){}
    return
}
sleepTime(4)//耗时
const server = http.createServer((req,res)=>{
    res.send('server starting ....');
})

server.listen(8080,()=>{
    console.log('启动');
})

Node.js应用场景

  • io密集型高并发请求
  • 操作数据库提供API服务
  • 实时聊天程序
  • Nodejs更适合io密集型任务

image

api服务

  1. npm init -y
  2. tsc --init //初始化tsconfig.json
  3. npm i ts-node -D
  4. npm i express
  5. npm i @types/express -D //ts识别 express
api_server.ts

import express from 'express'

const app = express();
app.get('/',(req,res)=>{
    res.end('xxx');
})
app.listen(8080,()=>{
    console.log('启动');
})

//tsconfig.json 
//允许导入json
//resolveJsonModule": true,   


Nodejs中全局对象

  • 与浏览器平台的window不完全相同
  • Nodejs全局对象上挂在许多属性

Global的根本作用就是作为宿主

全局对象可以看作是全局变量的宿主

Nodejs中全局对象是global

Nodejs常见全局变量

  • __filename: 返回正在执行脚本文件的绝对路径
  • __dirname:返回正在执行脚本所在目录
  • timer类函数: 执行顺序与事件循环间的关系
  • process:提供与当前进程互动的接口
  • require:实现模块的加载
  • module,exports:处理模块的导出
console.log(global)
<ref *1> Object [global] {
  global: [Circular *1],
  clearInterval: [Function: clearInterval],
  clearTimeout: [Function: clearTimeout],
  setInterval: [Function: setInterval],
  setTimeout: [Function: setTimeout] {
    [Symbol(nodejs.util.promisify.custom)]: [Getter]
  },
  queueMicrotask: [Function: queueMicrotask],
  performance: [Getter/Setter],
  clearImmediate: [Function: clearImmediate],
  setImmediate: [Function: setImmediate] {
    [Symbol(nodejs.util.promisify.custom)]: [Getter]
  }
}

console.log(__filename)
console.log(__dirname)
//默认情况下this是空对象 和global 并不是一样的

console.log(this==global)  //false


//在调用文件的时候 会传 require('module')  __filename  __dirname  module exports
(function(){
    console.log(this==global);  true
})()

全局变量之 process

无须require可直接使用

  • 获取进程信息
  • 执行进程操作

//资源: cpu内存
console.log(process.memoryUsage())
{
  rss: 166219776,  //常驻内存
  heapTotal: 135598080,//申请的总的内存大小
  heapUsed: 105314928,//实际使用的内存大小
  external: 3629670,  //扩展内存(底层c c++所使用的内存大小)
  arrayBuffers: 2148609// 高版本以前也是放在 external中,代表一个独立空间大小缓存区大小
}

//资源  cpu  内存
Buffer.alloc(1000)
console.log(process.memoryUsage())
{
  rss: 166424576,
  heapTotal: 135335936,
  heapUsed: 105663496,
  external: 3674675,
  arrayBuffers: 2193614
}

console.log(process.cpuUsage())
{ 
    user: 3234000, //  用户占用的cpu时间片段 
    system: 265000 //系统占用的cpu时间片段
}

//运行环境 : 运行目录,node环境, cpu架构,用户环境,系统平台
console.log(process.cwd());//当前项目的所在路径 工作目录运行路径
console.log(process.version); //node版本
console.log(process.versions);//node v8 zlib  brotli 等的版本
console.log(process.arch);// cpu架构  x64
console.log(process.env.NODE_ENV);// undefined  环境设置获取(生产环境 开发环境等)
console.log(process.env.PATH);//配置的系统环境变量
console.log(process.env.USERPROFILE);//管理员目录 例如:C:\Users\Administrator   如果是mac就是HOME
console.log(process.platform);//系统平台 win32

//运行状态:启动参数,pid 运行时间
console.log(process.argv)//node .main.js 1 2 3   
[
  'D:\\study\\service\\api\\node_modules\\ts-node\\dist\\bin.js',
  'D:\\study\\service\\api\\main.test.ts',
  '1',
  '2'
]
console.log(process.argv0);//   node
console.log(process.pid);// 16940
console.log(process.execArgv);//未看到效果
console.log(process.ppid);


//运行时间
console.log(process.uptime());//0.030201 从运行的开始到运行的结束
setTimeout(()={
    console.log(process.uptime())
},3000)

process事件:

//事件 当前脚本完成之后触发
process.on('exit',(code)=>{
    console.log('exit'+code);
    setTimeout(()=>{//exit只能写同步代码 这里面的不会执行
        console.log(123);
    })
})
process.on('beforeExit',(code)=>{
    console.log('before exit' + code);
})
console.log('代码执行完成')
代码执行完成
before exit0
exit0

process.exit();//如果写了上面的事件是不会触发  程序走到这里直接退出

//标准输出输入错误
console.log = function(data){
    //process.stdout 是一个流
    process.stdout.write('---'+data+'\n')//标准输出
}

const fs = require('fs');
//创建一个可读流,,,将数据通过管道交给下一个参数(环节)
fs.createReadStream('test.txt').pipe(process.stdout)

//process.stdin 输入流
process.stdin.pipe(process.stdout);//将流给 process.stdout


process.stdin.setEncoding('utf-8');//设置流字符集
process.stdin.on('readable',()=>{//内置事件
    let chunk = process.stdin.read();//标准输入里读取数据
    if(chunk != null){
        process.stdout.write('data'+chunk)
    }
})

path 模块常用api

  1. 内置模块,require之后直接使用
  2. 用于处理文件/目录的路径
  • basename() 获取路径中基础名称
  • dirname() 获取路径中目录名称
  • extname() 获取路径中扩展名称
  • isAbsolute() 获取路径是否为绝对路径
  • join() 凭借多个路径片段
  • resolve() 返回绝对路径
  • parse() 解析路径
  • format()序列化路径
  • normalize() 规划化路径
const path = require('path')
console.log(__filename);
//获取路径中的基础名称
console.log(path.basename(__filename));//main.js
console.log(path.basename(__filename,'.js'));//main

//获取路径中的基础名称
console.log(path.basename(__filename,'.css')) //main.js
console.log(path.basename('a/b/c'));/c
console.log(path.basename('a/b/c/'));/c

1.返回的就是接收路径当中的最后一部分
2.第二个参数表示扩展名,如果说没有设置则返回完整的文件名带后缀
3.第二个参数作为后缀时,如果没有在当前路径中被配置到,那么会忽略
4.处理目录路径的时候如果说,结尾处有路径分隔符,则也会被忽略掉


console.log(path.extname(__filename));  // .js
console.log(path.extname('/a/b')); //啥也没有
console.log(path.extname('/a/b/index.html.js.css')); // .css
console.log(path.extname('/a/b/index.html.js.'));//.



console.log(path.parse('/a/b/c/index.html'));
{
  root: '/',
  dir: '/a/b/c',     
  base: 'index.html',
  ext: '.html',      
  name: 'index'      
}


console.log(path.parse('/a/b/c'));
{ root: '/', dir: '/a/b', base: 'c', ext: '', name: 'c' }


console.log(path.parse('./a/b/c/'));
{ root: '', dir: './a/b', base: 'c', ext: '', name: 'c' }


console.log(path.format(path.parse('./a/b/c/'))); //./a/b\c

绝对路径

判断当前路径是否是绝对路径


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

路径拼接


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.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\b\c\d

绝对路径


console.log(path.resolve('index.html'))//D:\study\service\api\index.html

Buffer 缓冲区

  • Buffer 让javascript 可以操作二进制
  • io行为操作的就是二进制数据
  • Stream流操作并非nodejs独创
  • 流操作配合管道实现数据分段传输
  • buffer 由c++ 分配 v8回收
Buffer总结
  • 无需require的一个全局变量
  • 实现Nodejs平台下的进制数据操作
  • 不占用v8堆内存大小的内存空间
  • 内存的使用由node来控制,由v8的GC回收
  • 一般配合Stream流使用,充当数据缓冲区

image

创建Buffer实例

Nodejs中的Buffer是一片内存空间

  • alloc:创建指定字节大小的buffer
  • allocUnsafe:创建指定大小的buffer(不安全)
  • from: 接收数据,创建buffer
//16 进制  每一个都是8位
const b1 = Buffer.alloc(10)
console.log(b1);//<Buffer 00 00 00 00 00 00 00 00 00 00>


const b2 = Buffer.allocUnsafe(10)
console.log(b2);//<Buffer 00 00 00 00 00 00 00 00 00 00>

不安全得只要有空间就会拉来使用

Buffer.from("1") 创建buffer

Buffer.from(string|array|Buffer, 字符集) 参数

能接收字符串 数组 本身buffer

const b1 = Buffer.from('中')
console.log(b1);//<Buffer e4 b8 ad>//三个字节

//数组中有中文直接放不识别中文
const b1 = Buffer.from([1,2,'中'],'utf8')
console.log(b1);//<Buffer 01 02 00>



const b1 = Buffer.from([0xe4, 0xb8, 0xad], 'utf8')
console.log(b1);//<Buffer e4 b8 ad>
console.log(b1.toString()); //中



const b1 = Buffer.alloc(3);
const b2 = Buffer.from(b1);//创建一个对应空间的空数据
console.log(b1);//<Buffer 00 00 00>
console.log(b2);//<Buffer 00 00 00>
b1[0] = 1
console.log(b1);//<Buffer 01 00 00>
console.log(b2);//<Buffer 00 00 00>
总结: 对新空间的一个拷贝

新空间的拷贝

Buffer实例方法
  • fill:使用数据填充buffer
  • write:向buffer中写入数据
  • toString:从buffer中提取数据
  • slice:截取buffer
  • indexOf:在buffer中查找数据
  • copy:拷贝buffer中的数据
fill

let buf = Buffer.alloc(6)

buf.fill('123456');
//内存长度大于填充长度会重复填充
//内存长度小于填充长度会截取
//buf.fill(string,index,last)
//string 填充的数据 
//index 对应buffer下标位置 
//last:写入的结束位置结束位置取不到
//如果string可以是int类型 但是buffer中始终是16进制的
console.log(buf)//<Buffer 31 32 33 34 35 36>
console.log(buf.toString())//123456
write
let buf = Buffer.alloc(6);
buf.write('123',1);
//buf.write(string,start,length)
// 没有填充效果,start是开始填充位置,length当前要写入的一个长度
console.log(buf);
console.log(buf.toString())
toString
buf = Buffer.from('按不出');
console.log(bur);
console.log(bur.toString());



const buf = Buffer.from('按不出');
console.log(buf );
console.log(buf .toString('utf-8'));// 正常输出
console.log(buf .toString('utf-8',3));// 正常输出  一个字符三个字节(哪一个位置开始截取)

console.log(buf .toString('utf-8',39));// 截取 从3  -9

slice



const buf = Buffer.from('按不出');
let b1= buf.slice()
console.log(b1)
console.log(b1.toString())

const buf = Buffer.from('按不出');
let b1= buf.slice(3)//截取开始位置
//slice(start,end);  slice(-3) 从后往前截取
console.log(b1)
console.log(b1.toString())
indexOf
const buf = Buffer.from('fff犯得上反对')
console.log(buf)
console.log(buf.indexOf('上'))  //9
console.log(buf.indexOf('上',10))  //从第10个开始找,没找到就是-1

copy

const buf = Buffer.alloc(6)
let b2 = Buffer.from('abc')
b2.copy(b1)//b2 为copy源 b1为copy容器

b2.copy(b1,3,3)
// copy(b1,index ,start,end)  
//index从哪个位置开始写入
//start 从哪个位置开始读取
//end 哪个位置结束

Buffer静态方法
  • concat 将多个buffer拼接成一个新的buffer
  • isBuffer 判断当前数据是否为buffer
let b1 = Buffer.from('顶顶')
let b2 = Buffer.from('撒啊')
let b = Buffer.concat([b1,b2])
console.log(b)
console.log(b.toString())// 顶顶撒啊
let b = Buffer.concat([b1,b2],9)// 三个字节  顶顶撒


let b1 = '123'
let b2 = Buffer.alloc(3)
console.log(Buffer.isBuffer(b1));//false
console.log(Buffer.isBuffer(b2));//true
自定义Buffer的split

ArrayBuffer.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 = 'zce吃馒头,吃面条,我吃所有吃'
let bufArr = buf.split('吃')

fs模块

readFile 是一次性将文件读取或写入

image

image

image

常见flag操作符

  • r表示可读
  • w表示可写
  • s表示同步
  • +表示执行相反操作
  • x表示排它操作
  • a表示追加操作

fs介绍总结

  • fs是Nodejs中内置核心模块
  • 代码层面上fs分为基本操作类和常用API
  • 权限位 标识符 操作符

文件操作api

  • readFile 从指定文件中读取数据
  • writeFile 向指定文件中写入数据
  • appendFile 追加的方式向指定文件中写入数据
  • copyFile 将某个文件中的数据拷贝至另一个文件
  • watchFile 指定文件进行监控
readFile
const fs = require('fs');
const path = require('path')

//readFile
fs.readFile(path.resolve('data.txt'),'utf-8',(err,data)=>{
    console.log(err);
    console.log(data);
})
writeFile
//writeFile 覆盖的写操作
fs.writeFile('data.txt','hello node.js',(err)=>{
//如果文件不存在 创建文件
    if(!err){
        fs.readFile('data.txt','utf-8',(err,data)=>{
            console.log(data)//hello node.js
        })
    }
})

  1. 默认覆盖写
  2. 写入的时候文件不存在会创建这个文件
writeFile
//writeFile
fs.writeFile('data.txt','123',{
    mode:438,//操作权限  可读可写 不可执行
    flag:'r+',//可读可写 直接覆盖前几个,,,如果是w+则是全文件覆盖写入
    encoding:'utf-8'
},(err)=>{
    if(!err){
        fs.readFile('data.txt','utf-8',(err,data)=>{
            console.log(data); //123lo node.js
        })
    }

})
  • r+ 读取后写入
  • w+清空后写入
appendFile
//appendFile
fs.appendFile('data.txt','he',(err)=>{
    console.log('写入成功')
})

第三个参数也可以传对象{}

//copyFile
fs.copyFile('data.txt','test.txt',()=>{
    console.log('拷贝成功')
})
//watchFile
//interval 间隔 毫秒
fs.watchFile('data.txt',{interval:20},(curr,prev)=>{
    if(curr.mtime != prev.mtime){//mtime 修改时间
        console.log('文件被修改了')
        fs.unwatchFile('data.txt')//取消监控
    }
})

markdown转html -todo

const fs = require('fs')
const path = require('match')
const marked = require('marked')
const browserSync = require('browser-sync')

1.读取md和css内容

2.将上述读取出来的内容替换占位符,生成一个最终需要展示的html字符串

3.将上述的html字符写入到指定的html文件中

4.监听md文件内容的变化,然后更新html内容

5.使用browser-sync 来实现显示Html内容

let mdPath = path.join(__dirname,process.argv[2])
let cssPath = path.resolve('github.css')
let htmlPath = mdPath.replace(path.extname(mdPath),'.html')
console.log(mdPath)
console.log(cssPath)
console.log(htmlPath)

fs.readFile(mdPath , 'utf-8',(err,data)=>{
    //将 md -》html
    let htmlStr = marked(data)
    fs.readFile(cssPath,'utf-8',(err,data)=>{
        let retHtml = temp.replace('{{content}}',htmlStr).replace('{{style}}',data)
        //将上述的内容写入到指定的html文件中,用于在浏览器里展示
        fs.writeFile(htmlPath,retHtml,(err)=>{
            console.log('html 生成成功')
        })
    })
})



image

image

Last Updated:
Contributors: 刘荣杰