Node 基础

1、什么是Node.js

  • Node.js是一个开源、跨平台的JavaScript运行时环境,构建在Chrome的V8引擎之上。
  • Node.js是异步事件驱动的单线程模型。
  • 由于Node.js异步非阻塞的特性,因此适用于 I/O 密集型的应用场景。

需要注意的是,Node.js是单线程模型,需要避免CPU的耗时操作。

2、CommonJS模块化

2.1 CommonJS规范

Node.js应用由模块组成,默认采用的是CommonJS规范。即每个文件是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类等都是私有的,对其它文件不可见。

不过我们可以通过在模块中通过exports或者module.exports命令将内容导出,其它文件就可以通过require命令访问到这些被导出的内容。

  • module:module是对当前模块对象的引用。module.exports用于定义模块导出的内容。

  • exports:exports是module.exports对应的引用。

  • require:require加载模块,访问模块导出的内容。

  • 语法

// 模块导出 - user.js
const name = 'lucy';
const age = 28;
const printName = () => {
        console.log(name);
};

module.exports = { name, age, printName };
// 或者
exports.name = name;
exports.age = age;
exports.printName = printName;
// 模块导入 - index.js
const { name, age, printName } = require('./user.js');

console.log(name, age, printName);

2.2 CommonJS示例

例子1 - 给exports直接赋值

// 模块导出 - user.js
const name = 'lucy';
const age = 28;
const printName = () => {
    console.log(name);
};

exports = { name, age };



// 模块导入 - index.js
const { name, age, printName } = require('./user.js');

// undefined、undefined、undefined
console.log(name, age, printName);



// 入口文件
node ./index.js

例子2 - 同时使用exports和module.exports

// 模块导出 - user.js
const name = 'lucy';
const age = 28;
const printName = () => {
    console.log(name);
};

module.exports = { name, age };
exports.printName = printName;



// 模块导入 - index.js
const { name, age, printName } = require('./user.js');

// lucy 28 undefined
console.log(name, age, printName);



// 入口文件
node ./index.js

例子3 - 访问包含随机语句的模块

// 模块导出 - user.js
const height = Math.random();

module.exports = { height };



// 模块导入 - index.js
const { height: firstHeight } = require('./user.js');

// 第一次访问: 0.14063734111371584
console.log('第一次访问:', firstHeight);

const { height: secondHeight } = require('./user.js');

// 第二次访问: 0.14063734111371584
console.log('第二次访问:', secondHeight);



// 入口文件
node ./index.js

例子4 - 循环引用

// moduleA.js
const name = 'moduleA';
const path = './moduleA.js';

const moduleB = require('./moduleB');
// moduleB.name moduleB
console.log('moduleB.name', moduleB.name);

module.exports = { name, path };



// moduleB.js
const name = 'moduleB';
const path = './moduleB.js';

const moduleA = require('./moduleA');
// moduleA.name undefined
console.log('moduleA.name', moduleA.name);

module.exports = { name, path };



// 入口文件
node ./moduleA.js

总结

  • 模块导出的数据存放在module.exports对象中。
  • exports是module.exports的别名。
  • 模块加载过一次后会进行缓存,第二次加载时直接返回上一次的缓存结果。
  • Node.js会检测出当前代码是否存在循环引用,并做相应的处理。

如何删除模块缓存
require.cache: 模块被加载时缓存在此对象中。
require.resolve: 定位到模块位置,返回模块的解析后的文件名。

// xxx: 文件标识符
delete require.cache[require.resolve('xxx')];

查看当前模块被包裹后的内容

/* 输出:
    function (exports, require, module, __filename, __dirname) {
        module file content
    }
*/
console.log('' + arguments.callee);

查看主入口module对象

/* 输出:
    {
        "id": ".",
        "path": "",
        "exports": {},
        "filename": "",
        "loaded": false,
        "children": [
            {
                "id": "",
                "path": "",
                "exports": {
                    "name": "lucy",
                    "age": 28
                },
                "filename": "",
                "loaded": true,
                "children": [],
                "paths": [...]
            }
        ],
        "paths": [...]
    }
*/
console.log(JSON.stringify(require.main, null, 2));

3、Node.js核心模块使用

3.1 HTTP模块(常用)

http模块是一个非常常用的模块,我们可以用它来搭建一个服务器,或者使用它发送请求。

  1. 搭建服务器
const http = require('node:http');
const { Buffer } = require('node:buffer');
const { URL } = require('node:url');
const querystring = require('node:querystring');

// 程序支持的HTTP方法列表
// console.log(http.METHODS);

// 所有标准HTTP响应状态代码的集合,以及每个状态代码的简短描述。
// console.log(http.STATUS_CODES);

const server = http.createServer((req, res) => {

    /* ---------  Request   ---------- */
    // console.log(req.headers);
    // console.log(req.headers['cookie']);
    // console.log(req.httpVersion);
    // console.log(req.method);
    // console.log(req.url);
    // GET请求
    // console.log('query:', new URL(req.url, 'http://localhost:8888').searchParams);

    /* ---------  Response   ---------- */

    // 状态码和状态码描述
    // 获取状态码
    // console.log(res.statusCode);
    // console.log(res.statusMessage);

    // 设置状态码
    // res.statusCode = 404;
    // res.statusMessage = 'not found';

    // 设置响应头信息
    res.setHeader('Content-Type', 'text/html');
    res.setHeader('X-Foo', 'Bar');

    // res.writeHead(status, statusMessage, headers)
    // res.writeHead(200, 'Very OK', { 'Content-Type': 'text/plain' });

    // 获取响应头信息(header name自动转换成小写)
    // console.log(res.getHeaders());
    // console.log(res.getHeaderNames());
    // console.log(res.getHeader('X-Foo'));
    // console.log(res.hasHeader('X-Foo'));

    // 响应主体信息
    // res.write(chunk: string | buffer, encoding, callback)
    res.write(Buffer.from('hello world'))
    res.write('ok 1');
    res.write('ok 2');

    // res.end(data, encoding, callback); 相当于 res.write(data, encoding) + res.end(callback)
    res.end('ok end', 'utf8', () => {
        console.log('数据发送完成');
    });
});

// 启动服务
server.listen(8888, 'localhost', () => {
    console.log('服务启动成功');
});

// 关闭服务
// server.on('close', () => {
//     console.log('The server is closed');
// });

// server.close(() => {
//     console.log('Close the server.');
// });
  1. 获取POST请求的数据
const http = require('node:http');
const querystring = require('node:querystring');

const server = http.createServer((req, res) => {
    if (req.method === 'POST') {
        let postData = '';
        req.on('data', (chunk) => {
            postData += chunk.toString();
        });
        req.on('end', () => {
            console.log('postData:', querystring.parse(postData));
            res.end('POST request end');
        });
    } else {
        res.end('Other request end');
    }
});

server.listen(8888, 'localhost', () => {
    console.log('服务启动成功');
});
  1. 客户端:发送GET网络请求
// http-server.js
const http = require('node:http');

const server = http.createServer((req, res) => {
    if (req.method === 'GET') {
        console.log(req.headers['x-auth']);
        res.setHeader('Content-Type', 'text/html');
        res.end('This is a get request');
    } else if (req.method === 'POST') {
        // 接收client发送过来的数据
        let postData = '';
        req.on('data', (chunk) => {
            postData += chunk.toString();
        });
        req.on('end', () => {
            console.log('postData:', postData);
            res.setHeader('Content-Type', 'application/json');
            res.end(JSON.stringify({ a: 'b' }));
        });
    } else {
        res.statusCode = 404;
        res.end();
    }
});

server.listen(8889, 'localhost', () => {
    console.log('服务启动成功');
});

// 关闭服务
server.on('error', (error) => {
    console.log('The server has error', error.message);
})

server.on('close', () => {
    console.log('The server is closed');
});
// http-get.js
const http = require('node:http');

// http.get(url, options, callback)
const client = http.get('http://localhost:8889/?a=b', {
    headers: {
        'X-AUTH': '1234',
    },
}, (res) => {
    console.log(res.statusCode);
    console.log(res.headers);
    res.setEncoding('utf8');

    // 获取返回的内容
    let rawData = '';
    res.on('data', (chunk) => {
        rawData += chunk;
    });
    res.on('end', () => {
        console.log('data:', rawData);
    });
});

// 请求服务出现错误
client.on('error', (e) => {
    console.log('client error info', e.message);
});

// 关闭客户端
client.end();
  1. 客户端:发送POST网络请求
// http-post.js
const http = require('node:http');
const { Buffer } = require('node:buffer');

const postData = JSON.stringify({
    'msg': 'Hello World!',
});

// http.request(options, callback)
const client = http.request({
    protocol: 'http:',
    hostname: 'localhost',
    port: '8889',
    pathname: '/',
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Content-Length': Buffer.byteLength(postData),
    },
    
}, (res) => {
    console.log(res.statusCode);
    console.log(res.headers);
    res.setEncoding('utf8');

    // 获取返回的内容
    let rawData = '';
    res.on('data', (chunk) => {
        rawData += chunk;
    });
    res.on('end', () => {
        console.log('data:', rawData);
    });
});

// 请求服务出现错误
client.on('error', (e) => {
    console.log('client error info', e.message);
});

client.write(postData);

// 关闭客户端
client.end();

3.2 URL模块(常用)

URL模块提供了一些非常实用的用于解析URL的方法。

  1. URL对象
const url = require('node:url');
const myURL =
  new URL('https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash'); 

/*
{
  href: 'https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash',
  origin: 'https://sub.example.com:8080',
  protocol: 'https:',
  username: 'user',
  password: 'pass',
  host: 'sub.example.com:8080',
  hostname: 'sub.example.com',
  port: '8080',
  pathname: '/p/a/t/h',
  search: '?query=string',
  searchParams: URLSearchParams { 'query' => 'string' },
  hash: '#hash'
}
*/
console.log(myURL);

修改URL信息

const url = require('node:url');
const myURL =
  new URL('https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash'); 

myURL.port = 8081;

// https://user:pass@sub.example.com:8081/p/a/t/h?query=string#hash
console.log(myURL.toString());
  1. URLSearchParams对象
const myURL = new URL('https://example.org/?abc=123');
// 123
console.log(myURL.searchParams.get('abc'));

myURL.searchParams.append('abc', 'xyz');
// https://example.org/?abc=123&abc=xyz
console.log(myURL.href);

myURL.searchParams.delete('abc');
myURL.searchParams.set('a', 'b');

// false
console.log(myURL.searchParams.has('abc'));

// https://example.org/?a=b
console.log(myURL.href);

myURL.search = new URLSearchParams('name=lily');
// https://example.org/?name=lily
console.log(myURL.href);

手动创建URLSearchParams对象

const newSearchParams = new URLSearchParams('a=b');
newSearchParams.append('a', 'c');

// a=b&a=c
console.log(newSearchParams.toString());

3.3 Query String模块(常用)

Query String模块提供了一些非常实用的用于解析查询字符串的方法。

  1. querystring.parse(str, sep, eq)

解析查询字符串。

/*
    { a: 'b', c: [ 'd', 'f' ] }
*/
const querystring = require('node:querystring'); 

const obj = querystring.parse('a=b&c=d&c=f');
console.log(obj);
  1. querystring.stringify(obj, sep, eq)

将对象序列化成查询字符串。

const querystring = require('node:querystring'); 

const obj = {
    a: 'b',
    c: ['d', 'f']
};

// a=b&c=d&c=f
console.log(querystring.stringify(obj));

// a:b;c:d;c:f
console.log(querystring.stringify(obj, ';', ':'));

3.4 Path模块(常用)

Path模块提供了用于处理文件和目录路径的实用程序。

  1. path.basename(path, suffix)

返回路径的最后一部分。

const path = require('node:path'); 

// index.html
path.basename('/foo/bar/baz/asdf/index.html');

// index
path.basename('/foo/bar/baz/asdf/index.html', '.html');
  1. path.delimiter

返回特定平台的路径之间分隔符。Windows使用的是;,POSIX使用的是:。

const path = require('node:path'); 
console.log(path.delimiter);
console.log(process.env.PATH);
  1. path.dirname(path)

返回路径的目录名。

const path = require('node:path'); 

// /foo/bar/baz/asdf
path.dirname('/foo/bar/baz/asdf/quux');
  1. path.extname(path)

返回路径的扩展名。根据最后一个.字符到路径结尾的字符串。

const path = require('node:path'); 

// .html
path.extname('index.html');

// .
path.extname('index.');

// 
path.extname('index');

// .md
path.extname('.index.md');
  1. path.isAbsoute(path)

如果path是绝对路径则返回true。

const path = require('node:path'); 

ath.isAbsolute('/foo/bar'); // true
path.isAbsolute('/baz/..'); // true
path.isAbsolute('qux/');    // false
path.isAbsolute('.');       // false 
  1. path.join([…paths])

将所有给定的路径片段连接在一起,然后对生成的路径进行规范化。

const path = require('node:path');


// /foo/bar/baz/asdf
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..');
  1. path.normalize(path)

规范化给定的路径,解析…和.片段。

const path = require('node:path');

// /foo/bar/baz/asdf
path.normalize('/foo/bar//baz/asdf/quux/..');
  1. path.relative(from, to)

根据当前工作目录返回从from到to的相对路径。

const path = require('node:path');

// ../../impl/bbb
path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb');
  1. path.resolve([…paths])

将一系列的路径片段解析为绝对路径。

 const path = require('node:path');

// /foo/bar/baz
path.resolve('/foo/bar', './baz');

// /tmp/file
path.resolve('/foo/bar', '/tmp/file/');

// 假如当前命令执行的目录是/home/myself/node(和代码文件所在的目录没有关系)
// /home/myself/node/wwwroot/static_files/gif/image.gif
path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
  1. path.sep

返回特定平台的路径中目录之间分隔符。Windows使用的是\,POSIX使用的是/。

const path = require('node:path');

console.log(path.sep);

path.join()path.resolve()的区别

path.join是用来连接路径片段,并返回规范化后的路径
path.resolve将路径片段解析为绝对路径

// 例子1
// /bar/foo
console.log(path.join('/bar', '/foo'));
// /foo
console.log(path.resolve('/bar', '/foo'));

// 例子2:假如当前命令执行的目录是/home/myself/node(和代码文件所在的目录没有关系)
// bar/foo/
console.log(path.join('bar/', 'foo/'));
// /home/myself/node/bar/foo
console.log(path.resolve('bar/', 'foo/'));

3.5 File System模块(常用)

node:fs模块与文件系统进行交互。

  1. 基本使用
// 使用基于Promise的API
const fs = require('node:fs/promises');

// 使用基于回调函数或者同步操作的API
const fs = require('node:fs');
  1. 基于Promise、回调函数、同步操作的使用方式
// promise
const { readFile } = require('node:fs/promises');
const path = require('node:path');

(async function(path) {
    try {
      const content = await readFile(path, 'utf8');
      console.log(content);
    } catch (error) {
      console.error('there was an error:', error.message);
    }
  })(path.resolve(__dirname, './music.txt'));


// callback
const { readFile } = require('node:fs');
const path = require('node:path');

readFile(path.resolve(__dirname, './music.txt'), 'utf8', (err, data) => {
    if (err) throw err;
    console.log(data);
});

// sync handle
const { readFileSync } = require('node:fs');
const path = require('node:path');

try {
    const content = readFileSync(path.resolve(__dirname, './music.txt'), 'utf8');
    console.log(content);
} catch (err) {
  // handle the error
}

接下来的案例代码都是基于同步操作的API编写。
3. 创建文件夹

const { mkdirSync } = require('node:fs');

try {
    mkdirSync('./temp');
    // 不可以同时创建不存在的多层级目录
    mkdirSync('./logs/errorlog');
} catch (err) {
  // handle the error
  console.log(err);
} 
  1. 写入文件(如果文件不存在,则创建文件)
const { writeFileSync } = require('node:fs');
const path = require('node:path');

try {
     writeFileSync(path.resolve(__dirname, './music.txt'), '新的内容', 'utf8');
} catch (err) {
  // handle the error
} 
  1. 追加文件内容
const { appendFileSync } = require('node:fs');
const path = require('node:path');
const os = require('node:os');

try {
    appendFileSync(path.resolve(__dirname, './music.txt'), `${os.EOL}新的内容`, 'utf8');
} catch (err) {
  // handle the error
}
  1. 读取文件夹
const { readdirSync } = require('node:fs');

try {
    const files = readdirSync('.');
   console.log(files);
} catch (err) {
  // handle the error
  console.log(err);
}
  1. 读取文件
const { readFileSync } = require('node:fs');
const path = require('node:path');

try {
    const content = readFileSync(path.resolve(__dirname, './music.txt'), 'utf8');
    console.log(content);
} catch (err) {
  // handle the error
} 
  1. 区分文件夹还是文件、判断文件或者文件夹是否存在
const { statSync } = require('node:fs');

try {
    const stat = statSync('./temp');
    if (stat.isDirectory()) {
        console.log('这是一个文件夹');
    } else if (stat.isFile()) {
        console.log('这是一个文件');
    }
} catch (err) {
  // handle the error
  console.log(err && '文件不存在');
}
  1. 重命名文件
 const { renameSync } = require('node:fs');

try {
    renameSync('./music.txt', './new-music.txt');
} catch (err) {
  // handle the error
}
  1. 删除文件夹
const { rmdirSync } = require('node:fs');

try {
    // 目录不为空,则删除失败
    rmdirSync('./temp');
} catch (err) {
  // handle the error
  console.log(err);
}
  1. 删除文件
const { unlinkSync } = require('node:fs');

try {
    unlinkSync('./music1.txt');
} catch (err) {
  // handle the error
  console.log(err);
}
  1. 实战:递归创建文件夹
const { mkdirSync, statSync } = require('node:fs');
const path = require('node:path');

function mkdirs(dir) {
    const dirs = dir.split(path.sep);
    dirs.forEach((currentDir, index) => {
        const dirPath = dirs.slice(0, index + 1).join(path.sep);
        if (currentDir) {
            let isExisted = false;
            try {
                isExisted = statSync(dirPath).isDirectory();
            } catch (err) {
               // 说明文件夹不存在
            }

            if (!isExisted) {
                mkdirSync(dirPath);
            }
        }
    });
}

mkdirs(path.resolve(__dirname, './temp/logs'));

3.6 Modules:CommonJS模块

使用CommonJS规范的文件模块对象。

特性:

  1. require:加载文件模块导出的对象。
  2. module.exports:存储文件模块导出的对象。
  3. __dirname: 当前模块的文件夹路径。
  4. __filename: 当前模块的文件路径。
  5. exports:模块导出对象,是module.exports的别名。
  6. require.cache:存储已加载的模块的缓存对象。
  7. require.main:主入口文件模块对象。
  8. require.resolve:返回模块解析后的文件路径。
console.log(__dirname);
console.log(__filename);

exports = module.exports = {a : 1, b : 2};
exports.c = 3;

console.log(require.main);

// xxx.js不存在,会报错
delete require.cache[require.resolve('./xxx.js')];

3.7 Modules:ECMAScript模块

ECMAScript Module(ESM)是打包JavaScript代码以供重复使用的官方标准格式。模块是使用importexport相关的语句定义的。

生效规则

  1. 如果文件扩展名为.js。只有最近的package.json文件的type属性值为module时,Node.js才会认为文件使用的是ESM规范的模块,其它情况都会认为采用的是CommonJS规范的模块。
  2. 无论type字段的值如何,.mjs文件始终被视为ES模块,.cjs文件总是被视为CommonJS模块。

注意事项

  1. 必须指定文件扩展名。
  2. 文件夹的索引文件也必须要完全指定。

示例

// user.mjs
const name = 'lily';
const age = 28;

export default { name, age };

// index.mjs
import user from './user.mjs';
console.log(user);

特性

  1. 导入断言
    目前只支持json类型。
// animal.json
{
    "name": "monkey"
}
// index.mjs
import animal from './animal.json' assert { type: 'json' };
console.log('animal', animal);
  1. 直接在内置的模块中导出API
import { readFile } from 'node:fs';
readFile(new URL('./foo.txt', import.meta.url), { encoding: 'utf8' }, (err, source) => {
  if (err) {
    console.error(err);
  } else {
    console.log(source);
  }
});
  1. import.meta
    包含url和resolve属性的对象。
  2. import.meta.url
    返回当前文件的url路径,以file://协议开头。
// file:///app/index.mjs 
console.log(import.meta.url);
  1. import.meta.resolve
    返回需要被解析的文件的url路径,以file://协议开头。
// file:///app/user.mjs 
console.log(import.meta.resolve('./user.mjs'));

总结:ESM和CommonJS的区别

  1. ESM没有requireexportsmodule.exports
  2. ESM没有__filename__dirname
  3. ESM没有require.resolve,不过可以使用import.meta.resolve
  4. ESM没有require.cache

3.8 Modules:node:module API

提供了一些通用的与模块实例交互的方法。

  1. module.builtinModules

Node.js提供的所有模块的名称列表。可用于验证模块是否由第三方维护。

const builtin = require('node:module').builtinModules;
  1. module.createRequire(filename)
    创建require函数。filename参数必须是URL对象、URL字符串,或者是绝对路径。
// sibling-module.js
module.exports = {
    name: 'sibling-module',
};

// index.mjs
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);

const siblingModule = require('./sibling-module');
console.log(siblingModule);
  1. module.isBuiltin(moduleName)
    检测模块是否是内置模块。
const isBuiltin = require('node:module').isBuiltin;
console.log(isBuiltin('node:path')); // true
console.log(isBuiltin('path')); // true
console.log(isBuiltin('wss')); // false 

3.9 OS模块

node:fs模块与文件系统进行交互。

  1. os.EOL
const os = require('node:os'); 

console.log(os.EOL);

3.10 Stream模块

Stream是一个抽象接口,用于处理Node.js中的流数据。node:stream模块为实现流接口提供了API。
Stream可以是可读的,也可以是可写的,或者两者都有。所有流都是EventEmitter的实例。
Node.js提供了很多流对象,比如操作文件系统、对HTTP服务的请求等。
通常在处理大体积文件的时候用到流,防止占用过多的内存

  1. 创建文件只读流
// 创建只读流
const { createReadStream, statSync } = require('node:fs');

// highWaterMark
const stream = createReadStream('./demo.txt', { encoding: 'utf8' });

let totalLength = 0;
stream.on('data', (chunk) => {
    totalLength += Buffer.byteLength(chunk);
});

stream.on('end', () => {
    console.log(`数据读取完毕`);
    console.log(statSync('./demo.txt').size);
    console.log(totalLength);
});
  1. 创建指定范围的只读流
const rangeStream = createReadStream('./demo.txt', { encoding: 'utf8', start: 3, end: 5 });

let content = '';
rangeStream.on('data', (chunk) => {
    content += chunk;
});

rangeStream.on('end', () => {
    // 一个字符
    console.log(content);
});
  1. 创建文件可写流
// 复制文件
const { createReadStream, createWriteStream, statSync } = require('node:fs');

const readStream = createReadStream('./demo.txt', { encoding: 'utf8' });
const writeStream = createWriteStream('./demo-write.txt', { encoding: 'utf8' });

// 方式1:手动写入
readStream.on('data', (chunk) => {
    writeStream.write(chunk);
});

readStream.on('end', () => {
    console.log(`数据写入完毕`);
    console.log(statSync('./demo.txt').size === statSync('./demo-write.txt').size);
});

// 方式2:通过管道完成写入
readStream
    .on('end', () => {
        console.log(`数据读取完毕`);
    })
    .pipe(writeStream)
    .on('finish', () => {
        console.log(`写入完毕`);
        console.log(statSync('./demo.txt').size === statSync('./demo-write.txt').size);
    })
  1. 转换流
// 压缩文件
const { createReadStream, createWriteStream } = require('node:fs');
const zlib = require('node:zlib');

const readStream = createReadStream('./demo.txt');
const duplexStream = zlib.createGzip();
const writeStream = createWriteStream('./demo.tar.gz');

readStream.pipe(duplexStream).pipe(writeStream);
  1. 下载文件
const { createReadStream } = require('node:fs');
const zlib = require('node:zlib');
const http = require('node:http');

http.createServer((req, res) => {
    res.writeHead(200, {
        'Content-Type': 'application/octet-stream',
        'Content-Encoding': 'gzip',
        'Content-Disposition': 'attachment; filename=demo.tar.gz',
    });
    const readStream = createReadStream('./demo.txt');
    const duplexStream = zlib.createGzip();

    readStream.pipe(duplexStream).pipe(res);
}).listen(8890);

3.11 readline模块

node:readline模块提供了一个接口,用于一次一行地从Readable流读取数据。

  1. 问题提示
const readline = require('node:readline');
const { stdin: input, stdout: output } = require('node:process');

const rl = readline.createInterface({ input, output });

rl.question('What do you think of Node.js? ', (answer) => {
    console.log(answer);
    rl.close();
});
  1. 逐行读取文件内容
const { createInterface } = require('node:readline');
const { once } = require('node:events');
const { createReadStream } = require('node:fs');

(async () => {
    const rl = createInterface({
        input: createReadStream('./demo.txt'),
    });
    
    let line = 0;
    rl.on('line', (lineContent) => {
        console.log(`${++line}:`, lineContent.toString());
    });
    
    await once(rl, 'close');
    
    console.log('File read complete.');
})();
  1. 轻量的CLI
const readline = require('node:readline');
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  prompt: 'HAHA> ',
});

rl.prompt();

rl.on('line', (line) => {
  switch (line.trim()) {
    case 'hello':
      console.log('world!');
      break;
    default:
      console.log(`Say what? I might have heard '${line.trim()}'`);
      break;
  }
  rl.prompt();
}).on('close', () => {
  console.log('Have a great day!');
  process.exit(0);
}); 

3.12 process模块

process对象提供有关当前Node.js进程的信息。

  1. process.argv 和 process.argv0

process.argv属性返回一个数组,该数组包含Node.js进程启动时传递的命令行参数。
第一个元素将是process.execPath。如果需要访问argv[0]的原始值,请使用process.argv0。第二个元素将是正在执行的JavaScript文件的路径。剩下的元素将是任何额外的命令行参数。

const process = require('node:process');

/*
    [
        '/xxx/node/v20.8.0/bin/node',
        '/xxx/node-1/Node核心模块使用/10.process/index.js'
    ]
*/
console.log(process.argv);

// node
console.log(process.argv0);
  1. process.cwd()

返回Node.js进程的当前工作目录。

const process = require('node:process');

// /xxxx/Node核心模块使用/10.process
console.log(process.cwd());
  1. process.chdir(directory)
更改Node.js进程的当前工作目录。如果目录不存在,则抛出异常。
const { chdir, cwd } = require('node:process');

// Starting directory: /xxxx/Node核心模块使用/10.process
console.log(`Starting directory: ${cwd()}`);

try {
  chdir('../');

  // New directory: /xxxx/Node核心模块使用
  console.log(`New directory: ${cwd()}`);
} catch (err) {
  console.error(`chdir: ${err}`);
}
  1. process.env

返回一个包含用户环境的对象。可以手动添加一些环境信息到对象里面。

const process = require('node:process');

console.log(process.env);

process.env.mode = 'development';

console.log(process.env);

3.13 Buffer模块

Buffer对象表示固定长度的字节序列。许多Node.js API都支持Buffer。在日常工作开发中,我们基本不会用到这个模块。
虽然Buffer类在全局范围内可用,但仍然建议通过import或require语句显式引用它。

  1. Buffer.alloc(size, fill, encoding)

创建buffer对象。默认填充为0。


// 创建长度为5的缓冲区
const { Buffer } = require('node:buffer');

const buf1 = Buffer.alloc(5);
// <Buffer 00 00 00 00 00>
console.log(buf1);


// 创建长度为5的缓冲区,并将内容填充为a
const buf2 = Buffer.alloc(5, 'a');
// <Buffer 61 61 61 61 61>
console.log(buf2);

// 创建长度为11的缓冲区,并指定填充内容的编码格式(内容为:hello world)
const buf3 = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64');
// <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
console.log(buf3);
  1. Buffer.from(string | buffer)

根据已有的buffer或者字符串创建Buffer对象。

const { Buffer } = require('node:buffer');

// 根据字符串创建
const buf = Buffer.from('hello world', 'utf8');
// <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
console.log(buf);
console.log(buf.toString());


// 根据buffer对象创建
const buf1 = Buffer.from('buffer');
const buf2 = Buffer.from(buf1);

buf1[0] = 0x61;

// auffer
console.log(buf1.toString());
// buffer
console.log(buf2.toString());
  1. Buffer.byteLength(string, encoding)
    返回字符串编码后的字节长度。
const { Buffer } = require('node:buffer');
const str = '你好,world';

// 8字符,12字节
console.log(`${str.length}字符,${Buffer.byteLength(str, 'utf8')}字节`);
  1. Buffer.concat(list, totalLength)

返回结合后最新的buffer对象。

const { Buffer } = require('node:buffer');
const buf1 = Buffer.alloc(10);
const buf2 = Buffer.alloc(14);
const buf3 = Buffer.alloc(18);
const totalLength = buf1.length + buf2.length + buf3.length;

const bufA = Buffer.concat([buf1, buf2, buf3], totalLength);
// 42
console.log(bufA.length);
  1. Buffer.isBuffer(obj)

如果是buffer对象,则返回true。

const { Buffer } = require('node:buffer');
Buffer.isBuffer(Buffer.alloc(10)); // true
Buffer.isBuffer([]); // false
  1. buf.fill(value, offset, end, encoding)
const { Buffer } = require('node:buffer');
const buf = Buffer.alloc(10);
buf.fill('h', 2, 8, 'utf8');
// <Buffer 00 00 68 68 68 68 68 68 00 00>
console.log(buf);
  1. buf.includes(value)

如果value在buf中被找到,则返回true。

const { Buffer } = require('node:buffer');
const buf = Buffer.from('this is a buffer');
// true
console.log(buf.includes('this'));
  1. buf.indexOf(value)

查找value在buffer中的索引,没有找到则返回-1。

const { Buffer } = require('node:buffer');
const buf = Buffer.from('this is a buffer');
// 0
console.log(buf.indexOf('this'));
  1. buf.subarray(start, end)

根据start和end截取出一个新的buffer对象,原对象不会受影响。

const { Buffer } = require('node:buffer');
const buf = Buffer.from('this is a buffer');
const subBuf = buf.subarray(0, 4);
console.log(buf.toString());
console.log(subBuf.toString());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值