目录
1-1.Node概述
Node的特点
- 单线程,同时接受大量的请求,没有进程切换的开销,不适合进行复杂的运算,但是io速度很快,不会做任何与业务逻辑有关的事情。
- 简单的信息的记录
- 静态资源托管
- 缓存
通常用Node干什么
- 开发桌面应用程序
- 开发服务端应用程序
1-2.全局对象
- npm i -D @types/node 代码智能提示
- node的全局对象
- setTimeout
- setInterval
- setImmediate
- console
- __dirname
- __filename
- Buffer
- process
- 在浏览器中setTimeout和 setInterval返回结果是数字,但是node中返回的是对象
- setImmediate类似于setTimeout 0
- __dirname 获取当前运行模块所在的目录,并非global属性
- __filename 获取当前运行模块文件路径,不是global属性
- Buffer 类型化数组,继承自UInt8Array,计算机存储的基本单位:字节,1个字节=8位
- process(进程)
- cwd() process.cwd() 命令行前面显示的路径 绝对路径
- exit() 强制退出node进程 可传入退出码,0表示成功退出,默认为0
- argv 返回string,获取命令中的所有参数
- platform 返回操作系统版本
- kill(进程id)
- env 获取环境变量 返回一个对象
1-3.Node模块化细节
模块的查找
require(‘路径’)
-
绝对路径,其他的路径最终也会转化为绝对路径,Mac中:require(’/Users/biaofeng/Desktop/Node/a.js’);window中:require(‘D:\re\NodeJS\a.js’)要进行转译
-
相对路径./或者…/
-
相对路径
-
检查是否是内置模块,如:fs、path
-
检查当前目录中的node_modules
-
检查上级目录的node_modules
-
转换为绝对路径
-
加载模块
扫描二维码关注公众号,回复: 12830242 查看本文章
-
-
关于后缀名:如果不提供后缀名,自动补全。先后顺序js、json、node、mjs
-
关于文件名
如果仅提供目录,不提供文件名(‘node ./’),则自动寻找改目录中的index.js
package.json中的main字段,表示包的默认入口,导入或执行时若仅提供目录,则使用main补全入口,默认值为index.js
module对象
记录当前模块的信息
- console.log(module)
Module {
id: '.', //入口模块为点,其他为绝对路径
path: '/Users/biaofeng/Desktop/Node',
exports: {
},
parent: null,
filename: '/Users/biaofeng/Desktop/Node/index.js',
loaded: false,
children: [],
paths: [
'/Users/biaofeng/Desktop/Node/node_modules',
'/Users/biaofeng/Desktop/node_modules',
'/Users/biaofeng/node_modules',
'/Users/node_modules',
'/node_modules'
]
}
require函数
[Function: require] {
resolve: [Function: resolve] {
paths: [Function: paths] }, //转化为绝对路径的
main: Module {
id: '.',
path: '/Users/biaofeng/Desktop/Node',
exports: {
},
parent: null,
filename: '/Users/biaofeng/Desktop/Node/index.js',
loaded: false,
children: [],
paths: [
'/Users/biaofeng/Desktop/Node/node_modules',
'/Users/biaofeng/Desktop/node_modules',
'/Users/biaofeng/node_modules',
'/Users/node_modules',
'/node_modules'
]
},
extensions: [Object: null prototype] {
'.js': [Function],
'.json': [Function],
'.node': [Function]
},
cache: [Object: null prototype] {
'/Users/biaofeng/Desktop/Node/index.js': Module {
id: '.',
path: '/Users/biaofeng/Desktop/Node',
exports: {
},
parent: null,
filename: '/Users/biaofeng/Desktop/Node/index.js',
loaded: false,
children: [],
paths: [Array]
}
}
}
// 面试题
exports.c = 3;
module.exports = {
a: 1,
b: 2
};
this.m = 5;
// this,exports ->{c:3,m:5} module.exports->{a:1,b:2}
console.log(this === module.exports) // false
// 导出的是{a:1,b:2}
//原理
module.exports = {
};
const exports = module.exports;
__temp.call(module.exports,...arguments);
return module.exports;
1-4.node中ES模块化
- 目前es模块化还处于试验阶段
- 模块要么是commonjs,要么是ES
- 默认是commonjs
- ES模块
- 文件后缀名为.mjs
- 最近的package.json中type的值是module
- 运行 node --experimental-modules index.mjs
import('./a.mjs').then(obj=>{
console.log(obj);//{ b: 2, default: 1 }
})
1-5基本内置模块
-
os
-
os.EOL end-of-line marker 换行符 linux:\n window:\r\n
-
os.arch() cpu的架构名
-
os.cpus() cpu信息,返回一个数组,os.cpus().length:几核
-
os.freemem() 剩余内存
-
os.homedir() 用户目录
-
os.hostname() 主机名
-
os.tmpdir() 操作系统临时目录
-
-
path
filename 文件的绝对路径
basename 文件名
path url中-
path.basename(‘sdfsd/sdfdsf/sdf/a.js’) ->a.js
-
第二个参数为文件扩展名,匹配上了就返回文件名,否则加上后缀名
-
path.basename(‘sdfsd/sdfdsf/sdf/a.js’,’.js’) ->a
-
path.basename(‘sdfsd/sdfdsf/sdf/a.js’,’.html’) ->a.js
-
-
path.sep 分隔符 /
-
path.delimiter 一块一块的分割符
-
path.dirname(‘a/b/c/s.js’) -> a/b/c
-
path.extname(‘adfs/sdfs/a.py’) -> .py
-
path.join(‘a’,‘b’,‘c’,‘d.js’) -> a/b/c/d.js
-
path.normalize(‘a/b/c/…/a.js’) -> a/b/a.js 规范化路径
-
path.relative(’/data/oran/test/aaa’,’/data/oran/ima/bbb’) -> …/…/ima/bbb
-
path.resolve(__dirname,’./a.js’) -> /Users/biaofeng/Desktop/Node/a.js
-
-
url
const URL = require('url');
const url = new URL.URL('http://tan.com:80/a/?t=3&c=5#abc');
console.log(url);
console.log(url.searchParams.has('t')); // true
console.log(url.searchParams.get('t')); // 3
/*
URL {
href: 'http://tan.com/a/?t=3&c=5#abc',
origin: 'http://tan.com',
protocol: 'http:',
username: '',
password: '',
host: 'tan.com',
hostname: 'tan.com',
port: '',
pathname: '/a/',
search: '?t=3&c=5',
searchParams: URLSearchParams { 't' => '3', 'c' => '5' },
hash: '#abc'
}
*/
- util
- util.callbackify() 将异步转化为回调函数
async function delay(deration = 1000) { return new Promise(resolve => { setTimeout(() => { resolve(deration); }, deration) }) } const callbackDelay = util.callbackify(delay); callbackDelay(2000,(err,deration)=>{ console.log(deration); }) delay().then(res=>{ console.log(res); })
- util.promisify() 将回调->异步
function delayCallback(duration, callback) { setTimeout(() => { callback(null, duration); }, duration) } var proDelay = util.promisify(delayCallback); (async()=>{ const r = await proDelay(1000); console.log(r); })() proDelay(500).then(res => { console.log(res); })
-
util.inherits(子类,夫类) 继承
-
util.isDeepStrictEqual(obj1,obj2) 深度严格比较
1-6文件io
- fs.readFile() 读取文件内容
const fs = require('fs');
const path = require('path');
const filename = path.resolve(__dirname, './text/a.txt');
// fs.readFile(filename,(err,content)=>{
// console.log(content.toString('utf-8'));
// })
// fs.readFile(filename,'utf-8',(err,content)=>{
// console.log(content);
// })
// fs.readFile(filename, {
// encoding: 'utf-8'
// }, (err,content)=>{
// console.log(content);
// })
// console.log(fs.readFileSync(filename,'utf-8')); //同步
async function test(){
const content = await fs.promises.readFile(filename,'utf-8');
console.log(content);
}
test();
- fs.writeFile() 向文件写入内容
- 复制粘贴图片
const fs = require('fs');
const path = require('path');
async function copyJpg(){
const fromFilename = path.resolve(__dirname,'./text/1.jpg');
const contentBuffer = await fs.promises.readFile(fromFilename);
const toFilename = path.resolve(__dirname,'./text/1.copy.jpg');
await fs.promises.writeFile(toFilename,contentBuffer);
console.log('copy success!');
}
copyJpg();
- fs.stat() 获取文件或目录信息,访问目录的话size为0,原因操作系统将目录当做空的文件,里面有个指针记录文件的地址。
- isDirectory() 是不是目录
- isFile() 是不是文件
const fs = require('fs');
const path = require('path');
const filename = path.resolve(__dirname,'./text/a.txt');
async function test(){
const stat = await fs.promises.stat(filename);
console.log(stat);
}
test();
/*
Stats {
dev: 16777220,
mode: 33188,
nlink: 1,
uid: 501,
gid: 20,
rdev: 0,
blksize: 4096,
ino: 4321131726,
size: 81, //文件大小字节数
blocks: 8,
atimeMs: 1604045246901.927,
mtimeMs: 1604045245083.801,
ctimeMs: 1604045245083.801,
birthtimeMs: 1604045236285.8137,
atime: 2020-10-30T08:07:26.902Z, //上次访问时间
mtime: 2020-10-30T08:07:25.084Z, //上次文件内容被修改时间
ctime: 2020-10-30T08:07:25.084Z, //上次文件状态被修改时间
birthtime: 2020-10-30T08:07:16.286Z // 文件创建时间
}
*/
- fs.readdir() 返回目录下一级的文件或者目录
const fs = require('fs');
const path = require('path');
const dirName = path.resolve(__dirname,'./text');
async function test(){
const dir = await fs.promises.readdir(dirName);
console.log(dir);
}
test();
- fs.mkdir() 创建目录,创建文件用writeFile写入空字符串
const fs = require('fs');
const path = require('path');
const dir = path.resolve(__dirname,'./text/1');
async function test(){
await fs.promises.mkdir(dir);
console.log('创建成功');
}
test();
- 读取一个目录中所有的目录和文件
const fs = require('fs');
const path = require('path');
class File {
constructor(filename, name, ext, isFile, size, createTime, updateTime) {
this.filename = filename;
this.name = name;
this.ext = ext;
this.isFile = isFile;
this.size = size;
this.createTime = createTime;
this.updateTime = updateTime;
}
async getContent(isBuffer = false) {
if (this.isFile) {
// 文件
if (isBuffer) {
return await fs.promises.readFile(this.filename);
}
return await fs.promises.readFile(this.filename, 'utf-8');
} else {
//目录
return null;
}
}
async getChildren() {
if(this.isFile){
//不是文件
return [];
}
let child = await fs.promises.readdir(this.filename);
child = child.map(name => {
const reslut = path.resolve(this.filename,name);
return File.getFile(reslut);
})
// console.log(child);
return Promise.all(child);
}
static async getFile(filename) {
const stat = await fs.promises.stat(filename);
const name = path.basename(filename);
const ext = path.extname(filename);
const isFile = stat.isFile();
const size = stat.size;
const createTime = stat.birthtime;
const updateTime = stat.mtime;
return new File(filename, name, ext, isFile, size, createTime, updateTime);
}
}
// async function test() {
// const filename = path.resolve(__dirname, './text');
// const file = await File.getFile(filename);
// // console.log(file);
// // console.log(await file.getContent(true));
// console.log(await file.getChildren());
// }
// test();
async function readDir(filename){
const file = await File.getFile(filename);
return await file.getChildren();
}
async function test(){
const filename = path.resolve(__dirname,'./text');
const res = await readDir(filename);
console.log(await res[0].getChildren());
// console.log(res);
}
test();
- 删除文件 fs.onlink()
- utf-8 一个汉字占3个字节
1-7.文件流
什么是流
流是指数据的流动,数据从一个地方缓缓的流动到另一个地方
-
可读流:readable
数据从源头流向内存 -
可写流:writeable
数据从内存流向源头 -
双工流:duplex
数据即可以从源头流向内存,也可以从内存中流向源头
为什么需要流
-
其他介质和内存数据规模不一样,如磁盘中的数据和内存中的数据大小不一样
-
其他介质和内存数据处理能力不一样,内存是快速的数据,磁盘是缓慢的
文件流
- 什么是文件流
内存数据和磁盘数据之间的流动 - 文件流的创建
- fs.createReadStream(path[,options]),可读流
- 含义:创建一个文件可读流,用于读取文件内容,读到内存的
- fs.createReadStream(path[,options]),可读流
const fs = require('fs');
const path = require('path');
const filename = path.resolve(__dirname,'./a.txt');
const rs = fs.createReadStream(filename,{
encoding:'utf-8',//默认buffer
// start: 开始字节
// end: 结束字节
highWaterMark: 1, // 默认一次读64*1024字节,还要看encoding
autoClose: true, //默认true 读完自动关闭
}) // 返回readable的子类ReadStream
// 事件 rs.on(事件名,处理函数)
// 文件打开事件,文件被打开后触发
rs.on('open',()=>{
console.log('文件打开了');
})
//文件打开出错
rs.on('error',()=>{
console.log('出错了');
})
//文件关闭 可以手动关闭 rs.close() 可以自动关闭,文件读完后
rs.on('close',()=>{
console.log('文件关闭')
})
// 读文件内容 要注册这个事件才会读
rs.on('data',(chunk)=>{
console.log('读到了一部分数据',chunk);
rs.pause(); //暂停读取,会触发pause事件
})
rs.on('pause',()=>{
console.log('暂停了');
setTimeout(()=>{
rs.resume(); // 恢复读取,会触发resume事件
},1000)
})
rs.on('resume',()=>{
console.log('恢复了');
})
rs.on('end',()=>{
console.log('文件读取完毕');
})
- 可读流
- fs.createWriteStreanm(path[,options]) 创建一个写入流
- path:写入文件路径
- options
- flags:操作文件的方式
- encoding:编码方式
- start:起始字节
- highWaterMark:每次最多写入的字节数,与encoding无关
- 返回:writeable的子类WriteStream
- ws.on(事件名,处理函数)
- ws.write(data)
- 写入一组数据
- data可以是字符串或者Buffer
- 返回一个boolean值,true表示写入的通道没有被填满,接下来的数据可以直接写入,不用排队,false表示写入的通道已经被填满,接下来的数据将进入写入队列
- 当写入队列清空时,会触发drain事件
- ws.end([data])
- 结束写入,将自动关闭文件
- 是否自动关闭取决于autoClose配置
- 默认为true
- data可选,表示关闭前最后一次写入
- 结束写入,将自动关闭文件
1-8.net模块
const net = require('net');
const socket = net.createConnection({
host: 'duyi.ke.qq.com',
port: 80
},()=>{
console.log('连接成功');
});
function parseResponse(response){
const num = response.indexOf('\r\n\r\n');
const header = response.substring(0,num);
const body = response.substring(num+2);
const headerArr = header.split('\r\n');
const headerParts = headerArr.slice(1);
const headerPartsArr = headerParts.map((str)=>{
return str.split(':').map(str=>str.trim());
})
const headerObj = headerPartsArr.reduce((res,ele)=>{
res[ele[0]] = ele[1];
return res;
},{
})
// console.log(headerObj);
return {
header: headerObj,
body
}
}
let receive = null;
function isover(){
// console.log(receive);
const len = receive.header["Content-Length"];
const curLen = Buffer.from(receive.body,'utf-8').byteLength;
// console.log(len,curLen);
return curLen > len;
}
socket.on('data',(chunk)=>{
const response = chunk.toString('utf-8');
if(!receive){
// 第一次
receive = parseResponse(response);
if(isover()){
socket.end();
}
return;
}
receive.body += chunk;
if(isover()){
socket.end();
return;
}
// socket.end();
})
socket.write(`GET / HTTP/1.1
Host: duyi.ke.qq.com
`);
socket.on('close',()=>{
console.log(receive.body);
console.log('结束了');
})
/*
HTTP/1.1 400 Bad Request
Server: stgw/1.3.12.4_1.13.5
Date: Sat, 31 Oct 2020 14:12:31 GMT
Content-Type: text/html
Content-Length: 181
Connection: close
<html>
<head><title>400 Bad Request</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<hr><center>stgw/1.3.12.4_1.13.5</center>
</body>
</html>
*/
const net = require('net');
const path = require('path');
//服务器
const server = net.createServer();
server.listen(10000); //服务器监听9527端口
server.on('listening',()=>{
console.log('server listening 9527');
})
server.on('connection',socket=>{
console.log('有客服端连接');
socket.on('data',(chunk)=>{
console.log(chunk.toString('utf-8'));
socket.write(`HTTP/1.1 200 ok
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
</head>
<body>
<h1>好的</h1>
</body>
</html>`);
socket.end();
})
socket.on('close',()=>{
console.log('连接关闭了');
})
})