NodeJS笔记-系列
初识NodeJS
Express框架
NodeJS-MongoDB
NodeJS接口、会话控制
Node-回眸[一]
Node-回眸[二]
Node-回眸[三]
QX-AI
GPT-4
QX-AI初始化中...
暂无预设简介,请点击下方生成AI简介按钮。
介绍自己
生成预设简介
推荐相关文章
生成AI简介

初识

NodeJS是使用C++编写的基于ChromeV8引擎,开源、跨平台的JavaScript运行环境

中文API文档

Buffer

Buffer(缓冲区)存放二进制数据的缓存区,类似数组Array

1
2
let buf = Buffer.alloc(10);//每个二进制位都清0
let buf_2 = Buffer.allocUnsafe(10);//不清0,可能有旧数据,但速度较快

字符串转二进制编码,每个字符按编码转为二进制存入

1
2
3
let buf_3 = Buffer.from('hello');
console.log(buf_3);
// <Buffer 68 65 6c 6c 6f>

将数组存入Buffer

1
2
3
let buf_4 = Buffer.from([111, 112, 113, 114]);
console.log(buf_4);
// <Buffer 6f 70 71 72>

toString()Buffer转字符串

1
2
3
let buf_4 = Buffer.from([111, 112, 113, 114]);
console.log(buf_4.toString());// 默认采用UTF-8
// opqr

使用下标访问Buffer

1
2
3
4
5
let buf_5 = Buffer.from('chuckle');
console.log(buf_5[0]);//十六进制,99
console.log(buf_5[0].toString(2));//转为二进制,1100011
buf_5[0] = 90;// 修改
console.log(buf_5[0]);//十六进制,90

溢出,舍弃高位

1
2
3
buf_5[0] = 360;// 101101000
console.log(buf_5[0]);//十六进制,104
console.log(buf_5[0].toString(2));//1 0110 1000 => 110 1000

Buffer存中文,UTF-8中一汉字占三个字节

1
2
3
4
let buf_6 = Buffer.from('今天你好');
buf_6.write('轻笑');// 覆盖前两个
console.log(buf_6);//<Buffer e8 bd bb e7 ac 91 e4 bd a0 e5 a5 bd>
console.log(buf_6.toString());//轻笑你好

fs模块

文件系统(fs 模块)中的方法均有异步和同步版本,异步有回调函数,参数为err错误信息

创建 text.txt 并写入内容

1
2
3
4
5
6
7
8
9
10
11
12
const fs = require('fs');
//异步
fs.writeFile('./text.txt', '轻笑chuckle', err => {
if(err){
console.log(err);
return;
}
console.log('成功');
});
//同步
fs.writeFileSync('./text.txt', '轻笑chuckle');

appendFile 追加写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fs.appendFile('./text.txt', '\r\n追加内容', err => {
if(err){
console.log(err);
return;
}
console.log('成功');
});
// writeFile添加配置项实现追加写入
fs.writeFile('./text.txt', '\r\n追加内容',{flag: 'a'} , err => {
if(err){
console.log(err);
return;
}
console.log('成功');
});

createWriteStream 流式写入,适合频繁或大文件写入,与文件的连接不断开

1
2
3
4
5
const ws = fs.createWriteStream('.text.txt');
ws.write('1');
ws.write('2');
ws.write('3');
ws.close();// 断开连接,会自动断开,close可加可不加

readFile 文件读取

1
2
3
4
5
6
7
8
9
10
11
12
// 回调函数两个参数,错误信息和文件内容
fs.readFile('./text.txt', (err, data)=>{
if(err){
console.log(err);
return;
}
console.log(data.toString());
});
// 同步读取,直接返回文件内容
let data = fs.readFileSync('./text.txt');
console.log(data.toString());

createReadStream 流式读取

1
2
3
4
5
6
7
8
9
const rs = fs.createReadStream('./1.mp4');
rs.on('data', chunk =>{
console.log(chunk);// Buffer
console.log(chunk.length);// 每次读取64kb
});
rs.on('end', ()=>{
console.log('读取完成');
});

复制文件,使用流式操作更快,占用内存更少

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// readFile
fs.readFile('./text.txt', (err, data) =>{
if(err){
return console.error(err);
}
fs.writeFile('./text2.txt', data, err =>{});
});
// 流式操作
const rs = fs.createReadStream('./1.mp4');
const ws = fs.createWriteStream('./2.mp4');
rs.on('data', chunk =>{
ws.write(chunk);
});
// 流式操作pipe
rs.pipe(ws);

rename 文件重命名和移动

1
2
fs.rename('./1.mp4', './3.mp4', err=>{});
fs.rename('./3.mp4', './mp4/3.mp4', err=>{});

unlink 删除文件

1
2
fs.unlink('./2.mp4', err=>{});
fs.rm('./2.mp4', err=>{});

mkdir 创建文件夹

1
2
3
fs.mkdir('./a', err=>{});
// 递归创建
fs.mkdir('./a/b/c', {recursive: true},err=>{});

readdir 读取文件夹,读取到一个目标文件夹下文件名的数组

1
2
3
fs.readdir('./mp4', (err, data)=>{
console.log(data);// [ '3.mp4' ]
});

rmdir 删除文件夹

1
2
3
4
5
fs.rmdir('./a/b/c', err=>{});
// 递归删除,文件夹非空时使用
fs.rmdir('./a', {recursive: true},err=>{});
// 或使用rm
fs.rm('./a',{recursive: true} ,err=>{});

stat 查看资源状态

1
2
3
4
5
fs.stat('./3.mp4', (err, data)=>{
console.log(data);// 输出信息
console.log(data.isDirectory());//是否是文件夹
console.log(data.isFile());//是否是普通文件
});

__dirname 保存js文件所在目录的绝对路径,避免工作路径不同、工作区不同,导致相对路径不同而出bug

1
fs.writeFileSync(__dirname + '/1.txt', 'chuckle');

批量重命名文件,在文件名前加上0

1
2
3
4
5
6
7
8
9
10
11
fs.readdir(__dirname + '/rename', (err, data)=>{
data.forEach((item, index)=>{
let data = item.split('.');
let [num, suffix] = data;
if(Number(num)<10){
num = '0' + num;
}
fs.renameSync(`${__dirname}/rename/${item}`, `${__dirname}/rename/${num}.${suffix}`);
});
});

path模块

resolve 拼接规范的绝对路径

1
2
3
const path = require('path');
let path1 = path.resolve(__dirname + '/rename');
console.log(path1);// c:\chuckle\qx\nodejs\rename

其它方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
console.log(path.sep);// 获取当前系统的路径分隔符
// /
console.log(path.parse(__filename));// 解析路径并返回对象
// {
// root: 'c:\\',
// dir: 'c:\\chuckle\\qx\\nodejs',
// base: 'fs.js',
// ext: '.js',
// name: 'fs'
// }
console.log(path.basename(__filename));// 获取路径的基础名称
// fs.js
console.log(path.dirname(__filename));// 获取路径的目录名
// c:\chuckle\qx\nodejs
console.log(path.extname(__filename));// 获取文件的扩展名
// .js

http模块

搭建http服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 导入 http 模块
const http = require('http');
// 创建服务对象
const server = http.createServer((request, response)=>{
// 当服务接收到http请求的时候运行回调函数
response.setHeader('Content-Type', 'text/html; charset=utf-8');// 设置字符集防止乱码
response.end('你好');// 响应文本
});

// 监听端口,启动服务
server.listen(9000, ()=>{
// 当服务启动时运行回调函数
console.log('服务启动');
});

获取请求报文

获取请求行和请求头,修改createServe的回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const server = http.createServer((request, response)=>{
// 当服务接收到http请求的时候运行回调函数
// 获取请求方法
console.log(request.method);
// 获取请求url,只包含路径和查询字符串
console.log(request.url);
// 获取http协议版本号
console.log(request.httpVersion);
// 获取http的请求头
console.log(request.headers);
// 获取请求头中单独的属性值
console.log(request.headers.host);
response.end('hello world');
});

获取请求体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const server = http.createServer((request, response)=>{
// 当服务接收到http请求的时候运行回调函数
//获取请求体
let body = '';
// request也是一个可读流,绑定data事件一点点获取内容
request.on('data', chunk=>{
body += chunk;
})
// 绑定end事件
request.on('end', ()=>{
console.log(body);
// 响应
response.end('hello http');
});
});

获取请求路径和查询字符串

使用url模块获取请求路径和查询字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const url = require('url');// 导入url模块
const server = http.createServer((request, response)=>{
// 获取请求路径和查询字符串
console.log(request.url);
// 使用url模块解析url
let res = url.parse(request.url, true);// 第二个参数true将query属性值变为一个对象
console.log(res);
// Url {
// protocol: null,
// slashes: null,
// auth: null,
// host: null,
// port: null,
// hostname: null,
// hash: null,
// search: '?psw=1234&name=chuckle',
// query: [Object: null prototype] { psw: '1234', name: 'chuckle' },
// pathname: '/search',
// path: '/search?psw=1234&name=chuckle',
// href: '/search?psw=1234&name=chuckle'
// }
console.log(res.path);// 获取路径
console.log(res.query);// 获取查询字符串对象
console.log(res.query.psw);// 获取查询字符串中的属性值
});

使用URL类获取请求路径和查询字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const server = http.createServer((request, response)=>{
let url = new URL(request.url, 'http://127.0.0.1:9000');
console.log(url);
// URL {
// href: 'http://127.0.0.1:9000/search?psw=1234&name=chuckle',
// origin: 'http://127.0.0.1:9000',
// protocol: 'http:',
// username: '',
// password: '',
// host: '127.0.0.1:9000',
// hostname: '127.0.0.1',
// port: '9000',
// pathname: '/search',
// search: '?psw=1234&name=chuckle',
// searchParams: URLSearchParams { 'psw' => '1234', 'name' => 'chuckle' },
// hash: ''
// }
console.log(url.pathname);// 获取路径
console.log(url.searchParams);// 获取查询字符串map对象
console.log(url.searchParams.get('psw'));// map对象用get获取属性值
});

请求路径练习

根据请求路径响应不同内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const http = require('http');
const server = http.createServer((request, response)=>{
let url = new URL(request.url, 'http://127.0.0.1:9000');
// 获取请求方法
let {method} = request;
// 获取请求路径
let {pathname} = url;
// 根据请求路径响应不同内容
if (method === 'GET' && pathname === '/login'){
response.end('login');
}else if (method === 'GET' && pathname === '/reg'){
response.end('reg');
}else{
response.end('Not Found');
}
});
server.listen(9000, ()=>{
console.log('服务启动');
});

设置响应报文

设置响应码、响应头、响应体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const http = require('http');
const server = http.createServer((request, response)=>{
// 1.设置响应状态码
response.statusCode = 200;
// 2.设置响应状态的描述
response.statusMessage = 'chuckle';
// 3.响应头,两个参数,响应名和值,可自定义
response.setHeader('Content-Type', 'text/html; charset=utf-8');
response.setHeader('Server', 'nodejs');
response.setHeader('MyHeader', 'chuckle');
// 设置多个同名响应头
response.setHeader('test', ['a', 'b', 'c']);
// 4. 设置响应体,end只能执行一个,write可以有多个
response.write('chuckle');
response.write('qx');
// end是必须的,但设置write后一般end设为空
response.end();
});

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

配合fs模块响应文件

1
2
3
4
5
6
7
8
9
10
11
12
13
const http = require('http');
const fs = require('fs');

const server = http.createServer((request, response)=>{
let html = fs.readFileSync(__dirname + '/2.html');
response.write(html);
response.end();
});

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

响应文件练习

根据文件路径响应不同内容,下面这种写法不方便,后面有优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const http = require('http');
const fs = require('fs');

const server = http.createServer((request, response)=>{
let html = fs.readFileSync(__dirname + '/2.html');
let css = fs.readFileSync(__dirname + '/2.css');
let url = new URL(request.url, 'http://127.0.0.1:9000');
let {pathname} = url;
if(pathname === '/'){
response.write(html);
}else if(pathname === '/2.css'){
response.write(css);
}else {
response.statusCode = 404;
response.write('404');
}
response.end();
});

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

优化版:静态资源响应服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const http = require('http');
const fs = require('fs');

const server = http.createServer((request, response)=>{
let url = new URL(request.url, 'https://127.0.0.1:9000');
let {pathname} = url;
let root = __dirname;//网站根目录,资源都在这个根目录下找
let filePath = root + pathname;
fs.readFile(filePath, (err, data)=>{
if(err){
response.statusCode = 404;
response.write('404');
response.end();
return;
}
response.write(data);
response.end();
});
});

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

设置资源mime类型

mime类型用于表示文档、文件或字节流的性质和类型

HTTP服务可以设置响应头 Content-Type 来表明响应体的mime类型,浏览器会根据该类型决定如何处理资源

常见mime类型:
1、html text/html
2、css text/css
3、js text/javascript
4、png image/png
5、jpg image/jpeg
6、gif image/gif
7、mp4 video/mp4
8、mp3 audio/mpeg
9、json application/json

对于未知的资源类型,可以选择 application/octet-stream 刘览器在遇到该类型的响应时,会对响应体内容进行独立存储,实现下载效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
const http = require('http');
const fs = require('fs');
const path = require('path');
let mime = {
// html就加上字符集,避免乱码
html: 'text/html;charset=utf-8',
css: 'text/css',
js: 'text/javascript',
png: 'image/png',
jpg: 'image/jpeg',
gif: 'image/gif',
mp4: 'video/mp4',
mp3: 'audio/mpeg',
json: 'application/json',
other: 'application/octet-stream'
}

const server = http.createServer((request, response)=>{
let url = new URL(request.url, 'https://127.0.0.1:9000');
let {pathname} = url;
let root = __dirname;//网站根目录,资源都在这个根目录下找
let filePath = root + pathname;
fs.readFile(filePath, (err, data)=>{
if(err){
response.statusCode = 404;
response.write('404');
response.end();
return;
}
//获取文件后缀名
let ext = path.extname(filePath).slice(1);
let type = mime[ext] ? mime[ext] : mime[other];
console.log(type);
response.setHeader('Content-Type', type);
response.write(data);
response.end();
});
});

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

完善错误处理

Node.js常见系统错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
const http = require('http');
const fs = require('fs');
const path = require('path');
let mime = {
// html就加上字符集,避免乱码
html: 'text/html;charset=utf-8',
css: 'text/css',
js: 'text/javascript',
png: 'image/png',
jpg: 'image/jpeg',
gif: 'image/gif',
mp4: 'video/mp4',
mp3: 'audio/mpeg',
json: 'application/json',
other: 'application/octet-stream'
}
let errs = {
ENOENT: 404,
EPERM: 403
}

const server = http.createServer((request, response)=>{
response.setHeader('Content-Type', 'text/html; charset=utf-8');
// 请求方法错误
console.log(request.method);
if(request.method !== 'GET'){
response.statusCode = 405;
response.end('出错了');
return;
}
let url = new URL(request.url, 'https://127.0.0.1:9000');
let {pathname} = url;
let root = __dirname;//网站根目录,资源都在这个根目录下找
let filePath = root + pathname;
fs.readFile(filePath, (err, data)=>{
if(err){
let errCode = errs[err.code] ? errs[err.code] : 500;
console.log(errCode);
response.statusCode = errCode;
response.write('出错了');
response.end();
return;
}
//获取文件后缀名
let ext = path.extname(filePath).slice(1);
let type = mime[ext] ? mime[ext] : mime[other];
response.setHeader('Content-Type', type);
response.write(data);
response.end();
});
});

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

模块化

将一个复杂的程序文件,按一定规则拆分为多个文件,即模块化,每个小文件就算一个模块

模块内部数据是私有的,但可以主动度外暴露出去

模块化项目:
编写项目时一个个模块编写,再组合起来

模块化优点:
1、防止命名冲突
2、高服用性
3、高维护性

暴露数据

在模块中使用module.exportsexports向外暴露数据,但两者不能同时使用

隐式关系:exports = module.exports = {}

1
2
3
4
5
6
7
8
9
10
11
let name = 'chuckle';
let age = 19;
function fun() {
console.log(name);
}
module.exports = {
name,
fun
}
exports.age = age;

导入模块

  1. 导入js/sjon文件或c编写的mode扩展插件可以省略后缀,同名优先导入js
  2. 导入自己的模块最好使用相对路径 ./
  3. 导入其它类型的文件会以js文件处理
  4. 导入文件夹,首先会去找文件夹内package.json里main属性对应的文件,如果没用main属性或package.json则去找文件夹内的index.js或index.json,如果还没找到则报错,main属性对应的文件不存在也会报错
  5. 导入nodejs内置模块时,直接写模块名即可
1
2
const mod = require('./1.js');
const mod = require('./1');

包管理工具

包,即package,是一组特定功能的源码集合,即别人写好的各种工具

管理包,即对包进行下载、安装、删除、上传操作

常见包管理工具:npm、yarn、cnpm

npm

Node Package Manager即npm,是nodejs内置的包管理工具

package.json 是包必须有的配置文件
package-lock.json 存放依赖包的版本信息

require导入npm包流程:
1、在当前文件夹下的node_modules中找同名文件夹
2、在上级目录中node_modules中找同名文件夹,直到磁盘根目录

1
2
npm i <包名@版本号>// 安装
npm r <包名>// 移除

npm配置命令别名,修改package.json的 scripts 属性,通过npm run <命令名>运行命令,会自动向上寻找,添加start属性,可以省略run,直接npm start

1
2
3
4
5
6
7
8
9
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"server": "node ./index.js",
"start": "node ./index.js"
},
// 使用
npm run server
npm start

通过指定源来提高包安装速度

1
npm i <包名> --registry=https://registry.npmmirror.com

开发和生产依赖

开发环境:写源代码、工程文件时的环境,一般指程序员的电脑,只能由程序员访问
生产环境:项目代码正式运行的环境,一般指服务器,所有用户都能访问

开发依赖:只在写代码时用到的依赖包,安装时添加 -D 或 —save-dev
生产依赖:默认项,既在写代码时使用,又在代码运行时使用的依赖包,安装时添加 -S 或 —save 参数,

package.json中,dependencies属性保存生产依赖包信息,devDependencies属性保存开发依赖包信息

全局安装

开发和生产依赖都是局部安装的包,存放在工作目录的node_modules文件夹中

全局安装 npm i -g 不受工作目录位置限制,无需导入,一般是在命令行中通过独立命令去使用

npm root -g 查看全局安装包的位置

只有全局类的工具如 gulp、nodemon等才适合全局安装,通过查看包的文档来确定安装方式

cnpm

cnpm—npmmirror镜像站
使用方式和npm一样,是淘宝构建的完整 npmjs.org 镜像,了解即可,大多还是使用npm

修改npm的源地址来使用淘宝镜像

1
2
3
4
5
6
7
8
9
10
11
12
// 查看npm的源
npm config list
// 不推荐,该源无法上传包
npm config set registry https://registry.npmmirror.com/
// 安装时临时指定源
npm i <包名> --registry=https://registry.npmmirror.com
// 使用nrm管理源,推荐
npm i -g nrm
nrm use taobao
nrm ls// 列出可切换的所有源
npm install -g nrm open@8.4.2 --save// 使用nrm有报错则运行此命令

yarn

yarn 是由 Facebook 在 2016 年推出的新的 Javascript 包管理工具

yarn和npm需要按需选用,包管理工具不能混用,yarn的锁文件是yarn.lock

npm i -g yarn安装yarn

常用命令:

  1. 初始化 yarn init / yarn init -y
  2. 安装包
    1. yarn add <包名> 生产依赖
    2. yarn add <包名> —dev 开发依赖
    3. yarn global add <包名> 全局安装,第一次使用记得手动配置环境变量
  3. 删除包
    1. yarn remove uniq 删除项目依赖包
    2. yarn global remove nodemon 全局删除包
  4. 安装项目依赖 yarn
  5. 运行命令别名 yarn <别名>
  6. 全局安装包的位置 yarn global bin

管理npm包

  1. 修改 package.json 中的版本号
  2. npm publish 提交包
  3. npm unpublish —force 删除包

nvm管理node版本

nvm 全称 Node Version Manager 用来管理 node 版本的工具

常用命令:

  1. nvm list available 显示所有可以下载的 Node.js 版本
  2. nvm list 显示已安装的版本
  3. nvm install 18.12.1 安装 18.12.1 版本的 Node.js
  4. nvm install latest 安装最新版的 Node.js
  5. nvm uninstall 18.12.1 删除某个版本的 Node.js
  6. nvm use 18.12.1 切换 18.12.1 的 Node.js

设置下载node的镜像

1
export NVM_NODEJS_ORG_MIRROR=https://npmmirror.com/mirrors/node/