NodeJs
NodeJs
Buffer模块
Buffer 是一个类似于数组的 对象
,用于表示固定长度的字节序列
Buffer 本质是一段内存空间,专门用来处理 二进制数据
。
特点:
- Buffer 大小固定且无法调整
- Buffer 性能较好,可以直接对计算机内存进行操作
- 每个元素的大小为 1 字节(byte)
使用
创建 Buffer
Node.js 中创建 Buffer 的方式主要如下几种:
Buffer.alloc
1
2// 创建了一个长度为 10 字节的 Buffer,相当于申请了 10 字节的内存空间,每个字节的值为 0
let buf_1 = Buffer.alloc(10) //=>结果为<Buffer 00 00 00 00 00 00 00 00 00 00>Buffer.allocUnsafe
1
2// 创建了一个长度为 10 字节的 Buffer,buffer 中可能存在旧数据,可能会影响执行结果,所以叫 unsafe ,但是效率比 alloc 高
let buf_2 = Buffer.allocUnsafe(10)
Buffer.from
1
2
3
4// 通过字符串创建 Buffer
let buf_3 = Buffer.from('hello')
// 通过数组创建 Buffer
let buf_4 = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117])
Buffer 与字符串的转化
我们可以借助
toString
方法将 Buffer 转为字符串
1 | let buf_4 = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117]) |
**注意: **toString
默认是按照 utf-8
编码方式进行转换的
Buffer 的读写
Buffer 可以直接通过
[]
的方式对数据进行处理。
1 | let buf_3 = Buffer.from('hello') |
注意:
- 如果修改的数值超过
255
,则超过8
位数据会被舍弃 - 一个
utf-8
的字符 一般 占3个字节
fs 模块
fs 全称为
file system
,称之为文件系统
,是 Node.js 中的内置模块
,可以对计算机中的磁盘进行操作:
文件写入
文件读取
文件移动与重命名
文件删除
文件夹操作
查看资源状态
文件写入
文件写入就是将 数据 保存到 文件 中,我们可以使用如下几个方法来实现该效果
方法 | 说明 |
---|---|
writeFile | 异步写入 |
writeFileSync | 同步写入 |
appendFile / appendFileSync | 追加写入 |
createWriteStream | 流式写入 |
writeFile 异步写入
语法: fs.writeFile(file, data[, options], callback)
参数说明:
file 文件名
data 待写入的数据
options 选项设置
(可选)
callback 写入回调
返回值: undefined
代码示例:
1 | // require 是 Node.js 环境中的 '全局' 变量,用来导入模块 |
writeFileSync 同步写入
语法: fs.writeFileSync(file, data[, options])
参数与 fs.writeFile
大体一致,只是没有 callback 参数
返回值:undefined
代码示例:
1 | try{ |
Node.js 中的磁盘操作是由其他
线程
完成的,结果的处理有两种模式:
- 同步处理 JavaScript 主线程
会等待
其线程的执行结果,然后再继续执行主线程的代码,效率较低
- 异步处理 JavaScript 主线程
不会等待
其线程的执行结果,直接执行后续的主线程代码,效率较好
appendFile / appendFileSync 追加写入
appendFile
作用是在文件尾部追加内容,appendFile
语法与writeFile
语法完全相同
语法:
fs.appendFile(file, data[, options], callback)
fs.appendFileSync(file, data[, options])
返回值:二者都为 undefined
实例代码:
1 | fs.append('./座右铭.txt', '则其善者而从之,其不善者而改之。', err =>{ |
createWriteStream 流式写入
语法:fs.createWriteStream(path[, options])
参数说明:
path
文件路径options
选项配置(可选
)
**返回值: **Object
代码示例:
1 | let ws = fs.createWriteStream('./观书有感.txt') |
程序打开一个文件是需要消耗资源的,流式写入可以减少打开关闭文件的次数。
流式写入方式适用于 大文件写入或者频繁写入的场景,
writeFile
适合于 写入频率较低的场景
文件读取
文件读取顾名思义,就是通过程序从文件中取出其中的数据,我们可以使用如下几种方式:
方法 | 说明 |
---|---|
readFile | 异步读取 |
readFileSync | 同步读取 |
createReadStream | 流式读取 |
文件移动与重命名
在 Node.js 中,我们可以使用
rename
或renameSync
来移动或重命名文件或文件夹
语法:
fs.rename(oldPath, newPath, callback)
fs.renameSync(oldPath, newPath)
参数说明:
oldPath 文件当前的路径
newPath 文件新的路径
callback 操作后的回调
代码示例:
1 | fs.rename('./观书有感.txt', './论语/观书有感.txt', err =>{ |
注: 如果还是移动到当前路径,但是修改了名字,就是重命名了
文件删除
在 Node.js 中,我们可以使用
unlink
或unlinkSync
来删除文件
语法:
fs.unlink(path, callback)
fs.unlinkSync(path)
参数说明:
path 文件路径
callback 操作后的回调
代码示例:
1 | const fs = require('fs') |
文件夹操作
借助 Node.js 的能力,我们可以对文件夹进行
创建
、读取
、删除
等操作
方法 | 说明 |
---|---|
mkdir / mkdirSync | 创建文件夹 |
readdir / readdirSync | 读取文件夹 |
rmdir / rmdirSync | 删除文件夹 |
mkdir 创建文件夹
在 Node.js 中,我们可以使用
mkdir
或mkdirSync
来创建文件夹
语法:
fs.mkdir(path[, options], callback)
fs.mkdirSync(path[, options])
参数说明:
path 文件夹路径
options 选项配置(
可选
)callback 操作后的回调
示例代码:
1 | // 异步创建文件夹 mk make 制作 dir directory 文件夹 |
readdir 读取文件夹
在 Node.js 中,我们可以使用
readdir
或readdirSync
来读取文件夹
语法:
fs.readdir(path[, options], callback)
fs.readdirSync(path[, options])
参数说明:
path 文件夹路径
options 选项配置(
可选
)callback 操作后的回调
示例代码:
1 | // 异步读取 |
rmdir 删除文件夹
在 Node.js 中,我们可以使用
rmdir
或rmdirSync
来删除文件夹
语法:
fs.rmdir(path[, options], callback)
fs.redirSync(path[, options])
参数说明:
path 文件夹路径
options 选项配置( 可选 )
callback 操作后的回调
示例代码:
1 | // 异步删除文件夹 rm remove 移除 |
查看资源状态
在 Node.js 中,我们可以使用
stat
或statSync
来查看资源的详细信息
语法:
fs.stat(path[, options], callback)
fs.statSync(path[, options])
参数说明:
path 文件夹路径
options 选项配置( 可选 )
callback 操作后的回调
示例代码:
1 | // 异步获取状态 |
相对路径问题
fs 模块对资源进行操作时,路径的写法有两种:
相对路径
./座右铭.txt
当前目录下的 座右铭.txt座右铭.txt
等效于上面的写法../座右铭.txt
当前目录的上一级目录中的 座右铭.txt
绝对路径
D:/Program Files
windows 系统下的绝对路径/user/bin
Linux 系统下的绝对路径
相对路径中所谓的
当前目录
,指的是命名行的工作目录
,而并非是文件的所在目录所以当命名行的工作目录与文件所在目录不一致时,会出现一些 Bug
__dirname
__dirname
与 require
类似,都是 Node.js 环境中的 ‘全局’ 变量
__dirname
保存着 当前文件夹所在目录的绝对路径,可以使用 __dirname
与文件名拼接成绝对路径
代码示例:
1 | //=>__dirname + '/data.txt' === 'D:\\Desktop\\Node\\code\\03-fs模块/data.txt' |
使用 fs 模块的时候,尽量使用
__dirname
路径转换为绝对路径,这样可以避免相对路径产生的 Bug
path模块
path
模块提供了操作路径
的功能,我们将介绍如下几个较为常用的几个 API:
API | 说明 |
---|---|
path.resolve | 拼接规范的绝对路径 常用 |
path.sep | 获取操作系统的路径分隔符 |
path.parse | 解析路径并返回对象 |
path.basename | 获取路径的基础名称 |
path.dirname | 获取路径的目录名 |
path.extname | 获得路径的扩展名 |
代码示例:
1 | // 导入 fs 模块 |
HTTP模块
使用 nodejs 创建 HTTP 服务
1 | //1. 导入 http 模块 |
获取 HTTP 请求报文
含义 | 语法 |
---|---|
请求方法 | request.method |
请求版本 | request.httpVersion |
请求路径 | request.url |
URL 路径 | require('url').parse(request.url).pathname |
URL 查询字符串 | require('url').parse(request.url, true).query |
请求头 | request.headers |
请求体 | request.on(‘data’, function(chunk){}) request.on(‘end’, function(){}) |
设置 HTTP 响应报文
作用 | 语法 |
---|---|
设置响应状态码 | response.statusCode |
设置响应状态描述 | response.statusMessage ( 用的非常少 ) |
设置响应头信息 | response.setHeader(‘头名’, ‘头值’) (可以自定义 ) |
设置响应体 | response.write(‘xx’) response.end(‘xxx’) |
Node.js 模块化
介绍
将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程称之为
模块化
其中拆分出的 每个文件就是一个模块,模块的内部数据是私有的,不过模块可以暴露内部数据以便其他模块使用。
下面是模块化的一些好处:
防止命名冲突
高复用性
高维护性
模块暴露数据
模块暴露数据的方式有两种:
module.exports
= valueexports.name
= value
使用:
创建 me.js
1
2
3
4
5
6//声明函数
function tiemo(){
console.log('贴膜....');
}
//暴露数据
module.exports = tiemo;创建 index.js
1
2
3
4//导入模块
const tiemo = require('./me.js');
//调用函数
tiemo(); //=> 贴膜....
使用时有几点注意:
module.exports
可以暴露 任意 数据- 不能使用
exports = value
的形式暴露数据,模块内部 module 与 exports 的隐式关系exports = module.exports = {}
,require 返回的是目标模块中module.exports
的值
导入(引入)模块
在模块中使用 require 传入文件路径即可引入文件
1 | const test = require('./me.js') |
require 使用的一些注意事项:
对于自己创建的模块,导入时路径建议写 相对路径,且不能省略
./
和../
js
和json
文件导入时可以不用写后缀,c/c++编写的node
扩展文件也可以不写后缀,但是一般用不到,直接使用 node 的require()
方法即可将 JSON 文件转换成 JS 对象如果导入其他类型的文件,会以
js
文件进行处理如果导入的路径是个文件夹,则会 首先 检测该文件夹下
package.json
文件中main
属性对应的文件,如果存在则导入,反之如果文件不存在会报错。
如果 main 属性不存在,或者 package.json 不存在,则会尝试导入文件夹下的
index.js
和index.json
,如果还是没找到,就会报错
导入 node.js 内置模块时,直接 require 模块的名字即可,无需加
./
和../
导入模块的基本流程
这里我们介绍一下 require
导入 自定义模块 的基本流程
- 将相对路径转为绝对路径,定位目标文件
- 缓存检测
- 读取目标文件代码
- 包裹为一个函数并执行(自执行函数)。通过
arguments.callee.toString()
查看自执行函数 - 缓存模块的值
- 返回
module.exports
的值
1 | /** |
CommonJS 规范
module.exports
、exports
以及 require
这些都是 CommonJS
模块化规范中的内容。
而 Node.js 是实现了 CommonJS 模块化规范,二者关系有点像 JavaScript 与 ECMAScript。
CommonJS
CommonJS 是 Node.js 最初采用的模块系统,主要特点包括:
语法
1 | // 导出 |
特点
- 同步加载:模块在运行时同步加载
- 动态导入:可以在条件语句中导入
- 值拷贝:导入的是值的拷贝,而非引用
- 循环依赖处理:通过缓存部分执行的模块解决
使用场景
- Node.js 服务端开发
- 较老的前端项目(通过 Browserify 或 Webpack 等工具转换)
ES Module
ES Module 是 ECMAScript 标准的一部分,是现代 JavaScript 推荐的模块系统:
语法
1 | // 导出 |
特点
- 静态分析:导入导出语句在编译时处理,支持静态分析
- 异步加载:模块可以异步加载
- 值引用:导入的是值的引用,而非拷贝
- 不可修改:导入的绑定是只读的
- 支持 Tree Shaking:可以在构建时移除未使用的代码
使用场景
- 现代浏览器
- 现代前端框架(React, Vue, Angular 等)
- 使用 TypeScript 的项目
- 支持 ES Module 的 Node.js 版本
CommonJS 和 ES Moudle的区别
语法差异:
- CommonJS:
require()
和module.exports
- ES Module:
import
和export
- CommonJS:
加载时机:
- CommonJS: 运行时加载
- ES Module: 编译时加载(静态分析)
导入内容:
- CommonJS: 导入的是值的拷贝
- ES Module: 导入的是值的引用(实时绑定)
顶层作用域:
- CommonJS:
this
指向当前模块 - ES Module:
this
是 undefined
- CommonJS:
文件扩展名:
- CommonJS: 通常是
.js
- ES Module: 在 Node.js 中通常是
.mjs
,或在 package.json 中设置"type": "module"
- CommonJS: 通常是
在 Node.js 中使用 ES Module
在 Node.js 中使用 ES Module 有两种方式:
- 使用
.mjs
扩展名 - 在 package.json 中设置
"type": "module"
1 | { |
互操作性
在现代项目中,可能需要在 ES Module 中导入 CommonJS 模块,或反之:
1 | // ES Module 导入 CommonJS 模块 |
不过,这种互操作性有一些限制,特别是在处理默认导出和命名导出时。
npm包管理工具
介绍
管理『包』的应用软件,可以对「包」进行 下载安装
, 更新
, 删除
, 上传
等操作
借助包管理工具,可以快速开发项目,提升开发效率
包管理工具是一个通用的概念,很多编程语言都有包管理工具,所以 掌握好包管理工具非常重要
常用的包管理工具
下面列举了前端常用的包管理工具
npm
- yarn
- cnpm
npm 的安装
node.js 在安装时会 自动安装 npm
,所以如果你已经安装了 node.js,可以直接使用 npm
可以通过 npm -v
查看版本号测试,如果显示版本号说明安装成功,反之安装失败
npm 基本使用
初始化
创建一个空目录,然后以此目录作为工作目录 启动命令行工具,执行 npm init
npm init
命令的作用是将文件夹初始化为一个『包』, 交互式创建 package.json 文件
package.json
是包的配置文件,每个包都必须要有 package.json
package.json
内容示例:
1 | { |
属性翻译
1 | { |
初始化的过程中还有一些注意事项:
- package name (
包名
) 不能使用中文、大写,默认值是文件夹的名称
,所以文件夹名称也不
能使用中文和大写- version (
版本号
)要求x.x.x
的形式定义,x
必须是数字,默认值是1.0.0
- ISC 证书与 MIT 证书功能上是相同的,关于开源证书扩展阅读 http://www.ruanyifeng.com/blog/2011/05/how_to_choose_free_software_licenses.html
package.json
可以手动创建与修改- 使用
npm init -y
或者npm init --yes
极速创建package.json
搜索包
搜索包的方式有两种
命令行 『npm s/search 关键字』
网站搜索
网址是https://www.npmjs.com/
require 导入 npm 包基本流程
- 在当前文件夹下 node_modules 中寻找同名的文件夹
- 在上级目录中下的 node_modules 中寻找同名的文件夹,直至找到磁盘根目录
生产依赖与开发依赖
我们可以在安装时设置选项来区分 依赖的类型
,目前分为两类:
类型 | 命令 | 补充 |
---|---|---|
生产依赖 | npm i -S uniq npm i –save uniq |
-S 等效于 –save,-S 是默认选项 包信息保存在 package.json 中 dependencies 属性 |
开发依赖 | npm i -D less npm i –save-dev less |
-D 等效于 –save-dev 包信息保存在 package.json 中 devDependencies 属性 |
开发依赖
是只在开发阶段使用的依赖包,而生产依赖
是开发阶段和最终上线运行阶段都用到的依赖包
全局安装
我们可以执行安装选项 -g 进行全局安装
1 | npm i -g nodemon |
全局安装完成之后就可以在命令行的任何位置运行 nodemon
命令
该命令的作用是 自动重启 node 应用程序
说明:
- 全局安装的命令不受工作目录位置影响
- 可以通过
npm root -g
可以查看全局安装包的位置- 不是所有的包都适合全局安装,只有全局类的工具才适合,可以通过查看包的官方文档来确定安装方式
修改 windows 执行策略
windows 默认不允许 npm 全局命令执行脚本文件,所以需要修改执行策略
以
管理员身份
打开powershell
命令行键入命令
set-ExecutionPolicy remoteSigned
键入 A 然后敲回车 👌
安装包依赖
在项目协作中有一个常用的命令就是 npm i
,通过该命令可以依据 package.json
和 package-lock.json
的依赖声明安装项目依赖
1 | npm i |
运行之后文件夹下会增加两个资源
node_modules 文件夹
存放下载的包package-lock.json 包的锁文件
,用来锁定包的版本
node_modules 文件夹大多数情况都不会存入版本库
安装指定版本的包
项目中可能会遇到版本不匹配的情况,有时就需要安装指定版本的包,可以使用下面的命令的
1 | # 格式 |
删除依赖
项目中可能需要删除某些不需要的包,可以使用下面的命令
1 | # 局部删除 |
配置命令别名
通过配置命令别名可以更简单的执行命令
配置 package.json 中的 scripts
属性
1 | { |
配置完成之后,可以使用别名执行命令
1 | npm run server |
不过 start
别名比较特别,使用时可以省略 run
1 | npm start |
补充说明:
npm start
是项目中常用的一个命令,一般用来启动项目npm run
有自动向上级目录查找的特性,跟require
函数也一样- 对于陌生的项目,我们可以通过查看
scripts
属性来参考项目的一些操作
npx
npx (Node Package Execute) 是 npm 5.2.0 版本引入的工具,主要用于:
临时执行包:无需全局安装即可执行 npm 包
1
npx cowsay "Hello"
一次性使用:适合那些不需要长期安装的工具
1
npx create-react-app my-app
确保使用最新版本:每次都会使用最新版本的包
1
npx eslint --init
执行 GitHub gist 或仓库:可以直接运行 GitHub 上的代码
1
npx github:user/repo
npm和npx的区别
安装方式:
- npm 将包安装到本地 node_modules 或全局环境
- npx 临时下载并执行包,不会留下持久安装
使用场景:
- npm 适合安装项目依赖和常用工具
- npx 适合一次性命令和避免全局污染
版本控制:
- npm 安装后使用固定版本
- npx 每次执行都会使用最新版本(除非指定)
实际应用示例
创建 React 应用时:
- 使用 npm:
1
2npm install -g create-react-app
create-react-app my-app - 使用 npx(推荐):
1
npx create-react-app my-app
npx 方式避免了全局安装,并确保每次都使用最新版本的 create-react-app。
cnpm
介绍
cnpm 是一个淘宝构建的npmjs.com
的完整镜像,也称为『淘宝镜像』,网址https://npmmirror.com/
cnpm 服务部署在国内 阿里云服务器上,可以提高包的下载速度
官方也提供了一个全局工具包 cnpm
,操作命令与 npm 大体相同
安装
我们可以通过 npm 来安装 cnpm 工具
1 | npm install -g cnpm --registry=https://registry.npmmirror.com |
操作命令
功能 | 命令 |
---|---|
初始化 | cnpm init / cnpm init |
安装包 | cnpm i uniq cnpm i -S uniq cnpm i -D uniq cnpm i -g nodemon |
安装项目依赖 | cnpm i |
删除 | cnpm r uniq |
npm 配置淘宝镜像
用 npm 也可以使用淘宝镜像,配置的方式有两种
- 直接配置
- 工具配置
直接配置
执行如下命令即可完成配置
1 | npm config set registry https://registry.npmmirror.com/ |
工具配置
使用 nrm
配置 npm 的镜像地址 npm registry manager
安装 nrm
1
npm i -g nrm
修改镜像
1
nrm use taobao
检查是否配置成功(选做)
1
npm config list
yarn
yarn 介绍
yarn 是由 Facebook 在 2016 年推出的新的 Javascript 包管理工具,官方网址:https://yarnpkg.com/
yarn 特点
yarn 官方宣称的一些特点
- 速度超快:yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率,因此安装速度更快
- 超级安全:在执行代码之前,yarn 会通过算法校验每个安装包的完整性
- 超级可靠:使用详细、简洁的锁文件格式和明确的安装算法,yarn 能够保证在不同系统上无差异的工作
yarn 安装
我们可以使用 npm 安装 yarn
1 | npm i -g yarn |
yarn 常用命令
功能 | 命令 |
---|---|
初始化 | yarn init / yarn init -y |
安装包 | yarn add uniq 生产依赖 yarn add less –dev 开发依赖 yarn global add nodemon 全局安装 |
删除包 | yarn remove uniq 删除项目依赖包 yarn global remove nodemon 全局删除包 |
安装项目依赖 | yarn |
运行命令别名 | yarn <别名> # 不需要添加 run |
yarn 全局安装包的位置可以通过
yarn global bin
来查看。如果全局安装的包不可用:
- 配置 path 环境
yarn 配置淘宝镜像
可以通过如下命令配置淘宝镜像
1 | yarn config set registry https://registry.npmmirror.com/ |
可以通过 yarn config list
查看 yarn 的配置项
管理发布包
创建与发布
我们可以将自己开发的工具包发布到 npm 服务上,方便自己和其他开发者使用,操作步骤如下:
创建文件夹,并创建文件 index.js, 在文件中声明函数,使用 module.exports 暴露
npm 初始化工具包,package.json 填写包的信息 (包的名字是唯一的)
激活账号 ( 一定要激活账号 )
修改为官方的官方镜像 (命令行中运行
nrm use npm
)命令行下
npm login
填写相关用户信息命令行下
npm publish
提交包 👌
更新包
后续可以对自己发布的包进行更新,操作步骤如下
更新包中的代码
测试代码是否可用
修改
package.json
中的版本号发布更新
1
npm publish
删除包
执行如下命令删除包
1 | npm unpublish --force |
删除包需要满足一定的条件, https://docs.npmjs.com/policies/unpublish
- 你是包的作者
- 发布小于 24 小时
- 大于 24 小时后,没有其他包依赖,并且每周小于 300 下载量,并且只有一个维护者
扩展内容
在很多语言中都有包管理工具,比如:
语言 | 包管理工具 |
---|---|
PHP | composer |
Python | pip |
Java | maven |
Go | go mod |
JavaScript | npm/yarn/cnpm/other |
Ruby | rubyGems |
除了编程语言领域有包管理工具之外,操作系统层面也存在包管理工具,不过这个包指的是『软件包
』
操作系统 | 包管理工具 | 网址 |
---|---|---|
Centos | yum | https://packages.debian.org/stable/ |
Ubuntu | apt | https://packages.ubuntu.com/ |
MacOS | homebrew | https://brew.sh/ |
Windows | chocolatey | https://chocolatey.org/ |
express 介绍
express 是一个基于 Node.js 平台的极简、灵活的 WEB 应用开发框架,官方网址: https://www.expressjs.com.cn/
简单来说,express 是一个封装好的工具包,封装了很多功能,便于我们开发 WEB 应用(HTTP 服务)
express 使用
express 下载
express 本身是一个 npm 包,所以可以通过 npm 安装
1 | npm init |
express 初体验
大家可以按照这个步骤进行操作:
1 | //1. 导入 express |
express 路由
什么是路由
官方定义: 路由确定了应用程序如何响应客户端对特定端点的请求
路由的使用
一个路由的组成有 请求方法
, 路径
和 回调函数
组成
express 中提供了一系列方法,可以很方便的使用路由,使用格式如下:
1 | app.<method>(path,callback) |
代码示例:
1 | //导入 express |
获取请求参数
express 框架封装了一些 API 来方便获取请求报文中的数据,并且兼容原生 HTTP 模块的获取方式
1 | //导入 express |
获取路由参数
路由参数指的是 URL 路径中的参数(数据)
1 | app.get('/:id.html', (req, res) => { |
express 响应设置
express 框架封装了一些 API 来方便给客户端响应数据,并且兼容原生 HTTP 模块的获取方式
1 | //获取请求的路由规则 |
express 中间件
什么是中间件
中间件(Middleware)本质是一个回调函数
中间件函数
可以像路由回调一样访问 请求对象(request)
, 响应对象(response)
中间件的类型
全局中间件
路由中间件
定义全局中间件
每一个请求
到达服务端之后 都会执行全局中间件函数
声明中间件函数:
1 | let recordMiddleware = function(request,response,next){ |
应用中间件:
1 | app.use(recordMiddleware) |
声明时可以直接将匿名函数传递给 use
:
1 | app.use(function (request, response, next) { |
多个全局中间件
express 允许使用 app.use() 定义多个全局中间件
1 | app.use(function (request, response, next) { |
定义路由中间件
如果只需要对某一些路由进行功能封装,则就需要路由中间件
调用格式如下:
1 | app.get('/路径',`中间件函数`,(request,response)=>{ |
静态资源中间件
express 内置处理静态资源的中间件
1 | //引入express框架 |
注意事项:
- index.html 文件为默认打开的资源
- 如果静态资源与路由规则同时匹配,谁先匹配谁就响应
- 路由响应动态资源,静态资源中间件响应静态资源
获取请求体数据
1 | const express = require('express'); |
Router
什么是 Router
express 中的 Router 是一个完整的中间件和路由系统,可以看做是一个小型的 app 对象。
Router 作用
对路由进行模块化,更好的管理路由
Router 使用
创建独立的 JS 文件(homeRouter.js)
1 | //1. 导入 express |
主文件
1 | const express = require('express'); |
EJS 模板引擎
什么是模板引擎
模板引擎是分离 用户界面和业务数据 的一种技术
什么是 EJS
EJS 是一个高效的 Javascript 的模板引擎
官网: https://ejs.co/
中文站: https://ejs.bootcss.com/
EJS 初体验
下载安装EJS
1 | npm i ejs --save |
代码示例
1 | //1.引入ejs |
命令行下运行
EJS 常用语法
执行JS代码
1 | <% code %> |
输出转义的数据到模板上
1 | <%= code %> |
输出非转义的数据到模板上
1 | <%- code %> |
在express中使用ejs
1 | // 导入 express |
views/home.ejs:
1 | <!DOCTYPE html> |
express-generator
可使用应用程序生成器工具 (express-generator
) 快速创建应用程序框架。
安装:npm install -g express-generator
创建工程命令:express -e 项目名称
- -e:代表使用EJS模板引擎
MongoDB
MongoDB 是一个基于分布式文件存储的数据库,官方地址 https://www.mongodb.com/
操作语法与 JavaScript 类似,容易上手,学习成本低
Mongodb 中有三个重要概念需要掌握:
- 数据库(database) 数据库是一个数据仓库,数据库服务下可以创建很多数据库,数据库中可以存放很多集合
- 集合(collection) 集合类似于 JS 中的数组,在集合中可以存放很多文档
- 文档(document) 文档是数据库中的最小单位,类似于 JS 中的对象
Mongoose
介绍
Mongoose 是一个对象文档模型库,官网 http://www.mongoosejs.net/
作用
方便使用代码操作 mongodb 数据库
使用流程
1 | //1. 安装 mongoose |
字段类型
文档结构可选的常用字段类型列表
类型 | 描述 |
---|---|
String | 字符串 |
Number | 数字 |
Boolean | 布尔值 |
Array | 数组,也可以使用 [] 来标识 |
Date | 日期 |
Buffer | Buffer 对象 |
Mixed | 任意类型,需要使用 mongoose.Schema.Types.Mixed 指定 |
ObjectId | 对象 ID,需要使用 mongoose.Schema.Types.ObjectId 指定 |
Decimal128 | 高精度数字,需要使用 mongoose.Schema.Types.Decimal128 指定 |
字段值验证
Mongoose 有一些内建验证器,可以对字段值进行验证
必填项
1 | title: { |
默认值
1 | author: { |
枚举值
1 | gender: { |
唯一值
1 | username: { |
unique 需要
重建集合
才能有效果
CURD
数据库的基本操作包括四个,增加(create),删除(delete),修改(update),查(read)
增加
插入一条
1 | SongModel.create({ |
批量插入
1 | //1.引入mongoose |
删除
删除一条数据
1 | SongModel.deleteOne({_id:'5dd65f32be6401035cb5b1ed'}, function(err){ |
批量删除
1 | SongModel.deleteMany({author:'Jay'}, function(err){ |
更新
更新一条数据
1 | SongModel.updateOne({author: 'JJ Lin'}, {author: '林俊杰'}, function (err) { |
批量更新数据
1 | SongModel.updateMany({author: 'Leehom Wang'}, {author: '王力宏'}, function (err) { |
查询
查询一条数据
1 | SongModel.findOne({author: '王力宏'}, function(err, data){ |
批量查询数据
1 | //不加条件查询 |
条件控制
运算符
在 mongodb 不能 > < >= <= !== 等运算符,需要使用替代符号
>
使用$gt
<
使用$lt
=
使用$gte
<=
使用$lte
!==
使用$ne
1 | db.students.find({id:{$gt:3}}); id号比3大的所有的记录 |
逻辑运算
$or
逻辑或的情况
1 | db.students.find({$or:[{age:18},{age:24}]}); |
$and
逻辑与的情况
1 | db.students.find({$and: [{age: {$lt:20}}, {age: {$gt: 15}}]}); |
正则匹配
条件中可以直接使用 JS 的正则语法,通过正则可以进行模糊查询
1 | db.students.find({name:/imissyou/}); |
个性化读取
字段筛选
1 | //0:不要的字段 |
数据排序
1 | // sort 排序 |
数据截取
1 | //skip 跳过 limit 限定 |
会话控制
介绍
所谓会话控制就是 对会话进行控制
HTTP 是一种无状态的协议,它没有办法区分多次的请求是否来自于同一个客户端,无法区分用户
而产品中又大量存在的这样的需求,所以我们需要通过 会话控制 来解决该问题
常见的会话控制技术有三种:
- cookie
- session
- token
cookie
cookie 是 HTTP 服务器发送到用户浏览器并保存在本地的一小块数据
cookie 是保存在浏览器端的一小块数据
cookie 是按照域名划分保存的
简单示例:
域名 | cookie |
---|---|
www.baidu.com | a=100; b=200 |
www.bilibili.com | xid=1020abce121; hm=112411213 |
jd.com | x=100; ocw=12414cce |
cookie 的特点
浏览器向服务器发送请求时,会自动将 当前域名下
可用的 cookie 设置在请求头中,然后传递给服务器
这个请求头的名字也叫 cookie
,所以将 cookie 理解为一个 HTTP 的请求头也是可以的
cookie 的运行流程
填写账号和密码校验身份,校验通过后下发 cookie
有了 cookie 之后,后续向服务器发送请求时,就会自动携带 cookie
浏览器操作 cookie
浏览器操作 cookie 的操作,使用相对较少,大家了解即可
- 禁用所有 cookie
- 删除 cookie
- 查看 cookie
cookie 的代码操作
express 中可以使用 cookie-parser
进行处理
1 | const express =require('express'); |
不同浏览器中的 cookie 是相互独立的,不共享
session
session 是保存在 服务器端的一块儿数据,保存当前访问用户的相关信息
实现会话控制,可以识别用户的身份,快速获取当前用户的相关信息
session 运行流程
填写账号和密码校验身份,校验通过后创建 session 信息
,然后将 session_id
的值通过响应头返回给浏览器
有了cookie,下次发送请求时会自动携带cookie,服务器通过 cookie
中的 session_id
的值确定用
户的身份
session 的代码操作
express 中可以使用 express-session
对 session 进行操作
1 | const express = require('express'); |
session 和 cookie 的区别
cookie 和 session 的区别主要有如下几点:
- 存在的位置
- cookie:浏览器端
- session:服务端
- 安全性
- cookie 是以明文的方式存放在客户端的,安全性相对较低
- session 存放于服务器中,所以安全性
相对
较好
- 网络传输量
- cookie 设置内容过多会增大报文体积, 会影响传输效率
- session 数据存储在服务器,只是通过 cookie 传递 id,所以不影响传输效率
- 存储限制
- 浏览器限制单个 cookie 保存的数据不能超过
4K
,且单个域名下的存储数量也有限制 - session 数据存储在服务器中,所以没有这些限制
token
token
是服务端生成并返回给 HTTP 客户端的一串加密字符串, token
中保存着用户信息
token 不属于 http 标准,完全由前后端协商而定,但 cookie 属于 http 标准
token 的工作流程
填写账号和密码校验身份,校验通过后响应 token,token 一般是在响应体中返回给客户端的
后续发送请求时,需要手动
将 token 添加在请求报文中(cookie是自动携带的),一般是放在请求头中
token 的特点
服务端压力更小
- 数据存储在客户端
相对更安全
- 数据加密
- 可以避免 CSRF(跨站请求伪造)
扩展性更强
- 服务间可以共享
- 增加服务节点更简单
JWT
JWT(JSON Web Token )是目前最流行的跨域认证解决方案,可用于基于 token
的身份验证
JWT 使 token 的生成与校验更规范
我们可以使用 jsonwebtoken 包
来操作 token
1 | //导入 jsonwebtokan |
本地域名
所谓本地域名就是 只能在本机使用的域名,一般在开发阶段使用
操作流程
编辑文件 C:\Windows\System32\drivers\etc\hosts
1 | 127.0.0.1 www.baidu.com |
如果修改失败,可以修改该文件的权限
原理
在地址栏输入 域名
之后,浏览器会先进行 DNS(Domain Name System)查询,获取该域名对应的 IP 地址
请求会发送到 DNS 服务器,可以 根据域名返回 IP 地址
可以通过 ipconfig /all
查看本机的 DNS 服务器
hosts
文件也可以设置域名与 IP 的映射关系,在发送请求前,可以通过该文件获取域名的 IP 地址