Node-js


title: Node.js
date: 2022-06-30 21:26:11
tags:

  • Node.js
    categories:
  • Node.js

什么是Node.js

Node.js是一个基于Chrome V8引擎的JavaScript运行环境

Node.js中的JavaScript运行环境

Node-js_第1张图片

注意:

  1. 浏览器是JavaScript的前端运行环境
  2. Node.js是JavaScript的后端运行环境
  3. Node.js中无法调用DOM和BOM等浏览器内置API

Node.js框架

Node.js作为-个JavaScript的运行环境,仅仅提供了基础的功能和API。然而,基于Node.js提供的这些基础能,很多强大

的工具和框架如雨后春笋,层出不穷,所以学会了Node.jis ,可以让前端程序员胜任更多的工作和岗位:

①基于Express框架(http://www.expressjs.com.cn/) ,可以快速构建Web应用

②基于Electron框架(https://electronjs.org/) , 可以构建跨平台的桌面应用

③基于restify框架http://restify.com/) ,可以快速构建API接口项目

④读写和操作数据库、创建实用的命令行工具辅助前端开发、etc…

关于终端

转为开发人员设计,用于实现人机交互的一种方式。

  • win+r 打开运行输入cmd打开终端
  • 在文件路径中输入cmd打开终端
  • 按住shift+右键有一个powershell终端

常用的终端命令:

命令 作用
node -v 查看node.js版本
cd / 进入根目录
cd … 去到上一层目录
f: 进入F盘
cls 清空终端

终端快捷键:

  1. 使用↑键可以快速定位到上一次执行的命令
  2. 使用tab键能够快速补全路径
  3. 使用esc键能够快速清空当前输入的命令

fs文件系统模块

fs模块是Node.js官方提供的,用来操作文件的模块,它提供了一系列的方法和属性。用来满足用户对文件的操作需求。

例如:

  • **fs.readFile( )**方法,用来读取指定文件中的内容
  • **fs.writeFile( )**方法,用来向指定文件中写入内容

如果在javaScript代码中,使用fs模块来操作文件,则需要使用如下方式导入他:

img

fs.readFile()的语法格式 读取文件内容

使用fs.readFile()方法,可以读取指定文件中的内容,语法格式如下:

png

参数解读:

  • 参数1:必选参数,字符串,表示文件的路径。
  • 参数2:可选参数,表示以什么编码格式来读取文件。
  • 参数3:必选参数,文件读取完成后,通过回调函数拿到读取的结果。

示例代码:

// 导入fs模块  来操作文件
const fs = require('fs');
// 调用fs.readFile()方法读取文件

// 参数1:读取文件的存放路径
// 参数2:读取文件时候采用的编码格式 一般默认指定utf8
// 参数3:回调函数,拿到读取失败和成功的结果 err dataStr
fs.readFile('./files/11.txt', 'utf8', function(err, dataStr) {
    // 2.1 打印失败的结果
    //如果读取成功,则err的值为null
    //如果读取失败,则err的值为错误对象 dataStr的值为undefined
    console.log(err);
    console.log('----------');
    // 2.2 打印成功的结果
    console.log(dataStr);
});

成功:

png

失败:

Node-js_第2张图片

优化写法:

Node-js_第3张图片

fs.writeFile( )的语法格式 写入文件内容

使用fs.writeFile()方法,可以向指定文件中写入内容。语法格式如下:

png

参数解读:

  • 参数1:必选参数,指定一个文件路径的字符串,表示文件的存放路径。
  • 参数2:必选参数,表示要写入的内容。
  • 参数3:可选参数,表示以什么格式写入文件内容,默认utf8。
  • 参数4:必选参数,文件写入完成后的回调函数。

代码示例:

// 导入fs文件系统模块
const fs = require('fs');

// 调用fs.writeFile()方法,写入文件内容

//参数1:表示文件的存放路径
//参数2:表示要写入的内容
//参数3:省略
//参数4:回调函数

fs.writeFile('f:/files/2.txt', 'abc', function(err) {
    // 2.1如果文件写入成功,则err的值等于null
    // 2.2如果文件写入失败,则err的值等于一个错误对象
    console.log(err);
});

优化写法:

Node-js_第4张图片

__dirname解决路径拼接错误

有时候出现路径拼接错误是因为提供了./或者…/开头的相对路径导致

解决方法:

  • 提供一个完整路径
  • 但是移植性差,不利用代码维护
  • 使用__dirname
  • __dirname代表的是当前目录路径
fs.readFile(__dirname + '/files/2.txt', 'utf8', function(err, dataStr) {
    if (err) {
        return console.log('读取失败' + err.message);
    }
    console.log('读取成功' + dataStr);
});

path路径模块

path模块是Node.js官方提供的,用来处理路径的模块.它提供了一系列的方法和属性,用来满足用户对路径的处理需求.

例如:

  • path.join()方法,用来将多个路径片段拼接成一个完整的路径字符串
  • path.basename()方法,用来从路径字符串中,将文件名解析出来

如果要在JavaScript代码中使用path模块来处理路径,则需要使用如下方式导入他:

const path = require('path');

path.join()的语法格式

使用path.join()方法,可以把多个路径片段拼接为完整的路径字符串,语法格式如下:

path.join([...paths])

参数解读:

  • …paths< string >路径片段的序列
  • 返回值:< string >

代码示例:

Node-js_第5张图片

path.basename()的语法格式

使用path.basename()方法,可以获取路径中的最后一部分,经常通过这个方法获取路径中的文件名,语法格式如下:

path.basename(path[,ext])

参数解读:

  • path必选参数,表示一个路径的字符串
  • ext可选参数,表示文件扩展名
  • 返回:表示路径中的最后一部分

示例代码:

Node-js_第6张图片

path.extname()代码示例

使用path.extname()方法,可以获取路径中的扩展名部分

Node-js_第7张图片

http模块

什么是客户端?什么是服务器?

  1. 在网络节点中,负责消耗资源的电脑,叫做客户端
  2. 负责对外提供网络资源的电脑,叫做服务器

http模块是Node.js官方提供的,用来创建web服务器的模块,通过http模块提供的http.createServer()方法,就能方便把一台普通的电脑,变成一台web服务器,从而对外提供web资源服务。

如果希望使用http模块创建Web服务器,则需要导入他:

const http = require('http');

进一步理解http模块的作用

服务器和普通电脑的区别在于,服务器上安装了web服务器插件。例如:IIS,Apache等。通过安装这些服务器软件,就能把一台普通电脑变成一台web服务器。

在node.js中我们不需要使用IIS、Apache等这些第三方web服务器软件,因为我们可以基于Node.js提供的http模块,通过几行代码就能轻松手写一个服务器软件,从而对外提供web服务。

服务器相关概念

1.IP地址

IP地址就是互联网上每台计算机的唯-地址,因此IP地址具有唯一性。 如果把“个人电脑”比作"-台电话”,那么"IP地址"就相当于“电话号码”,只有在知道对方IP地址的前提下,才能与对应的电脑之间进行数据通信。

IP地址的格式:通常用”点分十进制”表示成(a.b.c.d) 的形式,其中, a,b,c,d 都是0~255之间的十进制整数。例如:用点分十进表示的IP地址(192.168.1.1)

注意:

  1. 互联网中每台Web服务器,都有自己的IP地址,例如:大家可以在Windows的终端中运行ping www baidu.com命令,即可查看到百度服务器的IP地址。
  2. 在开发期间,自己的电脑既是一台服务器, 也是-个客户端,为了方便测试,可以在自己的浏览器中输入127.0.0.1 这个IP地址,就能把自己的电脑当做一台服务 器进行访问了.

2.域名和域名服务器

尽管IP地址能够唯-地标记网络上的计算机,但IP地址是一长串数字,不直观,而且不便于记忆,于是人们又发明了另一套字符型的地址方案,即所谓的域名(Domain Name)地址

IP地址和域名是一对应的关系, 这份对应关系存放在一种叫做域名服务器(DNS, Domain name server)的电脑中。使用者只需通过好记的域名访问对应的服务器即可,对应的转换工作由域名服务器实现。因此,域名服务器就是提供IP地址和域名之间的转换服务的服务器。

注意:

  1. 单纯使用IP地址,互联网中的电脑也能够正常工作。但是有了域名的加持,能让互联网的世界变得更加方便。
  2. 在开发测试期间,127.0.0.1 对应的域名是localhost,它们都代表我们自己的这台电脑,在使用效果上没有任何区别。

3.端口号

计算机中的端口号,就好像是现实生活中的门牌号一样。 通过门牌号,外卖小哥可以在整栋大楼众多的房间中,准确把外卖送到你的手中。
同样的道理,在一台电脑中,可以运行成百上千个web服务。每个web服务都对应-个唯的端口号. 客户端发送过来的网络请求,通过端口号,可以被准确地交给对应的web服务进行处理。

Node-js_第8张图片

注意:

  1. 每个端口号不能同时被多个web服务器占用
  2. 在实际运用中URL中的80端口可以被省略

创建最基本的web服务器

创建web服务器的基本步骤

  1. 导入http模块
  2. 创建web服务器实例
  3. 为服务器实例绑定require事件,监听客户端的请求
  4. 启动服务器

步骤1-导入http模块

如果希望在自己电脑上创建一个web服务器,从而对外提供web服务,则需要导入http模块:

const http = require('http');

步骤2-创建web服务器实例

调用**http.sreateServer()**方法,即可快速创建一个web服务器实例:

const server = http.createServer();

步骤3-为服务器实例绑定request事件

为服务器实例绑定request事件,即可监听客户端发送过来的网络请求:

// 使用服务器实例的.on()方法,为服务器绑定一个request事件
sever.on('request',(req,res)=>{
    // 只要有客户端来请求我们的服务器,就会触发request事件,从而调用这个事件处理函数
    console.log('Someone visit our web server');
});

req请求对象

只要服务器接收了客户端的请求,就会调用server.on()为服务器绑定的request事件处理函数。如果想在事件处理函数中,访问与客户端相关的数据或者属性,就可以使用如下方法:

server.on('request',(req)=>{
    // req 是请求对象,它包含了与客户端相关的数据和属性,例如:
    // req.url 是客户端请求的url地址
    // req.method 是客户端的method请求类型
    const str = `Your request url is ${req.url},and request method is ${req.method}`;
    console.log(str);
});

res响应对象

在服务器的request使事件处理函数中,如果想访问与服务器相关的数据属性,可以使用如下方式:

server.on('request', (req, res) => {
    //req是请求对象,包含了与客户端相关的数据和属性
    // req.url是客户端请求的url地址
    const url = req.url;
    // req.method是客户端请求的method类型
    const method = req.method
    const str = `Your request url is ${url},and request method is ${method}`;
    console.log(str);
    // 调用res.end()方法向客户端响应一些内容
    res.end(str);
});

解决中文乱码问题

当调用res.end()方法,向客户端发送中文内容时候,就会出现乱码问题,此时,需要手动设置内容的编码格式:

res.setHeader('Content-Type', 'text/html;charset=utf-8');

server.on('request', (req, res) => {
    // 发送内容包含中文
    const str = `你请求的url地址是${req.url},请求的method类型是${req.method}`;
    // 调用res.setHeader('Content-Type', 'text/html;charset=utf-8')方法
    res.setHeader('Content-Type', 'text/html;charset=utf-8');
    // 调用res.end()方法向客户端响应一些内容
    res.end(str);
});

步骤4-启动服务器

调用服务器实例的.listen()方法,即可启动当前的web服务器实例:

// 调用server.listen(端口号,callback回调函数)方法,即可启动web服务器
server.listen(80,()=>{
    console.log('http server running at http//127.0.0.1');
});

根据不同的url相应不同的html内容

核心实现步骤:

  1. 获取请求的url地址
  2. 设置默认的响应内容404 Not found
  3. 判断用户请求的是否为**//index.html**首页
  4. 判断用户请求的是否为**/about.html**关于页面
  5. 设置Content-Type响应头,防止中文乱码
  6. 使用**res.end( )**把内容响应给客户端
const http = require('http');

const server = http.createServer();

server.on('request', (req, res) => {

    // 1. 获取请求的url地址
    const url = req.url;
    // 2. 设置默认的响应内容为404 Not found
    let content = `

404 Not Found

`
; // 3. 判断用户请求的是否为/或/index.html首页 // 4. 判断用户请求的是否为/about.html关于页面 if (url === '/' || url === '/index.html') { content = `

首页

`
; } else if (url === '/about.html') { content = `

关于页面

`
; } // 5. 设置Content-Type响应头,防止中文乱码 res.setHeader('Content-Type', 'text/html; charset=utf-8'); // 6. 使用res.end( )把内容响应给客户端 res.end(content); }); // 4. 启动服务器 server.listen(8080, function() { console.log('server running at http://127.0.0.1:8080'); });

模块化

什么是模块化?

模块化是指解决一个复杂问题时,自顶向下逐层把系统划分为若干模块的过程。对整个系统来说,模块是可以组合,分解,更换的单元

编程领域中的模块化,就是遵守固定的规则,把一个大文件拆分成独立并相互依赖的多个小模块:

把代码进行模块化拆分的好处:

  1. 提高了代码的复用性
  2. 提高了代码的可维护性
  3. 可以实现按需加载

模块化规范

模块化规范就是对代码进行模块化的拆分与组合时,需要遵循的那些规则。

例如:

  • 使用什么样的语法格式来引入模块
  • 在模块中使用什么样的语法格式向外暴露成员

模块化的好处:大家都遵循同样的模块化规范写代码,降低了沟通的成本,极大方便了各个模块之间的相互调用,利人利己。

Node.js中模块的分类

Node.js中根据模块来源的不同,将模块分为了3大类,分别是:

  • 内置模块(内置模块是Node.js官方提供的,例如:fs,path,http等)
  • 自定义模块(用户创建的每个js文件,都是自定义模块)
  • 第三方模块(由第三方开发出来的模块,并非官方提供的内置模块,也不用用户创建的自定义模块。使用前要先下载)

加载模块

使用强大的require()方法,可以加在需要的内置模块,用户自定义模块,第三方模块进行使用。例如:

Node-js_第9张图片

**注意:**使用require()方法加载其他模块时候,会执行被加载模块中的代码。

Node.js中的模块作用域

1.什么是模块作用域

和函数作用域类似,在自定义模块中定义的变量,方法。等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块作用域

2.模块作用域的好处

防止全局变量污染的问题

当两个js文件中有相同的变量,就会造成全局变量污染

3.向外共享模块作用域中的成员

1.module对象

在每个.js自定义模块中都有一个module对象,它里面存储了和当前模块有关的信息,打印如下:

Node-js_第10张图片

2.module.exports对象

在自定义模块中。可以使用module.exports对象,将模块内的成员共享出去,供外界使用。

外界用**require( )**方法导入自定义模块时候。得到的就是module.exports所指向对象。

如果想要公开自定义模块中的一部分,可以直接为module.exports添加属性或者方法

3.共享成员时的注意点

使用require()方法导入模块时,导入的结果,永远以module.exports指向的对象为准。

Node-js_第11张图片

4.exports对象

由于module.exports单词写起来比较复杂,为了简化向外共享成员的代码,Node 提供了exports 对象。默认情况下,exports 和module.exports指向同一个对象。最终共享的结果,还是以module.exports指向的对象为准。

Node-js_第12张图片

注意:

最终共享的结果,还是以module.exports指向的对象为准。

如果module.exports这个老六偷偷指向了另一个对象,则以它为准

5.exports和module.exports的使用误区

时刻谨记,require()模块时,得到的永远是module exports指向的对象:

Node-js_第13张图片

Node.js中的模块化规范

Node.js遵循了CommonJs模块化规范。CommonJS规定了模块的特性和各模块之间相互依赖。

CommonJS规定:

  1. 每个模块内部,module变量代表当前模块
  2. module变量是一个对象,它的exports属性,(即module.exports)是对外的接口
  3. 加载某个模块,其实就是加载该模块的module.exports属性,require()方法用于加载模块

npm与包

什么是包

Node.js中的第三方模块又叫做包。

就像电脑和计算机指的是相同的东西。第三方模块和包指的是同一个概念。只不过叫法不同

包的来源

不同于Node.js中的内置模块与自定义模块,包是由第三方个人或团队开发出来的,免费供所有人使用。

注意:Node.js中的包都是免费开源的,不需要付费即可免费下载使用。

为什么需要包

由于Node.js的内置模块仅提供了一些底层的API,导致在基于内置模块进行项目开发的时候,效率低。包是基于内置模块封装出来的,提供了更高级,更方便的API,极大的提高了开发效率。

包和内置模块之间的关系,类似于jQuery浏览器API之间的关系。

从哪里下载包

国外有家IT公司,叫做npm, Inc.这家公司旗下有一个非常著名的网站: https://www.npmjs.com/ .它是全球最大的包共享平台,你可以从这个网站上搜索到任何你需要的包,只要你有足够的耐心!
到目前位置,全球约1100多万的开发人员,通过这个包共享平台,开发并共享了超过120多万个包供我们使用。
npm, Inc. 公司提供了一个地址为https://registy.npmjs.org/的服务器,来对外共享所有的包,我们可以从这个服务器上下载自己所需要的包。

注意:

  • 从 https://www.npmjs.com/网站上搜索自己所需要的包
  • 从https://registy.npmjs.org服务器上下载自己需要的包

如何下载包

npm, Inc.公司提供了-一个包管理工具,我们可以使用这个包管理工具,从https://registry.npmis org/服务器把需要的包下载到本地使用。
这个包管理”工具的名字叫做Node Package Manager (简称npm包管理工具),这个包管理工具随着Node.js的安装包-起被安装到了用户的电脑上。
大家可以在终端中执行npm-v命令,来查看自己电脑上所安装的npm包管理工具的版本号:

Node-js_第14张图片

npm初体验

格式化时间高级做法

  1. 使用npm包管理工具,在项目中安装格式化时间的包moment ,使用npm install moment或者npm i moment命令
  2. 使用require( )导入格式化时间的包
  3. 参考moment的官方API文档对时间进行格式化

Node-js_第15张图片

初次安装后多出的文件

初次装包完成后,在项目文件夹下多-个叫做 node_ modules的文件夹和package-lock.json的配置文件。

其中:

  • node_modules文件夹用来存放所有已安装到项目中的包。require()导入第三方包时,就是从这个目录中查找并加载包。
  • package-lock.json配置文件用来记录node. _modules目录下的每一个包的下载信息, 例如包的名字、版本号、下载地址等。

注意:程序员不要手动修改node_ modules 或package-lock.json文件中的任何代码,npm包管理工具会自动维护它们。

安装指定版本的包

默认情况下,使用npm install命令安装包的时候,会自动安装最新版本的包。如果需要安装指定版本的包,可以在包名之后,通过@符号指定具体的版本,例如:

image-20220702105945239

包的语义化版本规范

包的版本号是以”点分十进制”形式进行定义的,总共有三位数字,例如 2.24.0
其中每一位数字所代表的的含义如下:

  • 第1位数字:大版本

  • 第2位数字:功能版本

  • 第3位数字: Bug修复版本

版本号提升的规则:只要前面的版本号增长了,则后面的版本号归零。

包管理配置文件

npm规定,在项目根目录中,必须提供-个叫做 package.json的包管理配置文件。用来记录与项目有关的一一些配置信息。例如:

  • 项目的名称、版本号、描述等
  • 项目中都用到了哪些包
  • 哪些包只在开发期间会用到
  • 那些包在开发和部署时都需要用到
dependencies节点

package.json文件中,有一个dependencies节点,专门用来记录您使用npm install命令安装了哪些包。

快速生成package.json

npm包管理工具提供了一个快捷命令,可以在执行命令时所处的目录中,快捷创建package.json这个包管理配置文件:

Node-js_第16张图片

注意:

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

git合作开发遇到的问题

在实际上开发中,上传至git的前提下会自动屏蔽掉node_modules文件中的所有第三方包。通过.gitignore配置文件忽略掉该文件。避免项目文件过大。

而在拿到项目的时候,可以通过npm install或者npm i指令安装所有你需要的第三方包。

卸载包

可以运行npm uninstall命令,来卸载指定的包:

Node-js_第17张图片

注意:npm uninstall 命令执行后,会把卸载的包,自动从package.json的dependencies中移除掉

devDependencies节点

如果某些包之在项目开发中会用到,在项目上线后不会用到,则建议把这些包记录到devDependencies节点中。与之对应的,如果某些包在开发和项目上线之后都需要用到,则建议把这些包记录到dependencies节点中。

使用如下命令:npm i 包名 -D

Node-js_第18张图片

淘宝NPM镜像服务器

淘宝在国内搭建了-一个服务器,专门把国外官方服务器上的包同步到国内的服务器,然后在国内提供下包的服务。从而极大的提高了下包的速度。

Node-js_第19张图片

扩展:

镜像是一种文件存储形式。一个磁盘上的数据在另一个磁盘上存在完全相同的副本即为镜像

切换npm的下包镜像源

下包的镜像源,指的是下包的服务器地址

  • npm config get registry 查看当前下包镜像源
  • npm config set registry=https://registry.npm.taobao.org/将下包的镜像源切换为淘宝镜像源

Node-js_第20张图片

nrm

为了更方便的切换下包的镜像源,我们可以安装nrm这个小工具,利用nrm提供的终端命令,可以快速查看和切换下包的镜像源。

  • npm i nrm -g通过npm包管理器,将nrm安装为全局可用的工具
  • nrm ls查看所有可用镜像源
  • nrm use taobao将下包的镜像源切换为淘宝镜像

Node-js_第21张图片

包的分类

项目包

那些被安装到项目的node _modules目录中的包,都是项目包。

项目包又分为两类,分别是;

  • 开发依赖包(被记录到devDependencies节点中的包,只在开发期间会用到)
  • 核心依赖包(被记录到dependencies节点中的包,在开发期间和项目.上线之后都会用到)

Node-js_第22张图片

全局包

在执行npm install 命令时,如果提供了-g 参数,则会把包安装为全局包。
全局包会被安装到C:\Users\用户目录\AppData\Roaming\npm\node_ modules 目录下。

Node-js_第23张图片

注意:

  1. 只有工具性质的包,才有全局安装的必要性,因为它们提供了好用的终端命令。
  2. 判断某个包是否需要全局安装后再能使用,可以参考官方提供的使用说明即可。

i5ting_toc

iting_toc是一个可以把md文档转为html页面的小工具,使用步骤如下:

Node-js_第24张图片

规范包结构

在清楚了包的概念以及如何下载和使用包之后,接下来,我们深入了解一下包的内部结构。

一个规范的包,它的组成结构,必须符合以下3点要求:

  1. 包必须以单独的目录而存在
  2. 包的顶级目录下要必须包含package.json 这个包管理配置文件
  3. package.json 中必须包含name, version, main这三个属性,分别代表包的名字版本号、包的入口。

注意:以上3点要求是一个规范的包结构必须遵守的格式,关于更多的约束,可以参考网站

发布包

在符合规范包结构的前提下就可以自己制作一个包并发布到npm网站。

关于包的规范

关于package.json文件

  • name 发布包时的包名字,不可以与他人重复
  • version 包版本
  • mian 入口文件
  • description 被检索时所展示的描述
  • keywords 被检索的关键字
  • license 开源协议

Node-js_第25张图片

遵循模块化规范

入口文件写入到index.js文件下,方法文件放入src文件中

Node-js_第26张图片

关于入口文件index.js

  1. dateNew/escape是对象
  2. 需要挂载到module.exports
  3. 使用...运算符展开这两个对象
  4. 让他们的所有属性交给新的对象module.exports

Node-js_第27张图片

发布包

在npm官网注册好账号后在终端登录账户

使用指令npm login

依次输入用户名 - 密码 - 邮箱

注意:镜像服务器必须是npm官方地址

删除包

运行npm unpublish 包名 --force命令,即可从npm删除已发布的包。

注意:

  1. npm unpublish命令只能删除72小时以内发布的包
  2. npm unpublish删除的包,在24小时内不允许重复发布
  3. 发布包的时候要慎重,尽量不要往npm上发布没有意义的包!

Express

Express简介

什么是Express

官方给出的概念: Express 是基于Node.js平台,快速、开放、极简的Web开发框架。
通俗的理解: Express 的作用和Node.js内置的http模块类似,是专门用来创建Web服务器的。
Express的本质:就是一个npm上的第三方包,提供了快速创建Web服务器的便捷方法。

Express的中文官网:. http://www.expressjs.com.cn/

EXpress基本使用

Express安装

在项目所处目录中,使用npm install [email protected]安装指定版本或者npm install express --save安装最新版本

创建基本的web服务器

  1. 导入express模块
  2. 创建web服务器
  3. 启动web服务器
// 1.导入express
const express = require('express');
// 2.创建web服务器
const app = express();
// 3.启动web服务器
app.listen(80, () => {
    console.log("express server running at http://127.0.0.1");
})

监听GET请求

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

  • 参数1:客户端请求的URL地址
  • 参数2:请求对应的处理函数
    • ​ req:请求对象(包含了与请求相关的属性和方法)
    • ​ res:响应对象(包含了与响应相关的属性与方法)
app.get('请求URL',function(req,res){/*处理函数*/});

监听POST请求

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

  • 参数1:客户端请求的URL地址
  • 参数2:请求对应的处理函数
    • ​ req:请求对象(包含了与请求相关的属性和方法)
    • ​ res:响应对象(包含了与响应相关的属性与方法)
app.post('请求URL',function(req,res){/*处理函数*/});

把内容响应给客户端

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

app.get('/user',(req,res)=>{
    // 向客户端发送JSON请求
    res.send({
        name:'zs',
        age:20,
        gender:'男'
    })
})

app.post('/user',(req,res)=>{
    // 向客户端发送文本请求
    res.send('请求成功');
})

获取URL中携带的查询参数

通过req.query对象,可以访问到客户端通过查询字符串的形式,发送到服务器的参数:

Node-js_第28张图片

获取URL中的动态参数

通过req.params对象,可以访问到URL中,通过:匹配到的动态参数:

:id并非固定写法,":"才是,id是可变的

// url地址中,可以通过:参数名的形式,匹配动态参数值
// 注意:这里的:id是一个动态的参数

app.get('/user/:id', (req, res) => {
    // req.params 是动态匹配到的URL参数,默认也是一个空对象
    console.log(req.params);
    
    // 里面存放着通过:动态匹配到的参数值
    res.send(req.params);
})

托管静态资源

1.express.static()

express提供了一个非常好用的函数,叫做express.static(),通过它,我们可以非常方便地创建一个静态资源服务器。
例如,通过如下代码就可以将public 目录下的图片、CSS 文件、JavaScript 文件对外开放访问了:

app.use(express.static('public'))

注意: Express 在指定的静态目录中查找文件,并对外提供资源的访问路径。
因此,存放静态文件的目录名不会出现在URL中。

2.托管多个静态目录

当有两个文件都需要开放自己创建静态服务器,可以反复调用express.static()方法

Node-js_第29张图片

访问静态资源文件时,express.static() 函数会根据目录的添加顺序查找所需的文件。优先级总是第一个

3.挂载路径前缀

如果希望在托管的静态资源访问路径之前,挂载路径前缀,则可以使用如下的方式:

image-20220704201812592

现在,你就可以通过带有 /public 前缀地址来访问 public 目录中的文件了:

  • http://localhost:3000/public/images/kitten.jpg
  • http://localhost:3000/publicss/style.css
  • http://localhost:3000/public/js/appjis

nodemon

为什么使用nodemon

在编写调试Node.js项目的时候,如果修改了项目的代码,则需要频繁的手动close掉,然后再重新启动,非常繁琐。
现在,我们可以使用noderaon (https://www.npmjs.com/package/nodemon) 这个工具, 它能够监听项目文件的变动,当代码被修改后,nodemon 会自动帮我们重启项目,极大方便了开发和调试。

使用指令npm i -g nodemon下载

使用nodemon

通过nodemon 'js文件'来启动服务器,当更改代码后,就不需要再重启服务器,nodemon会自动重启服务器。

Express路由

路由的概念

现实生活中的路由

Node-js_第30张图片

在这里,路由是按键与服务之间的映射关系

Express中的路由

在Express中,路由指的是客户端的请求与服务器处理函数之间的映射关系。
Express中的路由分3部分组成,分别是请求的**类型、请求的URL地址、处理函数,**格式如下:

app.METHOD(PATH,HANDLER)

Express中路由的例子

//匹配GET请求,且请求URL为 / 
app.get('/',function(req,res){
    res.send('Hello World!');
})

//匹配POST请求,且请求URL为 /
app.post('/',function(req,res){
    res.send('Got a POST requset');
})

路由的匹配过程

每当-一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数。
在匹配时,会按照路由的顺序进行四配,如果请求类型和请求的URL同时匹配成功,则Express会将这次请求,转交给对应的function函数进行处理。

Node-js_第31张图片

路由匹配的注意点:
①按照定义的先后顺序进行匹配
②请求类型和请求的URL同时匹配成功,才会调用对应的处理函数

路由的使用

1.简单的用法

在Express中使用路由最简单的方式,就是把路由挂载到app上,示例代码如下:

Node-js_第32张图片

2.模块化路由

为了方便对路由进行模块化的管理,Express 不建议将路由直接挂载到app.上,而是推荐将路由抽离为单独的模块。
将路由抽离为单独模块的步骤如下:

  1. 创建路由模块对应的js文件
  2. 调用express.Router( )函数创建路由对象
  3. 向路由对象上挂载具体的路由
  4. 使用module.exports向外共享路由对象
  5. 使用app.use( )函数注册路由模块

2.1创建路由模块

Node-js_第33张图片

Node-js_第34张图片

2.2注册路由模块

Node-js_第35张图片

注意:**app.use( )**函数的作用。就是用来注册全局中间件

2.3为路由模块添加前缀

类似于托管静态资源时,为静态资源统一挂载访问前缀一样,路由模块添加前缀的方式也非常简单:

Node-js_第36张图片

Express中间件

现实中的例子

在处理污水的时候,一般都要经过三个处理环节, 从而保证处理过后的废水,达到排放标准。

Node-js_第37张图片

处理污水的三个中间环节,就可以叫做中间件。

中间件的调用流程

当一个请求到达Express 的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。

Node-js_第38张图片

中间件的格式

Express的中间件,本质上就是一个function处理函数,Express 中间件的格式如下:

Node-js_第39张图片

注意:中间件函数的形参列表中,**必须包含next参数。**而路由处理函数中只包含req和res.

next函数的作用

next函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。

Node-js_第40张图片

使用Express中间件

定义中间件函数

可以通过如下的方式,定义一个最简单的中间件函数:

const express = require('express');

const app = express();

// 定义一个简单的中间件函数
const mw = function(req, res, next) {
    console.log('这是一个简单的中间件函数');
    // 把流转关系转交给下一个中间件或路由
    next();
}

app.listen(80, () => {
    console.log('http://127.0.0.1');
})

全局生效的中间件

客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件。
通过调用app.use(mw);即可定义一个全局生效的中间件,示例代码如下:

const express = require('express');

const app = express();

//------------------------------------------------------------------
// 定义一个简单的中间件函数
const mw = function(req, res, next) {
    console.log('这是一个简单的中间件函数');
    // 把流转关系转交给下一个中间件或路由
    next();
};
// 将mw注册为全局生效的中间件
app.use(mw);
//------------------------------------------------------------------
app.get('/', (req, res) => {
    console.log('调用了/路由');
    res.send('Home page');
});
app.get('/user', (req, res) => {
    console.log('调用了/user 路由');
    res.send('User page');
})

app.listen(80, () => {
    console.log('http://127.0.0.1');
})

简化合并写法:

Node-js_第41张图片

定义多个全局中间件

可以使用app.use0连续定义多个全局中间件。客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用,示例代码如下:

const express = require('express');

const app = express();

// 定义第一个全局中间件
app.use((req, res, next) => {
    console.log('调用了第一个全局中间件');
    next();
});
// 定义第二个全局中间件
app.use((req, res, next) => {
    console.log('调用了第二个全局中间件');
    next();
});

app.get('/', (req, res) => {
    res.send('Home page!');
});

app.listen(80, () => {
    console.log('http://127.0.0.1');
})

局部生效的中间件

不使用**app.use()**定义的中间件,叫做局部生效的中间件,示例代码如下:

const express = require('express');

const app = express();

// 定义中间件函数
const mw1 = (req, res, next) => {
    console.log('调用了第一个全局中间件');
    next();
};
// 创建路由 将参数添加在路由中
app.get('/', mw1, (req, res) => {
    res.send('Home page!');
});
app.get('/user', (req, res) => {
    res.send('User page!');
});
// 启动服务器
app.listen(80, () => {
    console.log('http://127.0.0.1');
});

定义多个局部生效的中间件:

可以在路由中,通过如下两种等价的方式,使用多个局部中间件:

Node-js_第42张图片

中间件的作用

多个中间件之间,共享同- -份req和res.基于这样的特性,我们可以在上游的中间件中,统-为req或res对象添加自定义的属性或方法,供下游的中间件或路由进行使用。

Node-js_第43张图片

中间件注意事项

  1. 一定要在路由之前注册中间件(除了错误级别中间件)
  2. 客户端发送过来的请求,可以连续调用多个中间件进行处理
  3. 执行完中间件的业务代码之后,不要忘记调用next(函数
  4. 为了防止代码逻辑混乱,调用next(函数后不要再写额外的代码
  5. 连续调用多个中间件时,多个中间件之间,共享req和res对象

中间件分类

为了方便大家理解和记忆中间件的使用,Express 官方把常见的中间件用法,分成了5大类,分别是:

  1. 应用级别的中间件
  2. 路由级别的中间件
  3. 错误级别的中间件
  4. Express 内置的中间件
  5. 第三方的中间件

应用级别中间件

Node-js_第44张图片

路由级别中间件

绑定到express.Router0实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别。只不过,应用级别中间件是绑定到app实例上,路由级别中间件绑定到router实例上,代码示例如下:

Node-js_第45张图片

错误级别中间件

错误级别中间件的作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
格式:错误级别中间件的function 处理函数中,必须有4个形参,形参顺序从前到后,分别是(err, req, res, next)。

Node-js_第46张图片

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

Express内置中间件

自Express 4.16.0版本开始,Express 内置了3个常用的中间件,极大的提高了Express项目的开发效率和体验:
express.static 快速托管静态资源的内置中间件,例如: HTML文件、 图片、CSS 样式等(无兼容性)
express.json 解析JSON格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)
express.urlencoded 解析URL-encoded格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)

express.json实例:

const express = require('express');

const app = express();

// 配置中间件
// 通过express.json()解析表单中的json格式数据
app.use(express.json());

// 创建路由
app.post('/user', (req, res) => {
    // 在服务器中可以使用req.body这个属性来接受客户端发送过来的请求体
    // 默认情况下,如果不配置 解析表单数据的中间件,默认为undefined
    console.log(req.body);
    res.send('ok');
})

// 启动服务器
app.listen(80, () => {
    console.log('http://127.0.0.1');
});

express.urlencoded实例:

express.urlencoded({ extended: false }) 固定写法

const express = require('express');

const app = express();

// 配置中间件

// 通过express.urlencoded()解析表单中的url-encoded格式的数据
app.use(express.urlencoded({ extended: false }));

// 创建路由
app.post('/book', function(req, res) {
    // 在服务器中可以使用req.body这个属性来获取json格式的表单和url-encoded格式数据
    console.log(req.body);
    res.send('ok');
})

// 启动服务器
app.listen(80, () => {
    console.log('http://127.0.0.1');
});

第三方的中间件

非Express官方内置的,而是由第三方开发出来的中间,叫做第三方中间件,在项目中,大家可以按需下载并配置第三方中间件,从而提高项目的开发效率。

例如:在[email protected]之前的版本中,经常使用body-parser这个第三方中间件,来解析请求体数据,使用步骤如下:

  1. 运行npm install body-parser安装中间件
  2. 使用require导入中间件
  3. 使用app.use()注册并且使用中间件

使用Express写接口

1.创建基本的服务器

const express = require('express');

const app = express();

// ........

app.listen(80, () => {
    console.log('http://127.0.0.1');
})

2.创建API路由模块

首先写一个模块化路由并暴露出去

// 模块化路由
const express = require('express');

const router = express.Router();

// 挂载对应的路由


// 向外暴露路由
module.exports = routers;

导入模块化路由并将路由模块挂载到中间件:

const express = require('express');

const app = express();

// 导入路由模块
const router = require('./14.APIrouter');
// 把路由模块注册到app上(中间件)
app.use('/api', router);



app.listen(80, () => {
    console.log('http://127.0.0.1');
})

3.编写GET接口

在路由模块 挂载对应的路由:

// 模块化路由模块
const express = require('express');

const routers = express.Router();

// 挂载对应的路由
routers.get('/get', (req, res) => {
    // 通过req.query获取客户端通过查询字符串 发送到服务器的数据
    const query = req.query;
    // 调用res.send()方法,向客户端相应处理的数据
    res.send({
        status: 0, // 0处理成功,1处理失败
        msg: 'GET 请求成功!', // 状态的描述
        data: query //需要响应给客户端的数据
    });
})

// 向外暴露路由
module.exports = routers;

4.编写POST请求

使用post请求需要配置解析表单数据的中间件:

const express = require('express');

const app = express();


// 配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }));


// 导入路由模块
const routers = require('./14.APIrouter');
// 把路由模块注册到app上(中间件) 并添加前缀'/api'
app.use('/api', routers);


app.listen(80, () => {
    console.log('http://127.0.0.1');
})

在路由模块编写POST接口:

// 模块化路由模块
const express = require('express');

const routers = express.Router();

// 挂载对应的路由
// get接口
routers.get('/get', (req, res) => {
    // 通过req.query获取客户端通过查询字符串 发送到服务器的数据
    const query = req.query;
    // 调用res.send()方法,向客户端相应处理的数据
    res.send({
        status: 0, // 0处理成功,1处理失败
        msg: 'GET 请求成功!', // 状态的描述
        data: query //需要响应给客户端的数据
    });
});
// post接口
routers.post('/post', (req, res) => {
    //通过req.body获取请求体中包含的url-encoded格式的数据
    const body = req.body;
    // 调用res.send()方法,向客户端响应结果
    res.send({
        status: 0,
        msg: 'POST 请求成功!',
        data: body
    });
});
// 向外暴露路由
module.exports = routers;

CORS跨域资源共享

1.接口的跨域问题

刚才编写的GET和POST接口,存在一个严重问题:不支持跨域请求

解决接口跨域问题的方案主要有两种:

  1. CORS(主流解决方案,推荐)
  2. JSONP(有缺陷的解决方案,只支持GET请求)

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

cors是express的一个第三方中间件。通过安装和配置cors中间件。可以很方便的解决跨域问题,使用步骤如下:

  1. 运行npm install cors安装中间件
  2. 使用const cors = require('cors')导入中间件
  3. 在路由之前调用app.use(cors())配置中间件
const express = require('express');
const app = express();
// 配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }));

//-----------------------------------------------------------
// 一定要在路由之前,配置cors这个中间件,从而解决接口的跨域问题
const cors = require('cors');
app.use(cors());
//-----------------------------------------------------------

// 导入路由模块
const routers = require('./14.APIrouter');
// 把路由模块注册到app上(中间件) 并添加前缀'/api'
app.use('/api', routers);
app.listen(80, () => {
    console.log('http://127.0.0.1');
})

3.什么是cors

CORS (Cross-Origin Resource Sharing,跨域资源共享)由-系列HTTP响应头组成,这些HTTP响应头决定浏览器是否阻止前端JS代码跨域获取资源。

浏览器的网源安全策略默认会阻止网页“跨域"获取资源。但如果接口服务器配熏了CORS相关的HTTP响应头,就可以解除浏览器端的跨域访问限制,

4.cors注意事项

  1. cors只要在服务器端进行配置,客户端浏览器无需做任何额外的配置,即可请求开启了cors的接口。
  2. cors在浏览器中有兼容性,只有支持XMLHttpPequest Level2的浏览器,才能正常访问开启了CORS的服务端接口(例如:IE10,Chrome4+,FireFox3.5+)

5.CORS响应头 - Access-Control-Allow-Origin

响应头部中可以携带一个Access-Control-Allow-Origin字段,其语法如下;

Access-Control-Allow-Origin:|*

其中,origin参数的值制定了允许访问该资源的外域URL

例如:下面的字段值将只允许来自http://itcast.cn的请求:

res.setHeader('Access-Control-Allow-Origin','http://itcast.cn');

如果指定了Access-Control-Allow-Origin字段的值为通配符*,表示允许来自任何域的请求。示例代码如下:

res.setHeader('Access-Control-Allow-Origin',*);

6.CORS响应头部- Access-Control-Allow-Headers

默认情况下,CORS仅支持客户端向服务器发送如下的9个请求头:
Accept、 Accept-L anguage、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、 Width 、Content-Type (值仅限于 text/plain. multipart/form-data、 application/x-www-form-urlencoded 三者之一)
如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过Access-Control-Allow-Headers对额外的请求头进行声明,否则这次请求会失败!

res.setHeader('Access-Control-Allow-Headers','Content-Type,x-Custom-Header')

7.CORS响应头部- Access-Control-Allow-Methods

默认情况下,CORS仅支持客户端发起GET、POST、HEAD请求。
如果客户端希望通过PUT、DELETE 等方式请求服务器的资源,则需要在服务器端,通过Access-Control-Alow-Methods来指明实际请求所允许使用的HTTP方法。
示例代码如下:

// 只允许 POST GET DELETE HEAD 请求方式
res.setHeader('Access-Control-Allow-Methods','POST,GET,DELETE,HEAD');
// 允许所有http请求
res.setHeader('Access-Control-Allow-Methods',*);

8.CORS请求的分类

客户端在请求CORS接口时,根据请求方式和请求头的不同。可以将CORS的请求分为两大类,分别是:

  1. 简单请求
  2. 预检请求
A.简单请求

同时满足以下两大类的请求,就属于简单请求:

  1. 请求方式:GET POST HEAD 三者之一
  2. HTTP头部信息不超过以下几种字段:无自定义头部字段。Accept. Accept-Language. Content-Language. DPR.Downlink. Save-Data. Viewport-Width. Width . Content-Type (只有三个值applic ation/x-www-form-urlencoded. multipart/form- data. text/plain)
B.预检请求

只要符合以下任意一个条件的请求。都需要进行预检请求:

  1. 请求方式为GET、POST、HEAD之外的请求Method类型
  2. 请求头中包含自定义头部字段
  3. 向服务器发送了application/json 格式的数据
两者区别

简单请求的特点:客户端与服务器之间只会发生一次请求。
预检请求的特点:客户端与服务器之间会发生两次请求,OPTION预检请求成功之后,才会发起真正的请求。

JSONP接口

1.回顾jsonp的概念与特点

概念:浏览器端通过

你可能感兴趣的:(javascript,前端,开发语言)