【Node】Node核心

1-1.Node概述

Node的特点

  • 单线程,同时接受大量的请求,没有进程切换的开销,不适合进行复杂的运算,但是io速度很快,不会做任何与业务逻辑有关的事情。
  • 简单的信息的记录
  • 静态资源托管
  • 缓存

通常用Node干什么

  • 开发桌面应用程序
  • 开发服务端应用程序

1-2.全局对象

  • npm i -D @types/node 代码智能提示
  • node的全局对象
    • setTimeout
    • setInterval
    • setImmediate
    • console
    • __dirname
    • __filename
    • Buffer
    • process
  1. 在浏览器中setTimeout和 setInterval返回结果是数字,但是node中返回的是对象
  2. setImmediate类似于setTimeout 0
  3. __dirname 获取当前运行模块所在的目录,并非global属性
  4. __filename 获取当前运行模块文件路径,不是global属性
  5. Buffer 类型化数组,继承自UInt8Array,计算机存储的基本单位:字节,1个字节=8位
  6. process(进程)
    1. cwd() process.cwd() 命令行前面显示的路径 绝对路径
    2. exit() 强制退出node进程 可传入退出码,0表示成功退出,默认为0
    3. argv 返回string,获取命令中的所有参数
    4. platform 返回操作系统版本
    5. kill(进程id)
    6. 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]),可读流
      • 含义:创建一个文件可读流,用于读取文件内容,读到内存的
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('连接关闭了');
    })
})

猜你喜欢

转载自blog.csdn.net/tscn1/article/details/109374194