Node.js
初识 Node.js 与内置模块
1. fs 文件系统模块
1.1 什么是 fs
fs 模块是 Node.js 官方提供的,用来操作文件的模块。提供了一系列的方法和属性,用来满足用户对文件的操作需求
fs.readFile()
: 用来读取指定文件的内容fs.writeFile()
: 用来向指定的文件写入内容
如果要在 JS 代码中,使用 fs 模块来操作文件,需要使用如下方式导入
1 |
|
1.2 读取指定文件的内容
fs.readFile()
的语法格式
1 |
|
- 参数1: 必选参数,字符串,表示文件的路径
- 参数2: 可选参数,表示以什么编码格式来读取文件
- 参数3: 必选参数,文件读取完成后,通过回调函数拿到读取的结果
fs.readFile()
的示例代码
1 |
|
读取成功
- err: null
- dataStr: 文件内容
读取失败
- err: 错误对象
- dataStr: undefined
判断文件是否读取成功
可以判断 err 对象是否为 null
1 |
|
1.3 向指定文件写入内容
fs.writeFile()
的语法格式
1 |
|
- 参数1: 必选参数,需要制定一个文件路径的字符串
- 参数2: 必选参数,表示要写入的内容
- 参数3: 可选参数,表示以什么编码格式来写入文件内容, 默认值是 utf8
- 参数4: 必选参数,文件写入完成后的回调函数
fs.writeFile()
的示例代码
1 |
|
- 写入成功
- err: null
- 写入失败
- err: 错误对象
判断文件是否写入成功
可以判断 err 对象是否为 null
1 |
|
1.4 处理路径问题
在使用 fs 模块操作文件时,如果提供的操作路径是以 ./ 或 ../ 开头的相对路径时,很容易出现路径动态拼接错误的问题
- 原因:代码在运行时,会以执行 node 命令时所处的目录,动态拼接出被操作文件的完整路径
- 解决:直接提供完整的路径
__dirname
表示当前文件所处目录
1 |
|
2. path 路径模块
2.1 什么是 path 路径模块
path 模块是 Node.js 官方提供的,用来处理路径的模块。提供了一系列的方法和属性,用来满足用户对路径的处理需求
path.join()
: 用来将多个路径片段拼接成一个完整的路径字符串path.basename()
: 用来从路径字符串中,将文件名解析出来
使用如下方法导入
1 |
|
2.2 路径拼接
path.join()
的语法格式
1 |
|
- 参数: …paths 字符串类型,路径片段的序列
- 返回值: 字符串
path.join()
的代码示例
1 |
|
- 今后涉及到路径的拼接操作,使用
path.join()
,不要直接使用 + 进行字符串的拼接
2.3 获取路径中的文件名
path.basename()
的语法格式
使用 path.basename()
可以获取到路径中的最后一部分,常通过该方法获取路径中的文件名
1 |
|
- path: 必选参数,表示一个路径的字符串
- ext: 可选参数,表示文件扩展名的字符串
- 返回值: 路径中的最后一部分
path.basename()
的代码示例
1 |
|
2.4 获取路径中的文件扩展名
path.extname()
的语法格式
使用 path.extname()
可以获取到路径中的扩展名部分
1 |
|
- path: 必选参数,表示一个路径的字符串
- 返回值: 扩展名字符串
path.extname()
的代码示例
1 |
|
3. http 模块
3.1 什么是 http 模块
http 模块是 Node.js 官方提供的、用来创建 web 服务器的模块。通过 http 模块提供的 http.createServer()
方法,就能方便地把一台普通的电脑,变成一台 web 服务器,从而对外提供服务
使用如下方法导入
1 |
|
3.2 创建基本 Web 服务器
1 |
|
req 请求对象
只要服务器接收到了客户端的请求,就会调用通过 server.on()
为服务器绑定的 request 事件处理函数
如果想在事件处理函数中,访问与客户端相关的数据或属性,可以使用 req 对象
res 响应对象
在服务器的 request 事件处理函数中,访问与服务器相关的数据或属性,可以使用 req 对象
3.3 根据不同 url 响应不同内容
1 |
|
模块化
1. 模块化的基本概念
1.1 什么是模块化
- 模块化是指解决一个复杂问题时,自顶向下逐层把系统划分为若干模块的过程,模块是可组合、分解和更换的单元。
- 编程领域中的模块化,就是遵守固定的规则,把一个大文件拆成独立并相互依赖的多个小模块
- 模块化可提高代码的复用性和可维护性,实现按需加载。
1.2 模块化规范
- 模块化规范是对代码进行模块化拆分和组合时,需要遵守的规则
- 如使用何种语法格式引用模块和向外暴露成员
2. Node.js 中的模块化
2.1 Node.js 中模块的分类
- 内置模块:由 Node.js 官方提供的,例如 fs, path, http 等
- 自定义模块:用户创建的每个 .js 文件,都是自定义模块
- 第三方模块:由第三方开发出来的模块,使用前需下载
2.2 加载模块
require()
方法,可以加载三类模块,只有加载自定义模块需要路径名(可以省略后缀名)- 使用
require()
加载模块时,会执行被加载模块中的代码
- 使用
2.3 模块作用域
和函数作用域类似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块作用域
可以防止全局变量污染
2.4 向外共享模块作用域中的成员
module
对象
- 每个 .js 自定义模块中都有一个
module
对象,它里面存储了和当前模块有关的信息
1 |
|
module.exports
对象
- 在自定义模块中,可以使用
module.exports
对象,将模块内的成员共享出去,供外界使用。 - 外界使用
require()
导入自定义模块时,得到的就是module.exports
指向的对象。 - 使用
require()
导入模块时,导入的结果永远以module.exports
指向的对象为准。
1 |
|
exports
对象
- 默认情况下,
exports
和module.exports
指向同一个对象。最终共享的结果,以module.exports
指向的对象为准。
2.5 Node.js 中的模块化规范
Node.js 遵循了 CommonJS 模块化规范,CommonJS 规定了模块的特性和各模块之间如何互相依赖
CommonJS 模块化规范
- 每个模块内部,
module
变量代表当前模块 module
变量是一个对象,module.exports
是对外的接口- 加载某个模块即加载该模块的
module.exports
属性,require()
用于加载模块
3. npm 与包
3.1 包
- Node.js 中的第三方模块又叫做包,由第三方个人或团队开发出来,免费供所有人使用
- 包是基于内置模块封装出来的
3.2 npm
初次装包后多了哪些文件
- node_modules 文件夹
- 用来存放所有已安装到项目中的包
- package-lock.json 配置文件
- 记录 node_modules 目录下的每一个包的下载信息,例如包名、版本号、下载地址
- 不需要手动修改,npm 会自动维护
安装指定版本的包
- 默认安装最新版本的包,可以通过
包名@版本号
指定具体版本
包的语义化版本规范
分为三位数字,以”点分十进制”形式进行定义, eg. 2.24.0
- 第 1 位: 大版本
- 第 2 位: 功能版本
- 第 3 位: Bug修复版本
3.3 包管理配置文件
npm 规定,在项目根目录中,必须提供一个叫做 package.json 的包管理配置文件。用来记录与项目有关的一些配置信息,例如
- 项目的名称、版本号、描述等
- 项目中都用到了哪些包
- 哪些包只在开发期间会用到
- 哪些包在开发和部署时都需要用到
快速创建 package.json
1 |
|
安装包
使用 npm i(nstall)
可以一次性安装所有的依赖包
- 会先读取 package.json 中的 dependencies 节点
卸载包
npm uninstall
devDependencies 节点
如果某些包只在项目开发阶段用到,项目上线后不会用到,则建议把这些包记录到 devDependencies 节点
1 |
|
与之相对,如果某些包在开发和上线都需要用到,则建议把这些包记录到 dependencies 节点
3.4 npm 换源
1 |
|
4. 模块的加载机制
4.1 优先从缓存中加载
模块第一次加载后会被缓存,即多次调用 require()
不会导致模块的代码被执行多次,提高模块加载效率
4.2 内置模块的加载机制
内置模块加载优先级最高。
4.3 自定义模块的加载机制
加载自定义模块时,路径要以 ./
或 ../
开头,否则会作为内置模块或第三方模块加载。
导入自定义模块时,若省略文件扩展名,则 Node.js 会按顺序尝试加载文件:
- 按确切的文件名加载
- 补全
.js
扩展名加载 - 补全
.json
扩展名加载 - 补全
.node
扩展名加载 - 报错
4.4 第三方模块加载
- 若导入第三方模块,Node.js 会从当前模块的父目录开始,尝试从
/node_modules
文件夹中加载第三方模块。 - 如果没有找到对应的第三方模块,则移动到再上一层父目录中,进行加载,直到文件系统的根目录。
例如,假设在
C:\Users\bruce\project\foo.js
文件里调用了require('tools')
,则 Node.js 会按以下顺序查找:
C:\Users\bruce\project\node_modules\tools
C:\Users\bruce\node_modules\tools
C:\Users\node_modules\tools
C:\node_modules\tools
4.5 目录作为模块加载
当把目录作为模块标识符进行加载的时候,有三种加载方式:
- 在被加载的目录下查找
package.json
的文件,并寻找main
属性,作为require()
加载的入口 - 如果没有
package.json
文件,或者main
入口不存在或无法解析,则 Node.js 将会试图加载目录下的index.js
文件。 - 若失败则报错
Express
1. 初识 Express
基于 Node.js 平台,快速、开放、极简的 Web 开发框架
Express 是用于快速创建服务器的第三方模块
1.1 基本使用
安装 Express:
1 |
|
创建服务器,监听客户端请求,并返回内容:
1 |
|
1.2 托管静态资源
- 通过
express.static()
方法可创建静态资源服务器,向外开放访问静态资源。 - Express 在指定的静态目录中查找文件,并对外提供资源的访问路径,存放静态文件的目录名不会出现在 URL 中
- 访问静态资源时,会根据目录的添加顺序查找文件
- 可为静态资源访问路径添加前缀
1 |
|
2. Express 路由
2.1 路由的概念
- Express 中,路由指的是客户端的请求与服务器处理函数之间的映射关系
- Express 中的路由分 3 部分组成,分别是请求的类型、请求的 URL 地址、处理函数
1 |
|
2.2 路由的使用
简单使用: 把路由直接挂载到 app 上,如上述代码所示
模块化路由: 为了方便对路由进行模块化的管理,Express不建议将路由直接挂载到 app 上,而是推荐将路由抽离为单独的模块。将路由抽离为单独模块的步骤如下:
- 创建路由模块对应的 .js 文件
- 调用
express.Router()
函数创建路由对象 - 向路由对象上挂载具体的路由
- 使用
module.exports
向外共享路由对象 - 使用
app.use()
函数注册路由模块
创建路由模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// router.js
const express = require('express')
// 创建路由对象
const router = express.Router()
// 挂载具体路由
router.get('/user/list', (req, res) => {
res.send('Get user list.')
})
router.post('/user/add', (req, res) => {
res.send('Add new user.')
})
// 向外导出路由对象
module.exports = router注册路由模块,添加访问前缀
1
2
3
4
5
6
7
8
9
10
11const express = require('express')
const router = require('./router')
const app = express()
// 注册路由模块,添加访问前缀(可选)
app.use('/api', router)
app.listen(80, () => {
console.log('http://127.0.0.1')
})app.use()
函数的作用,就是来注册全局中间件
3. Express 中间件
3.1 中间件的概念
- 中间件(Middleware),特指业务流程的中间处理环节