欢迎各位读者来阅读我的笔记,本笔记由B站视频:https://www.bilibili.com/video/BV1a34y167AZ 记录。
一、初识Node
1、浏览器中的JavaScript运行环境




2、什么是Node.js



3、安装Node.js


如果想看看是否安装成功,就直接打开终端输入以下命令:
node -v
4、使用Node运行JS代码

代码敲完后可以直接用编译器来执行,也可以用终端执行:
node hello.js
二、fs
1、读取文件内容


示例代码:
const fs = require('fs'); // 导入文件系统模块
// 异步读取文件内容
fs.readFile('./test.txt', 'utf8', function (err, data) {
console.log(err); // 打印错误信息 如果没有错误的话默认输出为 null
console.log(data); // 打印文件内容
})那我们从中可以知道如果说没有错误信息的话我们的输出内容是null,那我们可以直接通过if语法来判断是否有错误:
const fs = require('fs'); // 导入文件系统模块
// 异步读取文件内容
fs.readFile('./test.txt', 'utf8', function (err, data) {
if (err) {
console.error('读取文件出错:', err);
}
console.log('文件读取成功,内容是' + data);
})2、写入文件内容

直接看示例代码:
const fs = require('fs'); // 导入文件系统模块
// 异步写入文件内容
fs.writeFile('./src/node-js-study/fs-write.js', 'Hello Node.JS','utf8', function (err){
if (err) { // 如果有错误,打印错误信息
console.error(err);
} // 否则打印成功信息
console.log('文件写入成功');
});3、整理成绩的案例

主要的实现步骤:

示例代码:
const fs = require('fs');
fs.readFile('./practice-for-fs-use.js', 'utf8', (err, data) => {
if (err) {
console.error('读取文件出错:', err);
}
console.log('文件读取成功,内容是' + data);
// 先把成绩的数据,按照空格进行分割
const arrOld = data.split(' ');
// 循环分割后的数组,对每一项数据,进行字符串的替换操作
const arrNew = []
arrOld.forEach((item) => {
arrNew.push(item.replace('=', ','));
})
// 把新数组中的每一项进行合并,得到一个新的字符串
const newData = arrNew.join('\r\n'); // 注意这里使用了换行符进行分割
console.log(newData);
// 调用 fs write 写入文件
fs.writeFile('./practice-for-fs-use.js', newData, (err) => {
if (err) {
console.error('写入文件出错:', err);
}
console.log('文件写入成功');
})
})4、处理路经问题




示例代码:
fs.readFile(__dirname + '/files/1.txt', 'utf8', function (err, data) {
if (err) return console.log(err);
console.log('读取文件内容是:' + data);
})5、使用path模块处理路径
➀path.join()


示例代码:
const path = require('path'); // 导入路径处理模块
const pathStr = path.join('/a', '/b/c', '../', 'd', 'e'); // 作用:拼接路径
console.log(pathStr); // 输出: \a\b\d\e注意:今后凡是涉及到路径拼接的操作,都要使用 path.join() 方法进行处理。不要直接使用+进行字符串的拼接。
fs.readFile(path.join(__dirname, 'files', '1.txt'), 'utf8', function (err, data) {
if (err) return console.log(err);
console.log('读取文件内容是:' + data);
})这样就可以了,那为什么说不推荐用+来进行拼接呢?

➁path.basename()

示例代码:
const fpath = '/a/b/c/index.html';
const nameWithoutExit = path.basename(fpath, '.html'); // 作用:获取文件名称(不带扩展名)
console.log(nameWithoutExit);➂path.extname()

示例代码:
const fpath1 = "/a/b/c/index.html";
const fext = path.extname(fpath1);
console.log(fext); // 输出: .html三、时钟案例
1、分析需求&读取文件内容
➀fs模块处理style和script文件

主要实现的就是:


示例代码:
// 导入 fs 文件系统模块
const fs = require('fs');
// 导入 path 模块,处理路径问题
const path = require('path');
// 读取 clock.html 文件内容 匹配<style></style> 和 <script></script> 标签中的内容
const regStyle = /<style>[\s\S]*<\/style>/;
const regScript = /<script>[\s\S]*<\/script>/;➁使用fs 模块读取需要被处理的 html 文件

示例代码:
// 使用fs.readFile方法读取文件内容
fs.readFile(path.join(__dirname, 'clock.html'), 'utf8', function (err, dataStr) {
if (err) {
return console.log('读取文件失败!' + err.message);
}
// 读取文件成功后,调用对应的三个方法,分别拆解出 css、js、html 内容
}2、处理JS CSS HTML
➀定义resolveCSS方法
示例代码:
// 导入 fs 文件系统模块
const fs = require('fs');
// 导入 path 模块,处理路径问题
const path = require('path');
// 读取 clock.html 文件内容 匹配<style></style> 和 <script></script> 标签中的内容
const regStyle = /<style>[\s\S]*<\/style>/;
const regScript = /<script>[\s\S]*<\/script>/;
// 使用fs.readFile方法读取文件内容
fs.readFile(path.join(__dirname, 'clock.html'), 'utf8', function (err, dataStr) {
if (err) {
return console.log('读取文件失败!' + err.message);
}
// 读取文件成功后,调用对应的三个方法,分别拆解出 css、js、html 内容
resolveCSS(dataStr);
resolveJS(dataStr);
resolveHTML(dataStr);
});
function resolveCSS(dataStr) {
// 使用正则表达式提取样式内容
const r1 = regStyle.exec(dataStr);
// 将提取到的样式字符串进行处理
const newCSS = r1[0].replace('<style>', '').replace('</style>', '');
// 将处理后的样式内容写入到 clock.css 文件中
fs.writeFile(path.join(__dirname, 'clock.css'), newCSS, function (err) {
if (err) {
return console.log('写入 CSS 样式失败!' + err.message);
}
console.log('写入 CSS 样式成功!');
});
}➁定义resolveJS方法
示例代码:
// 定义处理js脚本的方法
function resolveJS(dataStr) {
// 使用正则表达式提取脚本内容
const r2 = regScript.exec(dataStr);
// 将提取到的脚本字符串进行处理
const newJS = r2[0].replace('<script>', '').replace('</script>', '');
// 将处理后的脚本内容写入到 clock.js 文件中
fs.writeFile(path.join(__dirname, 'clock.js'), newJS, function (err) {
if (err) {
return console.log('写入 JS 脚本失败!' + err.message);
}
console.log('写入 JS 脚本成功!');
});
}➂定义resolveHTML方法
示例代码:
// 定义处理html结构的方法
function resolveHTML(dataStr) {
// 将原始的 html 内容中的样式和脚本标签及其内容替换为空字符串
const newHTML = dataStr.replace(regStyle, '<link rel="stylesheet" herf="./index.css" />').replace(regScript, '<script src="./index.js"></script>');
// 将处理后的 HTML 内容写入到 clock.html 文件中
fs.writeFile(path.join(__dirname, 'clock.html'), newHTML, function (err) {
if (err) {
return console.log('写入 HTML 结构失败!' + err.message);
}
console.log('写入 HTML 结构成功!');
});
}3、时钟案例的两个注意点

四、HTTP
1、理解HTTP模块的概念和作用

服务器和普通电脑的区别在于,服务器上安装了 web 服务器软件,例如:IIS、Apache 等。通过安装这些服务器软件,就能把一台普通的电脑变成一台 web 服务器。
正常的话我们正常架构一个服务器,只需要利用 phpStudy等软件就可以直接创建一个web服务器了
在 Nodejs 中,我们不需要使用IIS、Apache 等这些第三方 web 服务器软件。因为我们可以基于 Nodejs 提供的http模块,通过几行简单的代码,就能轻松的手写一个服务器软件,从而对小提供web 眼务。
2、服务器相关的概念
这边的话内容会少很多,因为我很熟了,特别熟悉了。所以说这边就只会放截图,希望大伙们了解!



3、创建最基本的Web服务器
➀创建Web服务器的基本步骤
基本步骤:

示例代码:
// 1、导入HTTP模块
const http = require('http');
// 2、创建服务器实例
const server = http.createServer()
// 3.为服务器实例绑定request事件,监听客户端的请求
server.on('request', function (req, res) {
console.log('有人访问了网站')
})
// 4.启动服务器,监听指定端口号
server.listen(8080, function () {
console.log('服务器启动成功,可以通过http://127.0.0.1:8080')
})➁req请求对象

示例代码:
const http = request('http')
// 创建服务器实例
const server = http.createServer()
// req是请求对象,包含了客户端的请求信息
server.on('request', (req) => {
const url = req.url
// req.method 是客户的请求的
const method = req.method
console.log('客户端请求的URL是:' + url)
console.log('客户端请求的方法是:' + method)
})
server.listen(8080, () => {
console.log('服务器启动成功,可以通过http://127.0.0.1:8080')
})➂res响应对象
在服务器的 request 事件处理函数中,如果想访问与服务器相关的数据或属性。


这样的话我们访问网站就有页面显示了


➃解决中文乱码问题
当调用res.end()方法,向客户端发送中文内容的时候,会出现乱码问题,此时,需要手动设買内容的编码格式:
const http = require('http');
const server = http.createServer()
server.on('request', (req, res) => {
// 设置响应头,解决中文乱码问题
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end('你好,世界!');
})
server.listen(8080, () => {
console.log('服务器启动成功,可以通过http://127.0.0.1:8080');
})这一个部分就是设置编码格式为 UTF-8 这样就不会有乱码的问题:
res.setHeader('Content-Type', 'text/html; charset=utf-8');
4、根据不同的URL响应不同的HTML内容
核心的实践步骤:

示例代码:
const http = require('http');
const server = http.createServer()
server.on('request', (req, res) => {
// 获取客户端请求的URL地址
const url = req.url;
// 设置默认的响应内容为 404 页面
let content = '<h1>404 Not Found</h1>';
// 根据不同的URL地址响应不同的内容
if (url === '/' || url === '/home') {
content = '<h1>欢迎来到首页</h1>';
} else if (url === '/about') {
content = '<h1>关于我们</h1>';
}
// 设置响应头,解决中文乱码问题
res.setHeader('Content-Type', 'text/html; charset=utf-8');
// 将内容响应给客户端
res.end(content);
})
server.listen(8080, () => {
console.log('服务器启动成功,可以通过http://127.0.0.1:8080');
})5、时钟WEB服务器案例
➀请求访问
我们现在要做的是把我们前面拆分好的时钟服务器在Nodejs中应用起来:

实现步骤:

示例代码:
// 导入三个模块
const http = require('http');
const fs = require('fs');
const path = require('path');
// 创建HTTP服务器
const server = http.createServer()
// 监听客户端请求
server.on('request', (req, res) => {
// 获取到客户的请求的URL地址
const url = req.url;
// 把请求的URL地址映射为具体的文件路径
const fpath = path.join(__dirname, url);
// 根据映射过来的文件路径读取文件的内容
fs.readFile(fpath, 'utf8', (err, data) => {
// 读取文件失败,可能是文件不存在
if (err) return res.end('<h1>404 Not Found</h1>');
// 读取文件成功,将文件内容响应给客户端
res.end(data);
})
})
// 启动服务器
server.listen(8080, () => {
console.log('服务器启动成功,可以通过http://127.0.0.1:8080');
})访问地址:http://127.0.0.1:8080/clock/index.html 即可

➁优化资源请求路径

示例代码:
// 导入三个模块
const http = require('http');
const fs = require('fs');
const path = require('path');
// 创建HTTP服务器
const server = http.createServer()
// 监听客户端请求
server.on('request', (req, res) => {
// 获取到客户的请求的URL地址
const url = req.url;
// 把请求的URL地址映射为具体的文件路径
// const fpath = path.join(__dirname, url);
let fpath = '';
if (url === '/'){
fpath = path.join(__dirname, './clock/index.html');
}else{
fpath = path.join(__dirname, './clock', url);
}
// 根据映射过来的文件路径读取文件的内容
fs.readFile(fpath, 'utf8', (err, data) => {
// 读取文件失败,可能是文件不存在
if (err) return res.end('<h1>404 Not Found</h1>');
// 读取文件成功,将文件内容响应给客户端
res.end(data);
})
})
// 启动服务器
server.listen(8080, () => {
console.log('服务器启动成功,可以通过http://127.0.0.1:8080');
})五、模块化
1、模块化的概念
模块化是指解决一个舞杂问题时,自顶向下逐层把系统划分成若干模块的过程。对于整个系统来说,模块是可组合、分解和更换的单元。

模块化规范:

2、模块的分类&require的使用

示例代码:
const m1 = require('./导入模块测试');
console.log(m1);这是导入自定义模块

3、模块作用域和module对象
➀模块作用域
什么叫做模块作用域?:


➁module对象

打印内容:
{
id: '.',
path: '/Users/node-js-study',
exports: {},
filename: '/Users/node-js-study/模块化-演示module对象.js',
loaded: false,
children: [],
paths: [
'/Users/node-js-study/node_modules',
'/Users/node_modules',
'/Users/node_modules',
'/Users/node_modules',
'/node_modules'
],
Symbol(kIsMainSymbol): true,
Symbol(kIsCachedByESMLoader): false,
Symbol(kURL): undefined,
Symbol(kFormat): undefined,
Symbol(kIsExecuting): true
}4、module.exports对象的使用
在自定义模块中,可以使用 module.exports对象,将模块内的成员共享出去,供外界使用。
外界用 require() 方法导入自定义模块时,得到的就是 module.exports 所指向的则象。
示例代码:
// 向 module.exports 添加属性和方法
module.exports.username = '张三';
// 向 module.exports 添加一个方法
module.exports.sayHello = function () {
console.log('Hello 模块化');
}引用代码:
const m1 = require('./导入模块测试');
console.log(m1);
注意:使用 require() 方法导入模块时,导入的结果,永远以 module.exports 指向的对象为准。
module.exports = {
username: '张三',
sayHello() {
console.log('Hello 模块化');
}}两种写法的关键区别(简要):
1、给 module.exports 添加属性(例如 module.exports.prop = …)是在原有导出对象上增加字段,不改变 module.exports 的引用,适合累加多个导出。
2、直接 module.exports = { … } 是用新对象替换导出,会覆盖之前通过赋值添加的字段。
exports 只是 module.exports 的一个引用,不能用 exports = {} 来替代导出(那会断开引用);用 exports.prop = … 等价于 module.exports.prop = …。
5、exports对象
由于 module.exports单词比较复杂,为了简化同外共享的代码,Node 提供了exports对效。其默认情况下, exports 和 module.exports 指向同一个对象。最终共享的结果,还是以 module.exports 指向的对象为准。
// 模块化-exports对象.js
const username = 'zs'
// 向 exports 对象添加属性和方法
exports.username = username;
// 向 exports 对象添加一个属性
exports.age = 20
// 向 exports 对象添加一个方法
exports.sayHello = function () {
console.log('Hello exports 对象');
}最后,向外共享的结果,永远都是 module.exports 所指向的对象
直接写exports和写module.exports 都是一样的结果
6、exports和module.exports的使用误区
➀第一个误区
时刻谨记,require() 模块时,得到的永远是 module.exports 指向的对象:

➁第二个误区
就是反过来:

➂第三个误区

➃第四个误区

注意:为了防止混乱,建议大家不要在同一个模块中同时使用 exports 和 module.exports
7、CommonJS模块化规范

六、包与npm
1、包的概念
什么是包?

包的来源:

为什么需要包?

从哪里下载包?

点击跳转:http://npmjs.com/
注意:是到npmjs.com 来找到自己的包,然后到 registry.npmjs.org 来下载自己需要的包
如何下载:

执行以下命令:
npm -v
2、格式化时间的两种做法
➀传统的做法

示例代码:
function dateFormat(dtStr){
const dt = new Date(dtStr);
const y = dt.getFullYear();
const m = padZero(dt.getMonth()+1);
const d = padZero(dt.getDate());
const hh = padZero(dt.getHours());
const mm = padZero(dt.getMinutes());
const ss = padZero(dt.getSeconds());
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
}
// 定义时间格式化函数的第二种方法
function padZero(n){
return n > 9 ? n : '0' + n;
}
module.exports = {
dateFormat
}导包:
const TIME = require('./包与npm-格式化时间的两种做法')
// 使用TIME包中的dateFormat方法格式化当前时间
const dt = new Date();
const dtStr = TIME.dateFormat(dt);
console.log(dtStr);➁高级做法


终端以下命令就开始下载包:
npm i moment
这边的话刚好可以看 moment 的官方文档:https://momentjs.com/docs/
示例代码:
const moment = require('moment');
// 获取当前时间
const now = new Date();
// 使用 moment 格式化时间
const formattedTime = moment(now).format('YYYY-MM-DD HH:mm:ss');
console.log('当前时间格式化后为:', formattedTime);3、使用npm的其他注意点

安装指定版本的包:

npm i [email protected]包的语义化版本规范:

4、包管理配置文件

在多人协同合作的时候我们会遇到这个问题:


注意:今后在项目开发中,一定要把 node_modules 文件夹,添加到.gitignore 忽略文件中。
那如何快速创建 package.json 呢?

// 在执行命令的目录里生成package.json
npm init -y注意:
① 上述命令只能在英文的目录下成功运行!所以,项目文件夹的名称一定要使用英文命名,不要使用中文,不能出现空格。
② 运行 npm install 命令安装包的时候,npm 包管理工具会自动把包的名称和版本号,记录到 package.json 中。

接下来的话我们讲解下 dependencies 选项,当你下载依赖包的时候会出现在这里:


接下来为大家讲解:如何一次性安装所有的包


那如何卸载包呢?

devDependencies节点:

5、解决包下载慢的问题


那如何切换淘宝NPM镜像服务器呢:
查看当前下载的源
npm config get registry切换到淘宝的下载源:
npm config set registry=https://registry.npm.taobao.org/检查镜像是否下载成功:
npm config get registry当然的话也有一个小工具可以快速的帮我们切换下载源:

首先下载 nrm:
npm i nrm -g
查看所有的镜像源:
nrm ls
更换nrm服务器镜像源:
nrm use taobao
6、包的分类&规范的包结构
➀包的分类

那什么叫做全局包呢?

全局安装指定的包:
npm i 包名 -g卸载全局安装的包:
npm uninstall 包名 -g注意:只有工具性质的包,才有全局安装的必要性。因为它们提供了好用的终端命令。
判断某个包是否需要全局安装后才能使用,可以参考官方提供的使用说明即可。

➁i5ting_toc
i5ting_toc 是一个可以把 md 文档转为 html 页面的小工具,使用步骤如下:
将i5ting_toc 安装成全局包:
npm install -g i5ting_toc调用 i5ting_toc,轻松实现 md 转 html 的功能
i5ting_toc -f 要转换的md文件路径 -o➂规范的包结构

七、发布包
1、初始化基础和包结构
从这边到话我们就要开始学习如何制作属于自己的包,需要完成什么功能呢?

初始化包的基本结构:

初始化 package.json


2、格式化时间&main属性
后期的内容我们直接跳过了,因为发布包到npm 我个人认为不需要,也没必要。所以马上各位自己观看了,视频目录P32-P36
3、模块的加载机制




目录作为模块

八、express

1、认识express并创建基本的web服务器
什么叫做Express?

进一步理解 Express

那么 Express能做什么呢?

安装命令:
npm i express创建最基本的服务器:
// Description: 使用express创建最基本的服务器
// 导入express模块
const express = require('express');
// 创建express服务器实例
const app = express();
// 监听客户端的GET和POST请求,并向客户端响应具体的内容
app.listen(80, () => {
console.log('Express服务器启动成功,监听80端口');
});2、监听GET和POST请求&处理参数
➀监听 GET 请求
通过 app.get() 方法,可以监听客户端的 GET 请求,具体的语法格式如下:

➁监听POST请求
通过 app.post() 方法,可以监听客户端的 POST 请求,具体的语法格式如下:

➂把内容响应给客户端
通过 res.send() 方法,可以把处理好的内容,发送给客户端:

示例代码:
// 创建路由规则
app.get('/user', (req, res) => {
res.send({name: 'zs', age:20, gender: '男'}); // 响应一个JSON对象
})
app.post('/user', (req, res) => {
res.send('请求成功'); // 响应一个字符串
})➃获取URL中携带的查询参数
通过req.query 对象,可以访问到客户端通过查询字符申的形式,发送到服务器的参数:

示例代码:
app.get('/', (req, res) => {
// 通过 req.query 可以获取到客户端发送过来的 查询参数
// 注意:默认情况下,req.query 是一个空对象
console.log(req.query);
res.send(req.query);
})
➄获取URL中的动态参数
通过 req.params 对象,可以访问到 URL 中,通过:匹配到的动态参数:

示例代码:
// :id 是动态参数,表示可以匹配任意值,并且可以通过 req.params.id 获取到这个值
app.get('/user/:id', (req, res) => {
// 通过 req.params 可以获取到动态参数,默认情况下,req.params 是一个空对象
console.log(reg.params)
res.send(req.params);
})


3、静态资源处理
express 提供了一个非常好用的函数,叫做 express.static(),通过它,我们可以非常方便地创建一个静态资源服务器,例如,通过如下代码就可以将 public 目录下的图片、CSS 文件、JavaScript 文件对外开放访问了:
app.use(express.static('public'))
注意:Express 在指定的静态目录中查找文件,井对外提供资源您访问路径。因此,存放静态文件的目录名不会出现在 URL中。
示例代码:
const express = require('express');
const app = express();
// 在这里 调用 express.static() 方法,快速对外提供静态资源
app.use(express.static('./public'));
// 启动服务器
app.listen(8080, () => {
console.log('Express server listening on 8080');
});注意:./public 要更改成自己要开放的目录

挂载路径前缀:

示例代码:
app.use('/abc', express.static('./files'));相当于将 file 文件中的文件挂载到路径 /abc 了,和Linux挂载磁盘同一个道理
4、安装并使用nodemon
什么是nodemon?为什么需要它?

在终端中,运行如下命令,即可将 nodemon 安装为全局可用的工具:
npm install -g nodemon
那如何使用 nodemon呢?

# 以前要用这个命令启动项目
node app.js
# 现在换成这个命令就可以实现自动重启项目的效果:
nodemon app.js
九、路由
1、初始express中的路由
什么是路由?看看现实生活的路由:

在这里,路由是按键与服务之间的映射关系,那Express 中的路由呢?


路由匹配的过程:

那如何使用路由呢?在Express 中使用路由最简单的方式,就是把路由挂發到 app上,示例代码如下:
const express = require('express');
const app = express();
// 定义一个简单的路由
app.get('/', (req, res) => {
res.send('Hello, Express!');
});
app.post('/', (req, res) => {
res.send('收到一个POST请求');
})
// 让服务器监听指定端口
const PORT = 80;
app.listen(PORT, () => {
console.log(`服务器已启动,访问地址:http://localhost:${PORT}`);
});但是的话我们会很少使用路由挂载到APP上,因为会导致文件体积的增大
2、路由的模块化
模块化路由

示例代码:
// 描述: 这是一个使用 Express 框架定义路由的模块化示例。
const express = require('express');
// 创建路由对象
const router = express.Router();
// 定义路由规则
router.get('/user', (req, res) => {
res.send('获取用户信息');
});
router.post('/user', (req, res) => {
res.send('创建新用户');
});
module.exports = router;那接下来就是注册路由模块了
const express = require('express');
const app = express();
// 导入路由模块
const router = require('./routes/router');
// 注册路由模块
app.use(router);
app.listen(80, () => {
console.log('服务器启动成功,监听80端口');
})为路由模块添加前缀:


直接在注册路由模块的时候添加前缀即可,这样每个人访问挂载路由的时候就是 /api/xxxx

十、中间件
1、中间件的概念与格式
中间件(Middleware),特指业务流程的中间处理环节。举一下生活中的例子:


注意:中间件函数的形参列表中,必须包含 next 参数。而路由处理函数中只包含 req和 res
接下来给大家讲解下 next函数的作用:

2、全局生效的中间件
➀最简单的中间件函数
可以通过如下的方式,定义一个最简单的中间件函数:
const express = require('express');
const app = express();
// 定义一个简单的中间件函数
const = mw = function (req, res, next) {
console.log('这是一个最简单的中间件函数');
// 把流转关系,转交给下一个中间件或路由
next();
}
app.listen(8080, () => {
console.log('服务器启动成功,可以通过http://127.0.0.1:8080');
})➁全局生效的中间件

// 将mw注册为全局生效的中间件
app.use(mw)
app.get('/', (req, res) => {
res.send('Home page.');
})
app.get('/user', (req, res) => {
res.send('User page.');
})➂定义全局中间件的简化形式
app.use(function (req, res, next) {
console.log('这是第二个全局中间件');
next();
})➃中间件的作用

只要写在中间件里 然后每个路由都能够使用
➄定义多个全局中间件
可以使用 app.use() 连续定义多个全局中间件。客户端请求到达服务嚣之后,会按照中间件定义的先后顺序依次进行调用,示例代码如下:
app.use(function (req, res, next) {
console.log('这是第二个全局中间件');
next();
})
// 第三个全局中间件
app.use((req, res, next) => {
console.log('这是第三个全局中间件');
next();
})3、局部生效的中间件
不使用 app.use() 定义的中间件,叫做局部生效的中间件,示例代码如下:
const express = require('express');
const app = express();
const mw1 = (req, res, next) => {
console.log('这是第一个局部生效的中间件');
next();
}
// 2. 创建路由
app.get('/user', mw1, (req, res) => {
res.send('User 页面');
})
app.get('/home', (req, res) => {
res.send('Home 页面');
})
app.listen(8080, () => {
console.log('服务器已启动,访问地址:http://localhost:8080');
})那如何定义多个局部中间件?可以在路由中,通过如下两种等价的方式,使用多个局部中间件:
const mw1 = (req, res, next) => {
console.log('这是第一个局部生效的中间件');
next();
}
const mw2 = (req, res, next) => {
console.log('这是第二个局部生效的中间件');
next();
}
// 2. 创建路由
app.get('/user', mw1,mw2, (req, res) => {
res.send('User 页面');
})4、中间件的5个注意事项

5、中间件的分类

应用级别的中间件:

路由级别的中间件:

错误级别的中间件:

注意:错误级别的中间件,必须注册在所有路由之后!
Express内置的中间件:

第三方的中间件:

注意:Express 内置的 express.urlencoded 中间件,就是基于 body-parser 这个第三方中间件进一步封装出来的。
十一、编写接口
1、创建最基本的服务器&创建API路由模块
首先先回顾下如何创建最基本的服务器:

那我们接下来就创建API路由模块:
const express = require('express');
const app = express();
const router = require('./编写接口-APIRouter');
app.use('/api', router); // 把路由模块,注册到 app 上
app.listen(8080, () => {
console.log('服务器已启动,访问地址:http://localhost:8080');
});APIRouter.js:
const express = require('express');
const router = express.Router();
// 在这里挂载对应的路由
module.exports = router; // 暴露出来2、编写GET接口
示例代码:
const express = require('express');
const router = express.Router();
// 在这里挂载对应的路由
router.get('/get', (req, res) => {
const query = req.query;
res.send({
status: 0, // 0 表示成功,1 表示失败
msg: 'GET 请求成功!',
data: query
});
})
module.exports = router; // 暴露出来
同时的话 query 也能接受传参
3、编写POST接口
const express = require('express');
const router = express.Router();
router.get('/', function (req, res) {
// 通过 req.query 获取请求体中包含 url-encoded 格式的数据
const query = req.query;
// 调用 res.send() 方法,向客户端响应一个 JSON 格式的数据
res.send({
status: 0, // 0 表示成功,1 表示失败
msg: 'GET 请求成功!',
data: query
});
})
module.exports = router; // 暴露出来另外的话我们还需要额外添加一个中间件:
// 配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }));4、基于cors解决接口跨域问题
➀接口跨域问题
刚才编写的 GET和 POST接口,存在一个很严重的问题:不支持跨域请求。


我们现在这添加了POST和GET接口 ,我们到浏览器访问点击测试:

那咋办啊?怎么解决这个问题?

➁使用 cors 中间件解决跨域问题

示例代码:
// 一定要在路由之前,配置 cors 这个中间件,从而解决接口跨域的问题
const cors = require('cors');
app.use(cors());那说到这里了,我都还没给你们说什么是 cors?

有关CORS的注意事项:

十二、跨域
1、cors相关的三个响应头
➀Access-Control-Allow-Origin


➁Access-Control-Allow-Headers

➂Access-Control-Allow-Methods

2、cors的简单请求与预检请求
客户端在请求 CORS 接口时,根据请求方式和请求头的不同,可以将 CORS 的请求分为两大类,分别是:
简单请求和预检请求
➀简单请求

➁预检请求

➂简单请求和预检请求的区别



3、编写jsonp接口
我们先回顾一下JSONP:

在开始之前我们先讲解下创建JSONP接口的注意事项:

示例代码:
// 必须在配置 cors 中间件之前,配置 JSONP 的接口
app.get('/api/jsonp', (req, res) => {
// 定义 JSONP 接口的具体实现
});
实现 JSONP 接口的步骤:

实现代码:
// 必须在配置 cors 中间件之前,配置 JSONP 的接口
app.get('/api/jsonp', (req, res) => {
// 定义 JSONP 接口的具体实现
// 1.得到函数的名称
const funcName = req.query.callback;
// 2.定义要发送到客户端的数据对象
const data = { name: 'zs', age: 22 };
// 3.拼接出一个函数调用的字符串
const scriptStr = `${funcName}(${JSON.stringify(data)})`;
// 4.把函数调用的字符串,响应给客户端的<script>标签进行解析执行
res.send(scriptStr);
});在网页中使用 jQuery 发起 JSONP 请求:
调用$.ajax()函数,提供JSONP 的配置选项,从而发起JSONP 请求,示例代码如下:

十三、结语
我们的笔记就到此记录结束了(结束章节P56)后面大部分是数据库和实战项目的内容,这边直接跳过了。感谢各位读者的阅读!