Node.js-2
CommonJS模块化规范
模块化指自上而下把系统划分成若干模块的过程。
优点:复用性,可维护性,按需加载。
模块的分类
- 内置模块,由Node.js官方提供;
- 自定义模块,用户创建的每一个js文件都是自定义模块;
- 第三方模块,使用前需先下载。
加载模块
使用require()方法加载模块,加载模块时会执行模块中的代码
1 | const fs = require('fs'); |
模块作用域
与函数作用域类似,在自定义模块中定义的变量、方法等只能在当前模块使用。
module对象
每个自定义模块中都有一个module对象,里面存储了和当前模块有关的信息,其中exports为暴露的对象,通过exports可以使模块内的成员被外界访问到,导入的结果永远以module.exports指向的对象为准。
1 | const a = 'a'; |
exports对象
默认情况下,exports与module.exports指向同一个对象,最终结果依旧以module.exports为准。
但是,不能直接给exports赋值一个对象,需要对他的属性赋值。
1 | export.a = 'a'; // 正确 |
注意
默认情况下,exports与module.exports指向同一个对象,当直接添加属性时,会在原有的内存里添加属性,但如果对module.exports重新赋值一个对象,就会在内存中开辟一个新的空间存放赋值的属性与方法,原来对其属性的赋值都无效,也不再与exports指向一个对象。
为防止混乱,不要在同一个模块中同时使用exports与module.exports。
一个JS模块在node环境中执行时,是被包裹在一个内置函数中执行的,格式如下
1 | function (exports, require, module, __filename, __dirname){} |
node.js默认支持CommonJS,但浏览器不支持,因此要在浏览器端运行需要经过编译。
xxxxxxxxxx const express = require(‘express’);const app = express();app.get(‘/‘, (req, res) => { // 从客户端得到函数的名称 const fnname = req.query.fnname; // 定义要发送到客户端的数据对象 const data = { name: ‘jsonp’, age: 18 } // 拼接出一个函数的调用 const fn = ${fnname}(${JSON.stringify(data)}); res.send(fn);})js
const express = require('express'); const app = express();app.get('/', (req, res) => { // 从客户端得到函数的名称 const fnname = req.query.fnname; // 定义要发送到客户端的数据对象 const data = { name: 'jsonp', age: 18 } // 拼接出一个函数的调用 const fn = `${fnname}(${JSON.stringify(data)})`; res.send(fn); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38- `package.json`用来记录每个包的下载信息,名字、版本号、下载地址等。
- `npm init -y`快速创建`package.json`文件。
- **dependencies节点:**用来记录使用安装了哪些包(**核心依赖包**)。
- 使用`npm i`命令一次性安装所有dependencies中记录的包。
- **devDependencies节点:**记录只在项目开放阶段使用,上线后不用的包(**开发依赖包**),安装时在命令中加上`-D`(等价于`--save-dev`)即可。
- 在执行`npm install`命令时,若添加了`-g`参数,则会安装为全局包。
### 模块的加载机制
- 模块在第一次加载后会被缓存,多次调用require()不会导致模块中的代码执行多次。
- **内置模块**加载优先级最高。
- 加载自定义模块路径必须为相对路径,否则会被当做内置模块或第三方模块加载。
## Express
**Express**是基于Node.js平台,快速、开放、极简的**Web开发平台**,与http模块类似。
对前端来说,最常见的两种服务器分别是**Web网站服务器**、**API接口服务器**。
```js
const express = require('express');
// 创建服务器
const app = express();
app.get('/',(req, res) => {
// 把内容响应给客户端
res.send("666,get成功");
console.log(req.query);
})
app.post('/user', (req, res) => {
res.send("666,post成功");
})
app.listen(8000, ()=>{
console.log('server started');
})req.query可以访问客户端通过查询字符串的方式发送的参数,即URL中“?”后的参数,格式为
?username=abc&age=18。req.params可以访问URL中通过“:”匹配到的动态参数。
res.send()将内容响应给客户端,必须存在,否则请求会一直处于待处理状态。
客户端
1
2
3
4
5
6
7
8axios.get('http://localhost:8000/paramsname/paramsage',{
params: {
name:'queryname'
}
}).then((response) => {
console.log(response.data);
})
// 请求成功,输出 666,get成功服务端
1
2
3
4
5
6
7app.get('/:name/:age', (req, res) => {
res.send("666,get成功");
console.log("paramsName = " + req.params.name);
console.log("paramsAge = " + req.params.age);
console.log("queryName = " + req.query.name);
})
// 响应成功,输出 paramsName = paramsname paramsAge = paramsage queryName = query
托管静态资源
**express.static()**可以创建一个静态资源服务器,通过如下代码即可将public文件夹中的图片、css文件、js文件对外开放访问。
1 | app.use(express.static('./public')); |
Express在指定的目录中查找文件,并对外提供访问路径,因此存放文件的目录名不会出现在URL中。
若要托管多个静态资源目录,可以多次调用express.static()函数,访问文件时会按顺序查找。
挂载路径前缀
1 | app.use('/public', express.static('./public')) |
在访问静态资源时需带有/public前缀
如:http://localhost:8000/public/img/bg.jpg,http://localhost:8000/public/css/index.css
Express 路由
路由指客户端的请求与服务器处理函数之间的映射关系。
模块化路由
为方便对路由的管理,推荐将路由抽离为单独的模块。
1 | // 路由模块 route1.js |
1 | // 主文件中 |
为路由模块添加前缀,app.use('/api', route1),添加统一的访问前缀 /api。
Express 中间件
当一个请求到达Express的服务器后,可以连续调用多个中间件对这次请求进行预处理,最终结果通过路由响应给客户端。
中间件本质上是个函数,格式如下:
1 | app.get('/', (req, res, next) => { |
中间件必须包含next参数,且必须在最后调用**next()**。
next函数表示把流转关系转交给下一个中间件或路由。
客户端发起的任何请求,到达服务器后都会触发的中间叫做全局生效的中间件,格式为app.use((req, res, next) => {})。
多个中间件之间共享一份req,res,可以在上游的中间件中添加自定义属性或方法供下游使用。
局部生效的中间件,格式为app.get('/', fn(), (req, res) => {...})、app.get('/', fn1(), fn2() (req, res) => {...})或app.get('/', [fn1(), fn2()], (req, res) => {...}),其中fn、fn1、fn2为中间件。
中间件分类
应用级别的中间件,绑定到app实例上的中间件。
路由级别的中间件,绑定到express.Router()实例上的中间件。
错误级别的中间件专门用来捕获项目中的错误,形参为
(err, req, res),错误级别的中间件必须注册在所有路由之后。1
2
3
4
5// 错误中间件
app.use((err, req, res) => {
console.log(err.message);
res.send(err.message);
})Express内置的中间件,包括
express.static、express.json、express.urlencoded1
2
3
4// 解析JSON格式的请求体数据
app.use(express.json());
// 解析URL/encoded格式的请求体数据
app.use(express.urlencoded({ extended: false}));req.body用来接收客户端发送的请求体数据,若不配置解析表单数据的中间件则默认为undefined。1
2
3
4// 使用axios发送urlencoded格式的数据
const params = new URLSearchParams('name=abc&age=18');
params.append('height', 180); // 添加数据
axios.post('http://localhost:8000', params).then(res => {})第三方的中间件
自定义中间体
手写一个将JSON数据解析为对象的中间件,类似于express.json。
首先编写实现代码
1 | const express = require('express'); |
注意事项
next()一定要写在监听end事件函数中,因为监听事件是异步任务,如果将next()写在外面会直接执行next(),直到同步任务执行完毕才会执行监听事件,导致数据未被处理。
封装
1 | // jsonparser.js |








