什么是Node.js
Node.js是一个基于Chrome V8引擎的JavaScript运行环境
Node.js中的JavaScript运行环境
注意:
- 浏览器是JavaScript的前端运行环境
- Node.js是JavaScript的后端运行环境
- Node.js中无法调用DOM和BOM等浏览器内置API
Node.js框架
Node.js作为-个JavaScript的运行环境,仅仅提供了基础的功能和API。然而,基于Node.js提供的这些基础能,很多强大
的工具和框架如雨后春笋,层出不穷,所以学会了Node.js ,可以让前端程序员胜任更多的工作和岗位:
①基于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 | 清空终端 |
mkdir 文件名 | 新建文件夹 |
终端快捷键:
- 使用↑键可以快速定位到上一次执行的命令
- 使用tab键能够快速补全路径
- 使用esc键能够快速清空当前输入的命令
fs文件系统模块
fs模块是Node.js官方提供的,用来操作文件的模块,它提供了一系列的方法和属性。用来满足用户对文件的操作需求。
例如:
- **fs.readFile( )**方法,用来读取指定文件中的内容
- **fs.writeFile( )**方法,用来向指定文件中写入内容
如果在javaScript代码中,使用fs模块来操作文件,则需要使用如下方式导入他:
fs.readFile()的语法格式 读取文件内容
使用fs.readFile()方法,可以读取指定文件中的内容,语法格式如下:
参数解读:
- 参数1:必选参数,字符串,表示文件的路径。
- 参数2:可选参数,表示以什么编码格式来读取文件。
- 参数3:必选参数,文件读取完成后,通过回调函数拿到读取的结果。
示例代码:
1 | // 导入fs模块 来操作文件 |
成功:
失败:
优化写法:
fs.writeFile( )的语法格式 写入文件内容
使用fs.writeFile()方法,可以向指定文件中写入内容。语法格式如下:
参数解读:
- 参数1:必选参数,指定一个文件路径的字符串,表示文件的存放路径。
- 参数2:必选参数,表示要写入的内容。
- 参数3:可选参数,表示以什么格式写入文件内容,默认utf8。
- 参数4:必选参数,文件写入完成后的回调函数。
代码示例:
1 | // 导入fs文件系统模块 |
优化写法:
__dirname解决路径拼接错误
有时候出现路径拼接错误是因为提供了./或者../开头的相对路径导致
解决方法:
- 提供一个完整路径
- 但是移植性差,不利用代码维护
- 使用__dirname
- __dirname代表的是当前目录路径
1 | fs.readFile(__dirname + '/files/2.txt', 'utf8', function(err, dataStr) { |
path路径模块
path模块是Node.js官方提供的,用来处理路径的模块.它提供了一系列的方法和属性,用来满足用户对路径的处理需求.
例如:
- path.join()方法,用来将多个路径片段拼接成一个完整的路径字符串
- path.basename()方法,用来从路径字符串中,将文件名解析出来
如果要在JavaScript代码中使用path模块来处理路径,则需要使用如下方式导入他:
1 | const path = require('path'); |
path.join()的语法格式
使用path.join()方法,可以把多个路径片段拼接为完整的路径字符串,语法格式如下:
1 | path.join([...paths]) |
参数解读:
- ..paths< string >路径片段的序列
- 返回值:< string >
代码示例:
path.basename()的语法格式
使用path.basename()方法,可以获取路径中的最后一部分,经常通过这个方法获取路径中的文件名,语法格式如下:
1 | path.basename(path[,ext]) |
参数解读:
- path
<string>
必选参数,表示一个路径的字符串- ext
<string>
可选参数,表示文件扩展名- 返回:
<string>
表示路径中的最后一部分
示例代码:
path.extname()代码示例
使用path.extname()方法,可以获取路径中的扩展名部分
path.resolve(‘./a.js’)
可以将相对路径变成绝对路径
1 | path.resolve('./a.js'); //返回值是一个绝对路径 |
http模块
什么是客户端?什么是服务器?
- 在网络节点中,负责消耗资源的电脑,叫做客户端
- 负责对外提供网络资源的电脑,叫做服务器
http模块是Node.js官方提供的,用来创建web服务器的模块,通过http模块提供的http.createServer()方法,就能方便把一台普通的电脑,变成一台web服务器,从而对外提供web资源服务。
如果希望使用http模块创建Web服务器,则需要导入他:
1 | 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)
注意:
- 互联网中每台Web服务器,都有自己的IP地址,例如:大家可以在Windows的终端中运行ping www baidu.com命令,即可查看到百度服务器的IP地址。
- 在开发期间,自己的电脑既是一台服务器, 也是-个客户端,为了方便测试,可以在自己的浏览器中输入127.0.0.1 这个IP地址,就能把自己的电脑当做一台服务器进行访问了.
2.域名和域名服务器
尽管IP地址能够唯-地标记网络上的计算机,但IP地址是一长串数字,不直观,而且不便于记忆,于是人们又发明了另一套字符型的地址方案,即所谓的域名(Domain Name)地址
IP地址和域名是一对应的关系, 这份对应关系存放在一种叫做域名服务器(DNS, Domain name server)的电脑中。使用者只需通过好记的域名访问对应的服务器即可,对应的转换工作由域名服务器实现。因此,域名服务器就是提供IP地址和域名之间的转换服务的服务器。
注意:
- 单纯使用IP地址,互联网中的电脑也能够正常工作。但是有了域名的加持,能让互联网的世界变得更加方便。
- 在开发测试期间,127.0.0.1 对应的域名是localhost,它们都代表我们自己的这台电脑,在使用效果上没有任何区别。
3.端口号
计算机中的端口号,就好像是现实生活中的门牌号一样。 通过门牌号,外卖小哥可以在整栋大楼众多的房间中,准确把外卖送到你的手中。
同样的道理,在一台电脑中,可以运行成百上千个web服务。每个web服务都对应-个唯的端口号. 客户端发送过来的网络请求,通过端口号,可以被准确地交给对应的web服务进行处理。
注意:
- 每个端口号不能同时被多个web服务器占用
- 在实际运用中URL中的80端口可以被省略
创建最基本的web服务器
创建web服务器的基本步骤
- 导入http模块
- 创建web服务器实例
- 为服务器实例绑定require事件,监听客户端的请求
- 启动服务器
步骤1-导入http模块
如果希望在自己电脑上创建一个web服务器,从而对外提供web服务,则需要导入http模块:
1 | const http = require('http'); |
步骤2-创建web服务器实例
调用**http.createServer()**方法,即可快速创建一个web服务器实例:
1 | const server = http.createServer(); |
步骤3-为服务器实例绑定request事件
为服务器实例绑定request事件,即可监听客户端发送过来的网络请求:
1 | // 使用服务器实例的.on()方法,为服务器绑定一个request事件 |
req请求对象
只要服务器接收了客户端的请求,就会调用server.on()为服务器绑定的request事件处理函数。如果想在事件处理函数中,访问与客户端相关的数据或者属性,就可以使用如下方法:
1 | server.on('request',(req)=>{ |
res响应对象
在服务器的request使事件处理函数中,如果想访问与服务器相关的数据或属性,可以使用如下方式:
1 | server.on('request', (req, res) => { |
解决中文乱码问题
当调用res.end()方法,向客户端发送中文内容时候,就会出现乱码问题,此时,需要手动设置内容的编码格式:
res.setHeader('Content-Type', 'text/html;charset=utf-8');
1 | server.on('request', (req, res) => { |
步骤4-启动服务器
调用服务器实例的.listen()方法,即可启动当前的web服务器实例:
1 | // 调用server.listen(端口号,callback回调函数)方法,即可启动web服务器 |
根据不同的url相应不同的html内容
核心实现步骤:
- 获取请求的url地址
- 设置默认的响应内容为404 Not found
- 判断用户请求的是否为**/或/index.html**首页
- 判断用户请求的是否为**/about.html**关于页面
- 设置Content-Type响应头,防止中文乱码
- 使用**res.end( )**把内容响应给客户端
1 | const http = require('http'); |
process 系统进程模块
procee系统的进程模块
用process区分代码的运行环境
未来在框架的T程化开发中:用来区分前端代码的运行环境
模块化
什么是模块化?
模块化是指解决一个复杂问题时,自顶向下逐层把系统划分为若干模块的过程。对整个系统来说,模块是可以组合,分解,更换的单元。
编程领域中的模块化,就是遵守固定的规则,把一个大文件拆分成独立并相互依赖的多个小模块:
把代码进行模块化拆分的好处:
- 提高了代码的复用性
- 提高了代码的可维护性
- 可以实现按需加载
模块化规范
模块化规范就是对代码进行模块化的拆分与组合时,需要遵循的那些规则。
例如:
- 使用什么样的语法格式来引入模块
- 在模块中使用什么样的语法格式向外暴露成员
模块化的好处:大家都遵循同样的模块化规范写代码,降低了沟通的成本,极大方便了各个模块之间的相互调用,利人利己。
Node.js中模块的分类
Node.js中根据模块来源的不同,将模块分为了3大类,分别是:
- 内置模块(内置模块是Node.js官方提供的,例如:fs,path,http等)
- 自定义模块(用户创建的每个js文件,都是自定义模块)
- 第三方模块(由第三方开发出来的模块,并非官方提供的内置模块,也不用用户创建的自定义模块。使用前要先下载)
加载模块
使用强大的require()方法,可以加在需要的内置模块,用户自定义模块,第三方模块进行使用。例如:
注意:使用require()方法加载其他模块时候,会执行被加载模块中的代码。
Node.js中的模块作用域
1.什么是模块作用域
和函数作用域类似,在自定义模块中定义的变量,方法。等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块作用域;
2.模块作用域的好处
防止全局变量污染的问题
当两个js文件中有相同的变量,就会造成全局变量污染
3.向外共享模块作用域中的成员
1.module对象
在每个.js自定义模块中都有一个module对象,它里面存储了和当前模块有关的信息,打印如下:
2.module.exports对象
在自定义模块中。可以使用module.exports对象,将模块内的成员共享出去,供外界使用。
外界用**require( )**方法导入自定义模块时候。得到的就是module.exports所指向对象。
如果想要公开自定义模块中的一部分,可以直接为
module.exports
添加属性或者方法
3.共享成员时的注意点
使用require()方法导入模块时,导入的结果,永远以module.exports指向的对象为准。
4.exports对象
由于module.exports单词写起来比较复杂,为了简化向外共享成员的代码,Node 提供了exports 对象。默认情况下,exports 和module.exports指向同一个对象。最终共享的结果,还是以module.exports指向的对象为准。
注意:
最终共享的结果,还是以module.exports指向的对象为准。
如果module.exports这个老六偷偷指向了另一个对象,则以它为准😅
5.exports和module.exports的使用误区
时刻谨记,require()模块时,得到的永远是module exports指向的对象:
Node.js中的模块化规范
Node.js遵循了CommonJs模块化规范。CommonJS规定了模块的特性和各模块之间相互依赖。
CommonJS规定:
- 每个模块内部,module变量代表当前模块
- module变量是一个对象,它的exports属性,(即module.exports)是对外的接口
- 加载某个模块,其实就是加载该模块的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/的服务器,来对外共享所有的包,我们可以从这个服务器上下载自己所需要的包。
注意:
如何下载包
npm, Inc.公司提供了-一个包管理工具,我们可以使用这个包管理工具,从https://registry.npmis org/服务器把需要的包下载到本地使用。
这个包管理”工具的名字叫做Node Package Manager (简称npm包管理工具),这个包管理工具随着Node.js的安装包-起被安装到了用户的电脑上。
大家可以在终端中执行npm-v命令,来查看自己电脑上所安装的npm包管理工具的版本号:
npm初体验
格式化时间高级做法
- 使用npm包管理工具,在项目中安装格式化时间的包moment ,使用
npm install moment
或者npm i moment
命令 - 使用require( )导入格式化时间的包
- 参考moment的官方API文档对时间进行格式化
初次安装后多出的文件
初次装包完成后,在项目文件夹下多-个叫做 node_ modules的文件夹和package-lock.json的配置文件。
其中:
- node_modules文件夹用来存放所有已安装到项目中的包。require()导入第三方包时,就是从这个目录中查找并加载包。
- package-lock.json配置文件用来记录node. _modules目录下的每一个包的下载信息, 例如包的名字、版本号、下载地址等。
注意:程序员不要手动修改node_ modules 或package-lock.json文件中的任何代码,npm包管理工具会自动维护它们。
安装指定版本的包
默认情况下,使用npm install命令安装包的时候,会自动安装最新版本的包。如果需要安装指定版本的包,可以在包名之后,通过@符号指定具体的版本,例如:
包的语义化版本规范
包的版本号是以”点分十进制”形式进行定义的,总共有三位数字,例如 2.24.0
其中每一位数字所代表的的含义如下:
第1位数字:大版本
第2位数字:功能版本
第3位数字: Bug修复版本
版本号提升的规则:只要前面的版本号增长了,则后面的版本号归零。
包管理配置文件
npm规定,在项目根目录中,必须提供一个叫做 package.json的包管理配置文件。用来记录与项目有关的一一些配置信息。例如:
- 项目的名称、版本号、描述等
- 项目中都用到了哪些包
- 哪些包只在开发期间会用到
- 那些包在开发和部署时都需要用到
dependencies节点
package.json文件中,有一个dependencies节点,专门用来记录您使用npm install命令安装了哪些包。
快速生成package.json
npm包管理工具提供了一个快捷命令,可以在执行命令时所处的目录中,快捷创建package.json这个包管理配置文件:
注意:
①上述命令只能在英文的目录下成功运行!所以,项目文件夹的名称-定要使用英文命名,不要使用中文,不能出现空格。
②运行npm install命令安装包的时候,npm包管理工具会自动把包的名称和版本号,记录到package.json中。
git合作开发遇到的问题
在实际上开发中,上传至git的前提下会自动屏蔽掉node_modules文件中的所有第三方包。通过.gitignore
配置文件忽略掉该文件。避免项目文件过大。
而在拿到项目的时候,可以通过npm install
或者npm i
指令安装所有你需要的第三方包。
卸载包
可以运行npm uninstall
命令,来卸载指定的包:
注意:npm uninstall 命令执行后,会把卸载的包,自动从package.json的dependencies中移除掉
devDependencies/dependencies节点
如果某些包只在项目开发中会用到,在项目上线后不会用到,则建议把这些包记录到devDependencies节点中 开发依赖
npm i 包名 --save
与之对应的,如果某些包在开发和项目上线之后都需要用到,则建议把这些包记录到dependencies节点中。 运行依赖
npm i 包名 --save-dev
默认下的包都是运行依赖
淘宝NPM镜像服务器
淘宝在国内搭建了-一个服务器,专门把国外官方服务器上的包同步到国内的服务器,然后在国内提供下包的服务。从而极大的提高了下包的速度。
扩展:
镜像是一种文件存储形式。一个磁盘上的数据在另一个磁盘上存在完全相同的副本即为镜像
切换npm的下包镜像源
下包的镜像源,指的是下包的服务器地址
npm config get registry
查看当前下包镜像源npm config set registry=https://registry.npm.taobao.org/
将下包的镜像源切换为淘宝镜像源
nrm
为了更方便的切换下包的镜像源,我们可以安装nrm这个小工具,利用nrm提供的终端命令,可以快速查看和切换下包的镜像源。
npm i nrm -g
通过npm包管理器,将nrm安装为全局可用的工具nrm ls
查看所有可用镜像源nrm use taobao
将下包的镜像源切换为淘宝镜像
包的分类
项目包
那些被安装到项目的node _modules目录中的包,都是项目包。
项目包又分为两类,分别是;
- 开发依赖包(被记录到devDependencies节点中的包,只在开发期间会用到)
- 核心依赖包(被记录到dependencies节点中的包,在开发期间和项目.上线之后都会用到)
全局包
在执行npm install
命令时,如果提供了-g 参数,则会把包安装为全局包。
全局包会被安装到C:\Users\用户目录\AppData\Roaming\npm\node_ modules 目录下。
注意:
- 只有工具性质的包,才有全局安装的必要性,因为它们提供了好用的终端命令。
- 判断某个包是否需要全局安装后再能使用,可以参考官方提供的使用说明即可。
i5ting_toc
iting_toc是一个可以把md文档转为html页面的小工具,使用步骤如下:
规范包结构
在清楚了包的概念以及如何下载和使用包之后,接下来,我们深入了解一下包的内部结构。
一个规范的包,它的组成结构,必须符合以下3点要求:
- 包必须以单独的目录而存在
- 包的顶级目录下要必须包含package.json 这个包管理配置文件
- package.json 中必须包含name, version, main这三个属性,分别代表包的名字版本号、包的入口。
注意:以上3点要求是一个规范的包结构必须遵守的格式,关于更多的约束,可以参考网站
发布包
在符合规范包结构的前提下就可以自己制作一个包并发布到npm网站。
关于包的规范
关于package.json文件
name 发布包时的包名字,不可以与他人重复
version 包版本
type 配置模块化语法
- moduel 作为es模块处理
- commonjs 作为commonjs模块处理
mian 入口文件
scripts 自定义执行指令
description 被检索时所展示的描述
keywords 被检索的关键字
author 作者信息
license 开源协议
遵循模块化规范
入口文件写入到index.js
文件下,方法文件放入src文件中
关于入口文件index.js
- dateNew/escape是对象
- 需要挂载到module.exports
- 使用
...
运算符展开这两个对象 - 让他们的所有属性交给新的对象module.exports
发布包
在npm官网注册好账号后在终端登录账户
- 使用指令
npm login
依次输入用户名 - 密码 - 邮箱
- 推送
npm publish
注意:镜像服务器必须是npm官方地址
删除包
运行npm unpublish 包名 --force
命令,即可从npm删除已发布的包。
注意:
npm unpublish
命令只能删除72小时以内发布的包npm unpublish
删除的包,在24小时内不允许重复发布- 发布包的时候要慎重,尽量不要往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 express@4.17.1
安装指定版本或者npm install express --save
安装最新版本
创建基本的web服务器
- 导入express模块
- 创建web服务器
- 启动web服务器
1 | // 1.导入express |
监听GET请求
通过app.get()
方法,可以监听客户端的GET请求,具体的语法格式如下:
- 参数1:客户端请求的URL地址
- 参数2:请求对应的处理函数
- req:请求对象(包含了与请求相关的属性和方法)
- res:响应对象(包含了与响应相关的属性与方法)
1 | app.get('请求URL',function(req,res){/*处理函数*/}); |
监听POST请求
通过app.post()
方法,可以监听客户端的POST请求,具体语法如下:
- 参数1:客户端请求的URL地址
- 参数2:请求对应的处理函数
- req:请求对象(包含了与请求相关的属性和方法)
- res:响应对象(包含了与响应相关的属性与方法)
1
app.post('请求URL',function(req,res){/*处理函数*/});
把内容响应给客户端
通过res.send()
方法,可以把处理好的内容,发送给客户端:
1 | app.get('/user',(req,res)=>{ |
获取URL中携带的查询参数query
通过req.query
对象,可以访问到客户端通过查询字符串的形式,发送到服务器的参数:
获取URL中的动态参数params
通过req.params对象,可以访问到URL中,通过:匹配到的动态参数:
:id并非固定写法,”:”才是,id是可变的
1 | // url地址中,可以通过:参数名的形式,匹配动态参数值 |
postmen测试url:http://192.168.210.146:8080/login/666/789
- 此时得到的 id为666 pass为789
托管静态资源
1.express.static()
express提供了一个非常好用的函数,叫做express.static()
,通过它,我们可以非常方便地创建一个静态资源服务器。
例如,通过如下代码就可以将public 目录下的图片、CSS 文件、JavaScript 文件对外开放访问了:
1 | app.use(express.static('public')) |
注意: Express 在指定的静态目录中查找文件,并对外提供资源的访问路径。
因此,存放静态文件的目录名不会出现在URL中。
2.托管多个静态目录
当有两个文件都需要开放自己创建静态服务器,可以反复调用express.static()
方法
访问静态资源文件时,express.static() 函数会根据目录的添加顺序查找所需的文件。优先级总是第一个
3.挂载路径前缀
如果希望在托管的静态资源访问路径之前,挂载路径前缀,则可以使用如下的方式:
现在,你就可以通过带有 /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路由
路由的概念
现实生活中的路由
在这里,路由是按键与服务之间的映射关系
Express中的路由
在Express中,路由指的是客户端的请求与服务器处理函数之间的映射关系。
Express中的路由分3部分组成,分别是请求的类型、请求的URL地址、处理函数,格式如下:
1 | app.METHOD(PATH,HANDLER) |
Express中路由的例子
1 | //匹配GET请求,且请求URL为 / |
路由的匹配过程
每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数。
在匹配时,会按照路由的顺序进行四配,如果请求类型和请求的URL同时匹配成功,则Express会将这次请求,转交给对应的function函数进行处理。
路由匹配的注意点:
①按照定义的先后顺序进行匹配
②请求类型和请求的URL同时匹配成功,才会调用对应的处理函数
路由的使用
1.简单的用法
在Express中使用路由最简单的方式,就是把路由挂载到app上,示例代码如下:
2.模块化路由
为了方便对路由进行模块化的管理,Express 不建议将路由直接挂载到app.上,而是推荐将路由抽离为单独的模块。
将路由抽离为单独模块的步骤如下:
- 创建路由模块对应的js文件
- 调用express.Router( )函数创建路由对象
- 向路由对象上挂载具体的路由
- 使用module.exports向外共享路由对象
- 使用app.use( )函数注册路由模块
2.1创建路由模块
2.2注册路由模块
注意:**app.use( )**函数的作用。就是用来注册全局中间件
2.3为路由模块添加前缀
类似于托管静态资源时,为静态资源统一挂载访问前缀一样,路由模块添加前缀的方式也非常简单:
Express中间件
现实中的例子
在处理污水的时候,一般都要经过三个处理环节, 从而保证处理过后的废水,达到排放标准。
处理污水的三个中间环节,就可以叫做中间件。
中间件的调用流程
当一个请求到达Express 的服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理。
中间件的格式
Express的中间件,本质上就是一个function处理管道函数,Express 中间件的格式如下:
注意:中间件函数的形参列表中,必须包含next参数。而路由处理函数中只包含req和res.
next函数的作用
next函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。
使用Express中间件
定义中间件函数
可以通过如下的方式,定义一个最简单的中间件函数:
1 | const express = require('express'); |
全局生效的中间件
客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件。
通过调用app.use(mw);
即可定义一个全局生效的中间件,示例代码如下:
1 | const express = require('express'); |
简化合并写法:
定义多个全局中间件
可以使用app.use0连续定义多个全局中间件。客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用,示例代码如下:
1 | const express = require('express'); |
局部生效的中间件
不使用app.use()定义的中间件,叫做局部生效的中间件,示例代码如下:
1 | const express = require('express'); |
定义多个局部生效的中间件:
可以在路由中,通过如下两种等价的方式,使用多个局部中间件:
中间件的作用
多个中间件之间,共享同- -份req和res.基于这样的特性,我们可以在上游的中间件中,统-为req或res对象添加自定义的属性或方法,供下游的中间件或路由进行使用。
中间件注意事项
- 一定要在路由之前注册中间件(除了错误级别中间件)
- 客户端发送过来的请求,可以连续调用多个中间件进行处理
- 执行完中间件的业务代码之后,不要忘记调用next(函数
- 为了防止代码逻辑混乱,调用next(函数后不要再写额外的代码
- 连续调用多个中间件时,多个中间件之间,共享req和res对象
中间件分类
为了方便大家理解和记忆中间件的使用,Express 官方把常见的中间件用法,分成了5大类,分别是:
- 应用级别的中间件
- 路由级别的中间件
- 错误级别的中间件
- Express 内置的中间件
- 第三方的中间件
应用级别中间件
路由级别中间件
绑定到express.Router()实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别。只不过,应用级别中间件是绑定到app实例上,路由级别中间件绑定到router实例上,代码示例如下:
错误级别中间件
错误级别中间件的作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
格式:错误级别中间件的function 处理函数中,必须有4个形参,形参顺序从前到后,分别是(err, req, res, next)。
注意:错误级别的中间件,必须注册在所有路由之后!
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实例:
1 | const express = require('express'); |
express.urlencoded实例:
express.urlencoded({ extended: false }) 固定写法
1 | const express = require('express'); |
第三方的中间件
非Express官方内置的,而是由第三方开发出来的中间,叫做第三方中间件,在项目中,大家可以按需下载并配置第三方中间件,从而提高项目的开发效率。
例如:在express@4.16.0之前的版本中,经常使用body-parser这个第三方中间件,来解析请求体数据,使用步骤如下:
- 运行
npm install body-parser
安装中间件 - 使用require导入中间件
- 使用app.use()注册并且使用中间件
使用Express写接口
1.创建基本的服务器
1 | const express = require('express'); |
2.创建API路由模块
首先写一个模块化路由并暴露出去
1 | // 模块化路由 |
导入模块化路由并将路由模块挂载到中间件:
1 | const express = require('express'); |
3.编写GET接口
在路由模块 挂载对应的路由:
1 | // 模块化路由模块 |
4.编写POST请求
使用post请求需要配置解析表单数据的中间件:
1 | const express = require('express'); |
在路由模块编写POST接口:
1 | // 模块化路由模块 |
req.query
:我们用来接收GET方式提交的参数。req.body
:我们用来接收POST提交的参数。req.param
:两种都能接收到。
CORS跨域资源共享
1.接口的跨域问题
刚才编写的GET和POST接口,存在一个严重问题:不支持跨域请求
解决接口跨域问题的方案主要有两种:
- CORS(主流解决方案,推荐)
- JSONP(有缺陷的解决方案,只支持GET请求)
2.使用cors中间件解决跨域问题
cors是express的一个第三方中间件。通过安装和配置cors中间件。可以很方便的解决跨域问题,使用步骤如下:
- 运行
npm install cors
安装中间件 - 使用
const cors = require('cors')
导入中间件 - 在路由之前调用
app.use(cors())
配置中间件
1 | const express = require('express'); |
3.什么是cors
CORS (Cross-Origin Resource Sharing,跨域资源共享)由-系列HTTP响应头组成,这些HTTP响应头决定浏览器是否阻止前端JS代码跨域获取资源。
浏览器的网源安全策略默认会阻止网页“跨域”获取资源。但如果接口服务器配熏了CORS相关的HTTP响应头,就可以解除浏览器端的跨域访问限制,
4.cors注意事项
- cors只要在服务器端进行配置,客户端浏览器无需做任何额外的配置,即可请求开启了cors的接口。
- cors在浏览器中有兼容性,只有支持XMLHttpPequest Level2的浏览器,才能正常访问开启了CORS的服务端接口(例如:IE10,Chrome4+,FireFox3.5+)
5.CORS响应头 - Access-Control-Allow-Origin
响应头部中可以携带一个Access-Control-Allow-Origin字段,其语法如下;
Access-Control-Allow-Origin:<origin>|*
其中,origin参数的值制定了允许访问该资源的外域URL
例如:下面的字段值将只允许来自http://itcast.cn的请求:
1 | res.setHeader('Access-Control-Allow-Origin','http://itcast.cn'); |
如果指定了Access-Control-Allow-Origin字段的值为通配符*,表示允许来自任何域的请求。示例代码如下:
1 | 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对额外的请求头进行声明,否则这次请求会失败!
1 | 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方法。
示例代码如下:
1 | // 只允许 POST GET DELETE HEAD 请求方式 |
8.CORS请求的分类
客户端在请求CORS接口时,根据请求方式和请求头的不同。可以将CORS的请求分为两大类,分别是:
- 简单请求
- 预检请求
A.简单请求
同时满足以下两大类的请求,就属于简单请求:
- 请求方式:GET POST HEAD 三者之一
- 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.预检请求
只要符合以下任意一个条件的请求。都需要进行预检请求:
- 请求方式为GET、POST、HEAD之外的请求Method类型
- 请求头中包含自定义头部字段
- 向服务器发送了application/json 格式的数据
两者区别
简单请求的特点:客户端与服务器之间只会发生一次请求。
预检请求的特点:客户端与服务器之间会发生两次请求,OPTION预检请求成功之后,才会发起真正的请求。
JSONP接口
1.回顾jsonp的概念与特点
概念:浏览器端通过<script>
标签的src属性,请求服务器上的数据,同时,服务器返回一个函数的调用。这种请求数据的方式叫做JSONP.
特点:
①JSONP 不属于真正的Ajax请求,因为它没有使用XMLHttpRequest这个对象。
②JSONP 仅支持GET请求,不支持POST、PUT、 DELETE 等请求。
2.jsonp接口的注意事项
如果项目中已经配置了CORS跨域资源共享,为了防止冲突,必须在配置CORS中间件之前声明JSONP的接口。否则JSONP接口会被处理成开启了CORS的接口。示例代码如下:
实现jsonp接口的步骤
①获取客户端发送过来的回调函数的名字
②得到要通过JSONP形式发送给客户端的数据
③根据前两步得到的数据,拼接出一个函数调用的字符串
④把上一步拼接得到的字符串,响应给客户端的<script>
标签进行解析执行