0基础,带你快速入门Nodejs!

欢迎各位读者来阅读我的笔记,本笔记由B站视频:https://www.bilibili.com/video/BV1a34y167AZ 记录。

一、初识Node

1、浏览器中的JavaScript运行环境

2、什么是Node.js

官网地址:https://nodejs.org/zh-cn

学习路线

3、安装Node.js

其实官网直接下载就可以了
按照顺序直接安装即可

如果想看看是否安装成功,就直接打开终端输入以下命令:

node -v
弹出版本号就是安装成功了

4、使用Node运行JS代码

我们这次使用的是WebStorm来进行学习

代码敲完后可以直接用编译器来执行,也可以用终端执行:

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会帮你清除这个 .

➁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请求

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

我的是乱码,所以我们下部分要解决这个问题

➃解决中文乱码问题

当调用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 即可

➁优化资源请求路径

现在我们的需求是这样的,就是访问这个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 指向的对象:

意思就是当我们导入第一个 username 对象的时候。exports和module.exports 都是指向 username,但是当定义了 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);

➁高级做法

直接用第三方开发好的,使用包 moment 来进行操作

终端以下命令就开始下载包:

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);
})
GET什么东西 就收到什么

➄获取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);
})
直接把参数转换成参数 id
也可以叠多个参数:

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接口 ,我们到浏览器访问点击测试:

你会看见这个报错 Access-Control-Allow-Origin 跨域问题

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

➁使用 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 的请求分为两大类,分别是:

简单请求预检请求

➀简单请求

➁预检请求

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

服务器这边提供Delete的接口
这边定义一个DELETE接口
到浏览器测试就可以通过预检之后才能发起真正的请求(红色画圈部分就是预检

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)后面大部分是数据库实战项目的内容,这边直接跳过了。感谢各位读者的阅读!

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇