920 lines
23 KiB
Markdown
Executable File
920 lines
23 KiB
Markdown
Executable File
# NodeJS
|
||
|
||
> Node 是一个构建于 Chrome V8引擎之上的一个 Javascript 运行环境, 作用是让 js 拥有开发服务端的功能
|
||
|
||
* 使用事件驱动、非阻塞 IO 模型(异步读写)使得它非常的轻量级和高效
|
||
* Node 中绝大多数 API 都是异步(类似于 ajax),目的是提高性能
|
||
* node. js 官网: https://nodejs.org/en/
|
||
* 中文文档: http://nodejs.cn/api/
|
||
* 英文文档: https://nodejs.dev/en/api/v19/documentation/
|
||
* npm 官网 https://www.npmjs.com
|
||
|
||
## 安装
|
||
|
||
```sh
|
||
# 查看版本
|
||
node -v
|
||
```
|
||
|
||
node 版本管理: 安装 `n`
|
||
|
||
```sh
|
||
npm install -g n
|
||
# n --help
|
||
```
|
||
|
||
监控 js 变化并重启服务: `node-dev`
|
||
|
||
```sh
|
||
npm i -g node-dev
|
||
node-dev app.js
|
||
```
|
||
|
||
## 客户端 js 和服务端 js
|
||
|
||
客户端 JavaScript 由三部分组成
|
||
|
||
* ECMAScript:确定 js 的语法规范
|
||
* DOM:js 操作网页内容
|
||
* BOM:js 操作浏览器窗口
|
||
|
||
node 中的 JavaScript 组成
|
||
|
||
* ECMAScript
|
||
* 核心模块
|
||
* 第三方模块
|
||
|
||
基本的语法和写法和之前的 js 没有本质的区别
|
||
|
||
* **在 nodejs 中使用 dom 与 bom 的 api 程序会报错**
|
||
* 服务器端没有界面
|
||
* 不需要操作浏览器和页面元素
|
||
|
||
## 运行 node. js 程序
|
||
|
||
`node [js文件路径]`
|
||
|
||
生产环境: `pm2`
|
||
|
||
## 模块化
|
||
|
||
### CommonJS 规范
|
||
|
||
> 模块必须通过 ` module.exports={xxx:xxx}` 导出对外的变量或接口,通过 `require() ` 来导入其他模块的输出到当前模块作用域中。
|
||
|
||
CommonJS模块的特点:
|
||
|
||
* 所有代码运行在当前模块作用域中,不会污染全局作用域
|
||
* 模块同步加载,根据代码中出现的顺序依次加载
|
||
* 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
|
||
|
||
==模块使用前要先导入==
|
||
|
||
```js
|
||
/**
|
||
* 模块化规范
|
||
* CommonJS: 2015年前社区开发, nodejs官方默认
|
||
* - 自定义模块
|
||
* - 一个js文件就是一个模块
|
||
* - 使用 exports 或者 module.exports 暴露:
|
||
* exports.xxx = xxx
|
||
* module.exports = {xxx:xxx}
|
||
* 不能使用 exports = {xxx:xxx} 这是在给变量赋值, 上面的是修改对象的属性
|
||
* - 使用 require("模块的路径") 引入, 用变量来接收: const {xxx} = require()
|
||
* - 后缀自动补全: 先找js, 再找json
|
||
* - 核心模块
|
||
* - require("模块名")
|
||
* - require("node:模块名")
|
||
* nodejs将以下内容视为CommonJS
|
||
* 1. 使用.cjs扩展名
|
||
* 2. package.json的type属性为CommonJS,且扩展名为js
|
||
* 3. package.json不包含type属性,且扩展名为js
|
||
* 4. 扩展名是mjs, cjs, json, node, js以外的值, package.json的type属性不是module
|
||
*/
|
||
|
||
(function(exports, require, module, __filename, __dirname){
|
||
// 所有的CommonJS模块都会被包装到一个函数里
|
||
// exports, require是作为参数传进来的
|
||
console.log(arguments) // 证明
|
||
})
|
||
```
|
||
|
||
### ES Modules (ESM)
|
||
|
||
> export 导出, import 导入
|
||
|
||
```js
|
||
|
||
/* ES模块: 原生, 2015年es6标准发布
|
||
* - mjs 扩展名
|
||
* - package.json的type属性设置为module
|
||
*
|
||
* - 导出: export let a = xxx
|
||
* export const b = xxx
|
||
* - 导入: import {} from '路径.mjs'
|
||
* 改别名 import {a as b} from './test.mjs'
|
||
* 开发时要尽量避免 import * as c from './test.mjs' , 按需引用
|
||
* - 设置默认导出: export default function sum(){}
|
||
* default 后面跟值, export default let a = 0 这种不行, 后面是语句
|
||
* 一个模块只有一个默认导出
|
||
* - 默认导入: import sum {a} from './test.mjs'
|
||
* import {default as sum, a} from './test.mjs'
|
||
* 默认导入可以随便起名
|
||
* 默认导入可以和按需导入一起用
|
||
* - 通过es模块化导入的, 都是常量
|
||
* - es模块都是运行在严格模式下
|
||
*/
|
||
```
|
||
|
||
### require() 和 import, import()
|
||
|
||
#### 简单理解
|
||
|
||
* require(): 在JS本身不支持模块的情况下模拟出来的模块系统, 本质是立即执行函数
|
||
* import: 静态引入,在编译时完成模块加载
|
||
* import(): 和require()一样是动态加载,不同的是,它是异步的,返回一个Promise对象,而require()是同步的
|
||
|
||
#### 缓存方式
|
||
|
||
* require 是浅拷贝
|
||
|
||
```js
|
||
// 2.js
|
||
let num = 1;
|
||
let obj = {
|
||
num: 1
|
||
};
|
||
function add() {
|
||
num += 1;
|
||
obj.num += 1;
|
||
}
|
||
module.exports = { num, obj, add };
|
||
|
||
// testRequire.js
|
||
let a = require('./2.js');
|
||
console.log(a.num); // 1
|
||
console.log(a.obj.num); // 1
|
||
a.add();
|
||
console.log(a.num); // 1
|
||
console.log(a.obj.num); // 2
|
||
```
|
||
|
||
这里的 num 是基本数据类型, require 引入的是它的副本, add 函数里的 num + 修改的是局部变量, 修改不到副本
|
||
|
||
* import 并不对输出结果进行拷贝,而是直接指向输出结果的引用
|
||
|
||
```js
|
||
// 1.js
|
||
let num = 1;
|
||
let obj = {
|
||
num: 1
|
||
};
|
||
function add() {
|
||
num += 1;
|
||
obj.num += 1;
|
||
}
|
||
export { num, obj, add };
|
||
|
||
// testImport.js
|
||
import * as a from './1.js';
|
||
console.log(a.num); // 1
|
||
console.log(a.obj.num); // 1
|
||
a.add();
|
||
console.log(a.num); // 2
|
||
console.log(a.obj.num); // 2
|
||
```
|
||
|
||
* import() 也是引用
|
||
|
||
```js
|
||
// 1.js
|
||
let num = 1;
|
||
let obj = {
|
||
num: 1
|
||
};
|
||
function add() {
|
||
num += 1;
|
||
obj.num += 1;
|
||
}
|
||
export { num, obj, add };
|
||
|
||
// testImportFunction.js
|
||
let a = await import('./1.js');
|
||
console.log(a.num); // 1
|
||
console.log(a.obj.num); // 1
|
||
a.add();
|
||
console.log(a.num); // 2
|
||
console.log(a.obj.num); // 2
|
||
```
|
||
|
||
#### 相互引用
|
||
|
||
* require()无法引入ES6模块
|
||
* import可以引入CommonJS模块, 是把module.exports对象整体引入,类似于对exports default的接收,直接用一个变量 default 来接收。
|
||
|
||
```js
|
||
// 2.js
|
||
let num = 1;
|
||
let obj = {
|
||
num: 1
|
||
};
|
||
module.exports = { num, obj, add };
|
||
|
||
// testImportFunction.js
|
||
let a = await import('./2.js');
|
||
console.log(a.default.num); // 1
|
||
console.log(a.default.obj.num); // 1
|
||
```
|
||
|
||
* import()整体接收module.exports这个对象,并把它放在default属性下
|
||
|
||
```js
|
||
// 2.js
|
||
let num = 1;
|
||
let obj = {
|
||
num: 1
|
||
};
|
||
module.exports = { num, obj, add };
|
||
|
||
// testImportFunction.js
|
||
let a = await import('./2.js');
|
||
console.log(a.default.num); // 1
|
||
console.log(a.default.obj.num); // 1
|
||
```
|
||
|
||
## node. js 核心模块
|
||
|
||
Node 应用是由模块组成的,Node 遵循了 `CommonJS`的模块规范,来隔离每个模块的作用域,使每个模块在它自身的命名空间中执行。
|
||
|
||
### fs文件模块(读写文件)
|
||
|
||
先导入文件模块
|
||
|
||
```js
|
||
const fs = require('fs')
|
||
```
|
||
|
||
#### readFile异步读取
|
||
|
||
```js
|
||
fs.readFile(path[, options], callback(err,data))
|
||
/**
|
||
* 第一个参数:文件路径
|
||
* 第二个参数:编码格式 (可选参数,默认为buffer二进制,buffer:数据缓冲区)
|
||
* 第三个参数:读取回调操作(异步操作)
|
||
* err:如果读取成功, err为null,否则读取失败(一般文件路径错误或者找不到文件)
|
||
* data:读取到的数据(字符串|二进制)
|
||
*/
|
||
```
|
||
|
||
示例:
|
||
|
||
```js
|
||
fs.readFile('./data/aaa.txt','utf-8',(err,data)=>{
|
||
//按utf-8编码读取, 解决中文乱码
|
||
if(err){
|
||
console.log(err);
|
||
//抛出异常,throw的作用就是让node程序终止运行,方便调试
|
||
throw err;
|
||
}else{
|
||
console.log(data);
|
||
};
|
||
});
|
||
```
|
||
|
||
同步读取(几乎不用,会阻塞,一般在异步的api后面加上Sync就是同步):
|
||
|
||
```js
|
||
let data = fs.readFileSync('./data/aaa.txt','utf-8')
|
||
```
|
||
|
||
#### writeFile异步写入
|
||
|
||
```js
|
||
fs.writeFile(file, data[, options], callback(err))
|
||
/**
|
||
* 第一个参数:文件路径
|
||
* 第二个参数:要写入的数据
|
||
* 第三个参数:文件编码 默认utf-8
|
||
* 第四个参数: 异步回调函数
|
||
* err: 如果成功,err为null.否则读取失败
|
||
*/
|
||
```
|
||
|
||
1. 默认写入会覆盖
|
||
2. 如果文件名不存在,新创建再写入
|
||
3. 如果文件夹不存在,报错
|
||
|
||
示例:
|
||
|
||
```js
|
||
fs.writeFile('./data/bbb.txt','测试','utf-8',(err)=>{
|
||
if(err){
|
||
throw err;
|
||
}else{
|
||
console.log('写入成功');
|
||
};
|
||
});
|
||
```
|
||
|
||
#### 异步追加
|
||
|
||
异步地追加数据到文件,如果文件尚不存在则创建文件
|
||
|
||
```js
|
||
fs.appendFile(path, data[, options], callback(err))
|
||
```
|
||
|
||
#### Promise版本的fs方法
|
||
|
||
```js
|
||
const fs = require("node:fs/promises")
|
||
|
||
fs.readFile(path.resolve(__dirname, './hello.js'))
|
||
.then(buffer=>{
|
||
console.log(buffer.toString()) // 也可以直接用toSting()方法转成字符串
|
||
})
|
||
.catch(err=>{
|
||
console.log(err)
|
||
})
|
||
|
||
|
||
// 或者
|
||
;(async()=>{
|
||
try{
|
||
const buffer = await fs.readFile(path.resolve(__dirname, './hello.js')
|
||
console.log(buffer.toString())
|
||
}catch((err)=>{
|
||
console.log(err)
|
||
})
|
||
})()
|
||
```
|
||
|
||
```js
|
||
// 常见方法
|
||
fs.mkdir() // 创建目录
|
||
fs.rmkdir() // 删除目录
|
||
fs.rm() // 删除文件
|
||
fs.rename() // 重命名
|
||
fs.copyFile() // 复制
|
||
```
|
||
|
||
### path路径模块
|
||
|
||
==在服务端开发中,一般不要使用相对路径,而使用绝对路径==
|
||
|
||
```js
|
||
const path = require('path')
|
||
```
|
||
|
||
#### nodejs中的绝对路径和相对路径
|
||
|
||
* node中的相对路径: `./` 不是相对于当前文件所在路径,而是相对于执行node命令的文件夹路径(当前被执行的文件所在的文件夹路径).
|
||
* 解决方案:在nodejs中,每一个js文件都有两个全局属性,它可以帮助我们获取到文件的绝对路径
|
||
* \_\_filename:当前js文件绝对路径
|
||
* ==\_\_dirmame:当前js文件所在目录的绝对路径==
|
||
* windown中路径 用双反斜杠 `\\` 而不是 `\`
|
||
|
||
示例:
|
||
|
||
```js
|
||
const fs = require('fs')
|
||
let path = __dirname + '/aaa.txt'
|
||
console.log(path)
|
||
fs.readFile(path,'utf-8',(err,data)=>{
|
||
if(err){
|
||
console.log(err);
|
||
throw err;
|
||
}else{
|
||
console.log(data);
|
||
};
|
||
});
|
||
```
|
||
|
||
#### join()方法 路径拼接
|
||
|
||
```js
|
||
path.join([...paths])
|
||
/*使用path模块拼接文件路径与使用'+'连接符拼接的好处
|
||
1.会自动帮我们正确添加路径分隔符 '/',我们无需手动添加
|
||
2.当我们路径格式拼接错误的时候,能自动帮我们转换正确的格式
|
||
*/
|
||
```
|
||
|
||
示例:
|
||
|
||
```js
|
||
let filePath = path.join(__dirname, './page/login.html')
|
||
```
|
||
|
||
#### resolve()方法 路径处理
|
||
|
||
```js
|
||
// 把一个路径或路径片段的序列解析为一个绝对路径
|
||
// 传入路径从右至左解析,遇到第一个绝对路径解析停止
|
||
// 如果没有传入参数,将只返回当前根目录
|
||
path.resolve([...paths])
|
||
```
|
||
|
||
示例:
|
||
|
||
```js
|
||
// "/b" 就是遇到的第一个绝对路径
|
||
path.resolve('/a', '/b', 'c') // /b/c
|
||
path.resolve('/a', './b', 'c') // /a/b/c
|
||
//因为没有遇到第一个绝对路径,所以会一直向上解析(根目录路径/a/b/c)
|
||
path.resolve('a', 'b', 'c') // /Users/siyuan/Desktop/example/node测试/a/b/c
|
||
```
|
||
|
||
### process
|
||
|
||
> 获取进程信息, 或者对进程进行操作
|
||
|
||
使用: 全局变量, 直接使用
|
||
|
||
### 属性和方法
|
||
|
||
* process.exit(code): 结束当前进程, code默认0
|
||
* process.nextTick(()=>{}): 将函数插入tick队列, 调用栈=>tick=>微任务队列=>宏任务队列
|
||
|
||
## 服务器基础
|
||
|
||
### 基本的访问流程
|
||
|
||
1. 输入主机地址
|
||
2. 指定端口(如果没有指定, 默认是80)
|
||
3. 指定需要访问的资源路径
|
||
4. 发起请求
|
||
5. 获取服务器返回的结果并处理
|
||
|
||
### http协议
|
||
|
||
超文本传输协议(**H**yper**T**ext **T**ransfer **P**rotocol), 是基于TCP/IP协议之上的应用层协议
|
||
|
||
> HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准
|
||
> 客户端和服务器的通信必须遵守某种协议,http协议就是最常见的一种
|
||
|
||
### 端口
|
||
|
||
端口是通过端口号来标记的,端口号只有整数,范围是从0 到65535(2^16-1)
|
||
|
||
常见的端口号:
|
||
|
||
* 80:web服务器端口
|
||
* 3306:mysql数据服务器端口
|
||
|
||
查询端口状态`netstat`
|
||
|
||
以数字格式显示地址和端口信息`netstat -n`
|
||
|
||
### 常见的状态码
|
||
|
||
* 200: 请求已成功,请求所希望的响应头或数据体将随此响应返回
|
||
* 301: 请求资源永久重定向
|
||
* 302: 请求资源临时重定向
|
||
* 403: 无授权
|
||
* 404: 请求失败,请求所希望得到的资源未被在服务器上发现
|
||
* 500: 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器端的源代码出现错误时出现
|
||
|
||
### 返回数据的格式
|
||
|
||
* text/html格式:html代码,浏览器会以html语法解析
|
||
* text/css:样式,浏览器会以css语法解析
|
||
* application/javascript:js代码,浏览器会以js语法解析
|
||
* application/json:json格式字符串,描述从服务器返回的数据
|
||
|
||
## hellow app.js
|
||
|
||
流程:
|
||
|
||
1. 导入模块 `const * = require(*)`
|
||
2. 创建服务器`const server = http.creatServer()`
|
||
3. 监听端口`server.listen(端口, ()=>{})`
|
||
4. 响应请求, 进行事件处理`server.on('request', (req,res)=>{})`
|
||
|
||
注意点:
|
||
|
||
* `req.url`可以获取当前用户请求的url
|
||
* 中文乱码
|
||
|
||
```js
|
||
res.setHeader('Content-type','text/html;charset=UTF-8')
|
||
|
||
// html页面不需要, 头部已有
|
||
```
|
||
|
||
* 客户端没有指定url,默认为`/`
|
||
|
||
```js
|
||
//1.导入http模块
|
||
|
||
//2.创建服务器
|
||
|
||
//3.监听端口
|
||
/*
|
||
第一个参数:端口号
|
||
第二个参数:ip地址 默认不写,就是本机ip(127.0.0.1)
|
||
第三个参数:一个回调函数,启动时会调用
|
||
*/
|
||
|
||
//4.处理请求
|
||
```
|
||
|
||
```js
|
||
const http = require('http')
|
||
|
||
const server = http.createServer()
|
||
|
||
server.listen(3000,'127.0.0.1',(err)=>{
|
||
console.log('服务器开启成功: http://127.0.0.1:3000');
|
||
})
|
||
|
||
server.on('request',(req,res)=>{
|
||
// 所有请求都响应 'hello word'
|
||
res.end('hello word')
|
||
})
|
||
```
|
||
|
||
## 响应页面
|
||
|
||
```js
|
||
const http = require('http')
|
||
const fs = require('fs')
|
||
const path = require('path')
|
||
|
||
const server = http.createServer()
|
||
server.listen(3000, () => {
|
||
console.log('已开始监听 http://127.0.0.1:3000')
|
||
})
|
||
|
||
server.on('request', (req, res) => {
|
||
// 每个请求,都执行这里的代码
|
||
const url = req.url
|
||
console.log(url)
|
||
switch (url) {
|
||
case '/':
|
||
case '/index':
|
||
res.end('hello word')
|
||
break
|
||
case '/login':
|
||
// 页面head已经有编码格式
|
||
fs.readFile(path.join(__dirname, './page/login.html'), (err, data) => {
|
||
if (err) {
|
||
res.end('404 not found')
|
||
} else {
|
||
res.end(data)
|
||
}
|
||
})
|
||
break
|
||
default:
|
||
res.end('404 not found')
|
||
break
|
||
}
|
||
})
|
||
```
|
||
|
||
## 允许跨域
|
||
|
||
```js
|
||
res.setHeader('Access-Control-Allow-Origin', '*')
|
||
```
|
||
|
||
## 响应不同的请求
|
||
|
||
`req.method`获取请求的类型
|
||
|
||
### get请求
|
||
|
||
```js
|
||
const http = require('http')
|
||
const fs = require('fs')
|
||
const path = require('path')
|
||
|
||
const server = http.createServer()
|
||
server.listen(3000, () => {
|
||
console.log('127.0.0.1:3000')
|
||
})
|
||
server.on('request', (req, res) => {
|
||
const url = req.url
|
||
console.log('url:', url)
|
||
const method = req.method
|
||
console.log('method', method)
|
||
|
||
if (url === '/getUserList' && method === 'GET') {
|
||
fs.readFile(path.join(__dirname, './4-user.json'), 'utf-8', (err, data) => {
|
||
if (err) {
|
||
console.log(err)
|
||
res.end('404')
|
||
} else {
|
||
res.setHeader('Content-type', 'text/html;charset=UTF-8')
|
||
res.end(data)
|
||
}
|
||
})
|
||
return
|
||
}
|
||
|
||
res.end('hello word')
|
||
})
|
||
|
||
```
|
||
|
||
### post请求
|
||
|
||
```js
|
||
/**
|
||
* node支持大容量的参数传递, 它会分批接收参数, 接收参数会触发两个事件
|
||
* 1.给req注册一个data事件
|
||
* req.on('data', (chunk)=>{})
|
||
* 每接收一次参数就触发一次, 接收到的chunk是字符串格式
|
||
* 如果参数较多,它支持分批进行参数的接收,当客户端每发送一次数据流,都会触发里面的回调函数,我们需要主动将这些数据拼接起来
|
||
*
|
||
* 2.给req注册一个end事件
|
||
* req.on('end', ()=>{})
|
||
* 当客户端post数据全部发送完毕之后,就会触发这个事件
|
||
*
|
||
* 3.使用querystring模块解析接收完成的post参数数据
|
||
*/
|
||
```
|
||
|
||
```js
|
||
// 服务器
|
||
const http = require('http')
|
||
const fs = require('fs')
|
||
const path = require('path')
|
||
// 解析参数的querystring模块
|
||
const querystring = require('querystring')
|
||
|
||
const server = http.createServer()
|
||
server.listen(3000, () => {
|
||
console.log('127.0.0.1:3000')
|
||
})
|
||
server.on('request', (req, res) => {
|
||
// 允许跨域
|
||
res.setHeader('Access-Control-Allow-Origin', '*')
|
||
const url = req.url
|
||
console.log('url:', url)
|
||
const method = req.method
|
||
console.log('method', method)
|
||
|
||
if (url === '/login' && method === 'POST') {
|
||
let postData = ''
|
||
// 1.注册一个data事件
|
||
req.on('data', chunk => {
|
||
//具体多少次,取决于客户端带宽
|
||
postData += chunk
|
||
})
|
||
req.on('end', () => {
|
||
// 2.给req注册一个end事件
|
||
// 3.使用querystring模块解析接收完成的post参数数据
|
||
let postObj = querystring.parse(postData)
|
||
console.log('postObj',postObj)
|
||
if (postObj.username == 'admin' && postObj.password == '123456') {
|
||
res.end('yes')
|
||
} else {
|
||
res.end('no')
|
||
}
|
||
})
|
||
} else {
|
||
res.end('hello word')
|
||
}
|
||
})
|
||
|
||
```
|
||
|
||
登录页
|
||
|
||
```html
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Document</title>
|
||
</head>
|
||
<body>
|
||
<form method="post" action='http://127.0.0.1:3000/login'>
|
||
用户名:<input type="text" name='username' placeholder="请输入用户名"><br>
|
||
密码: <input type="password" name='passwrod' placeholder="请输入密码"> <br>
|
||
<input type="submit" value="提交">
|
||
</form>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
## 模块化
|
||
|
||
可以先把路由和响应拆分出来
|
||
|
||
以一个登录页面为例:
|
||
|
||
### 程序入口
|
||
|
||
```js
|
||
// app.js
|
||
// 路由被拆分到路由模块
|
||
const http = require('http')
|
||
const router = require('./js/router')
|
||
|
||
const server = http.createServer()
|
||
|
||
server.listen('3000', function () {
|
||
console.log('http://127.0.0.1:3000')
|
||
})
|
||
|
||
server.on('request', function (req, res) {
|
||
router(req, res)
|
||
})
|
||
```
|
||
|
||
### 路由模块
|
||
|
||
```js
|
||
// js/router.js
|
||
// 响应被拆分到响应模块
|
||
const handler = require('./handler')
|
||
|
||
module.exports = function (req, res) {
|
||
//获取请求方式
|
||
let method = req.method.toLowerCase()
|
||
//获取请求url
|
||
let url = req.url
|
||
|
||
//判断请求方式和url
|
||
|
||
//读取注册页面并返回
|
||
if (method == 'get' && url == '/register') {
|
||
handler.getRegisterPage(req, res, url)
|
||
}
|
||
//静态资源处理
|
||
else if (method == 'get' && url.indexOf('/css/') != -1 || url.indexOf('/js/') != -1 || url.indexOf('/images/') != -1) {
|
||
handler.getStaticSource(req, res, url)
|
||
}
|
||
//实现用户注册
|
||
else if (method == 'post' && url == '/register') {
|
||
handler.userRegister(req, res, url)
|
||
}
|
||
}
|
||
```
|
||
|
||
### 响应处理
|
||
|
||
```js
|
||
const fs = require('fs')
|
||
const path = require('path')
|
||
const mime = require('mime')
|
||
|
||
module.exports = {
|
||
//响应页面注册
|
||
getRegisterPage: function (req, res) {
|
||
fs.readFile(path.join(__dirname, '../views/register.html'), function (err, data) {
|
||
if (err) {
|
||
res.end('404')
|
||
throw err
|
||
} else {
|
||
res.end(data)
|
||
}
|
||
})
|
||
},
|
||
//响应静态资源
|
||
getStaticSource: function (req, res, url) {
|
||
fs.readFile(path.join(__dirname, '../' + url), function (err, data) {
|
||
if (err) {
|
||
res.end('404')
|
||
throw err
|
||
} else {
|
||
//根据文件类型不同设置响应头
|
||
res.setHeader('Content-Type', mime.getType(url))
|
||
res.end(data)
|
||
}
|
||
})
|
||
},
|
||
//实现用户注册
|
||
userRegister: function (req, res) {
|
||
//分批接收数据
|
||
let str = ''
|
||
req.on('data', (chunk) => {
|
||
str += chunk
|
||
})
|
||
req.on('end', () => {
|
||
console.log('str',str)
|
||
//调用自定义模块把接收的数据转成对象
|
||
let obj = JSON.parse(str)
|
||
console.log(obj)
|
||
|
||
//读取旧的数据
|
||
fs.readFile(path.join(__dirname, '../data/users.json'), 'utf-8', function (err, data) {
|
||
//设置响应头
|
||
res.setHeader('Content-type', 'text/html;charset=UTF-8')
|
||
if (err) {
|
||
let ret = {
|
||
code: 404,
|
||
msg: '注册失败'
|
||
}
|
||
res.end(JSON.stringify(ret))
|
||
throw err
|
||
} else {
|
||
let arr = JSON.parse(data)
|
||
console.log('arr',arr)
|
||
console.log('obj',obj)
|
||
//把数据加到旧数据
|
||
arr.push(obj)
|
||
fs.writeFile(path.join(__dirname, '../data/users.json'), JSON.stringify(arr, null, ' '), function (err, data) {
|
||
if (err) {
|
||
let ret = {
|
||
code: 404,
|
||
msg: '注册失败'
|
||
}
|
||
res.end(JSON.stringify(ret))
|
||
} else {
|
||
let ret = {
|
||
code: 200,
|
||
msg: '注册成功'
|
||
}
|
||
res.end(JSON.stringify(ret))
|
||
}
|
||
})
|
||
}
|
||
})
|
||
})
|
||
}
|
||
}
|
||
```
|
||
|
||
### 页面
|
||
|
||
```html
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<title>用户注册</title>
|
||
<link rel="stylesheet" href="../css/index.css" />
|
||
</head>
|
||
|
||
<body>
|
||
<div class="register">
|
||
<form id="myForm">
|
||
<ul>
|
||
<li>
|
||
<label for="">用户名</label>
|
||
<input type="text" name="username" class="name" />
|
||
</li>
|
||
<li>
|
||
<label for="">密码</label>
|
||
<input type="password" name="password" class="pass" />
|
||
</li>
|
||
<li>
|
||
<label for="">手机号</label>
|
||
<input type="text" name="phone" class="mobile" />
|
||
</li>
|
||
<li>
|
||
<label for=""></label>
|
||
<button type="submit" class="submit">立即注册</button>
|
||
</li>
|
||
</ul>
|
||
</form>
|
||
</div>
|
||
<script>
|
||
const form = document.querySelector('#myForm')
|
||
form.addEventListener('submit', e=>{
|
||
event.preventDefault()
|
||
const formData = new FormData(event.target)
|
||
const data = Object.fromEntries(formData.entries())
|
||
fetch('http://127.0.0.1:3000/register', {
|
||
method: 'post',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(data),
|
||
}).then(res=>{
|
||
console.log('res', res)
|
||
})
|
||
})
|
||
</script>
|
||
</body>
|
||
</html>
|
||
|
||
```
|
||
|
||
## 查看应用内存占用
|
||
|
||
Node.js提供了内置的 `v8` 模块,可以用来查看程序的内存占用情况。具体步骤如下:
|
||
|
||
```js
|
||
// 1. 在程序中引入v8模块
|
||
// 2. 手动触发垃圾回收
|
||
// 3. 获取内存占用信息
|
||
|
||
const v8 = require('v8')
|
||
|
||
v8.setFlagsFromString('--expose-gc')
|
||
global.gc()
|
||
|
||
const heap = v8.getHeapStatistics()
|
||
console.log(heap)
|
||
```
|
||
|
||
`getHeapStatistics()`方法返回一个包含有关V8堆内存使用情况的对象,包括总内存使用量,已分配内存量,垃圾回收次数等信息。通过这些信息,可以分析出程序的内存占用情况并进行优化。
|
||
|
||
注意:使用`v8`模块需要在启动Node.js时使用`--expose-gc`选项启用垃圾回收器的暴露功能。`node --expose-gc app.js`
|