# Node开发框架——Express
注意项:
* 引入自定义模块要用相对路径, 如`./`
## 简介
官网: https://www.expressjs.com.cn/
> Express是一个Node.js Web 应用程序开发框架
> 最肤浅理解: 路由框架
核心特性:
* **路由**: 定义了路由表用于执行不同的 HTTP 请求动作。
* **中间件**: 本质是一个函数, 在收到请求和返回响应的过程中进行处理
* **模版引擎**: ......
## 类似的框架
fastify koa koa2
## 使用
```js
//初始化, 创建package.json文件
npm init -y
//安装express到项目
npm i express -S
```
```js
//引入Express框架
const express = require('express')
//创建一个web服务器
const app = express()
//给app对象注册一个中间器,use是一个内置中间件,可以用来监听get和post请求
app.use((req, res) => res.send('Hello World!'))
//开启服务器并监听端口
app.listen(3000, ()=>{
console.log('http://127.0.0.1:300')
})
```
## 使用express-generator生成项目骨架
......
## 中间件
中间件功能可以执行以下任务:
* 执行任何代码。
* 更改请求和响应对象。
* 结束请求-响应周期。
* 调用堆栈中的下一个中间件函数。
中间件的分类:
* 应用层中间件
* 路由器级中间件
* 错误处理中间件
* 内置中间件
* 第三方中间件
### 应用层中间件
使用`use()`或者`METHOD()`绑定函数到应用程序实例app上, METHOD 包括get和post
* ==中间件是有顺序的==
* next() 放行, 触发后续的中间件. 不放行, 后面的中间件都不执行.
* next() 不能在响应处理完毕后调用. 举例: res.send() 后不能再接 next()
```js
const app = express()
//每个请求都会执行
app.use(function (req, res, next) {
console.log('Time:', Date.now())
next()
})
//访问根目录时会执行
app.use('/', (req, res, next) {
console.log('Time:', Date.now())
next()
})
```
### 路由器中间件
使用`router.use()`和`router.METHOD()`函数加载路由器级中间件。
```js
// 创建路由对象
const router = express.Router()
router.get('/', function (req, res) {
res.send('hello, user!')
})
//安装路由
app.use(router)
```
### 错误处理中间件
```js
//错误处理中间件始终带有四个参数, 不能省略
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
//要写在路由安装之后
```
### 内置中间件
Express具有以下内置的中间件功能:
* express.static : 提供静态资产,例如HTML文件,图像等。
```js
// 中间件有顺序, 哪个在前, 哪个先生效
//实现静态页面的自动访问,它会默认打开views下面的index.html
app.use('/', express.static(path.join(__dirname, 'views')))
// 响应静态资源
app.use(express.static('public'))
```
* express.json: 。它使用JSON有效负载分析传入请求,并基于body-parser
```js
app.use(bodyParser.json())
//接受一个对象或数组,并在发送之前将其转换为JSON
res.json({ code: 204, msg: '注册失败' })
```
### 第三方中间件
通过使用第三方中间件从而为Express应用增加更加功能
安装所需要功能的node模块,并在应用中加载,可以在应用级加载,也可以在路由级加载。
* bodyParse: 处理程序之前,在中间件中对传入的请求体进行解析
```js
//先安装再使用
npm i body-parser -S
```
```js
const bodyPsrse = require('body-parser')
...
//bodyParser.json是用来解析json数据格式的
app.use(bodyParser.json())
//bodyParser.urlencoded则用来解析我们通常的form表单提交的数据
app.use(bodyParser.urlencoded({
extended: false
//设置为false采用querystring库,如果设置为true那么采用qs库, 官方建议false
}))
//body-parse不支持FormData, 但post的其他数据都可以用它来接收
//get参数用express自带的req.query接收
```
## 路由
参数:
* 请求方式:
```js
app.get()
app.post()
app.put()
app.delete()
app.all()
```
* 请求路径: 只匹配对应路径的请求进行处理
```js
app.get('/', function (req, res) {
res.send('只响应根路由/')
})
app.get('/random.text', function (req, res) {
res.send('只响应/random.text')
})
```
* 处理方法:
```js
//单个回调函数
app.get('/example/a', function (req, res) {
res.send('Hello from A!')
})
//多个回调函数可以处理一条路由, 使用next()
app.get('/example/b', function (req, res, next) {
console.log('the response will be sent by the next function ...')
next()
}, function (req, res) {
res.send('Hello from B!')
})
```
## 项目
```
//1.规划项目文件夹
Demo
-data 数据
-node_modules 第三方模块
-public 公共资源
-css
-images
-js
-views 静态页面资源
-index.html 首页
-register
-app.js 程序入口
-handler.js 业务处理模块
-router.js 路由表
-package.json
-package-lock.json
//2. 引入内置模块
//3. 创建服务器, 监听端口
//4. 响应页面资源
//5. 静态资源托管
//6. 添加路由表 router.js
//7. 业务功能处理 handle.js
//8. 安装路由
```
### 程序入口 `app.js`
```js
const express = require('express')
const path = require('node:path')
const router = require('./router')
const bodyParser = require('body-parser')
let app = express()
app.listen(3002, () => {
console.log('127.0.0.1:3002')
})
// 添加body-parse解析器,使用它进行处理post方式所传递的参数
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: false
}))
//static中间函数实现静态页面的自动访问
app.use('/', express.static(path.join(__dirname, 'views')))
//响应静态资源
app.use(express.static('public'))
//安装路由功能
app.use(router)
// 路由可以安装多个, 中间件都是有顺序的
// app.use('url1', router1) // 都在url1下, 路径为url1/xxx
// app.use('url2', router1)
```
### 路由表`router.js`
```js
// 引入express
const express = require('express')
const handler = require('./handler')
// 创建路由对象
const router = express.Router()
// 添加(挂载)路由配置
router.post('/register', (req, res) => {
handler.userRegister(req, res)
})
// 暴露
module.exports = router
```
### 业务处理 `handler.js`
```js
const fs = require('node:fs/promises')
const path = require('node:path')
// 专门进行业务处理
module.exports = {
userRegister: function (req, res) {
// express封装了get请求参数的接收
// req.query 是express自带的
// 接收用户参数 以前req.on('data') req.on('end') > 参数的类型转换
// body:就是post方式所传递的参数,它是一个对象(参数的接收和参数的转换)
// console.log(req.body)
// 存储到指定的数据文件中,并响应结果
getRegister: (req,res) => {
console.log('req.body', req.body)
fs.readFile(path.resolve(__dirname, './data/users.json'),'utf-8')
.then(data=>{
let arr = JSON.parse(data)
arr.push(req.body)
return fs.writeFile(path.resolve(__dirname,'./data/users.json'),JSON.stringify(arr, null, ' '))
})
.then(result => {
res.json({code:200,msg:'注册成功'})
})
.catch(err=>{
console.log('err', err)
res.json({code:204,msg:'注册失败'})
})
}
}
```
* 重定向: res.redirect(path)
* 设置响应状态码: `res.status(404).send('404 not found')`
## 用户认证
另建笔记
## 模板引擎(了解)
服务端渲染:
| | 优势 | 劣势 |
| ------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| 服务端 | 1. 前端耗时少
2. 有利SEO
3. 客户端速度更快, 耗电更少 |1. 不利于前后端分离, 开发效率低
2. 请求多时, 对服务器造成访问压力|
折中方案: "首屏渲染", 首屏加载用服务器渲染,其他的用客户端渲染
模板引擎有很多: [Template Engines](https://expressjs.com/en/resources/template-engines.html)
其中: 前台模板引擎 express-art-template [介绍 - art-template](https://aui.github.io/art-template/zh-cn/docs/)
**使用步骤:**
* 下载安装
* 先下载art-template:`npm i art-template -S`
* 再下载express-art-template: `npm i express-art-template -S`
* 给express框架设置默认的模板引擎
```js
// 注册模板引擎
app.engine('html', require('express-art-template')); //第一个参数是渲染文件的格式
app.set('view options', {
debug: process.env.NODE_ENV !== 'production' //设置模板引擎的使用环境, 开发和上线都要使用
});
```
* 设置模板引擎文件的默认路径
```js
// 如果不设置,默认也是views
app.set('views',path.join(__dirname, './views')
app.set('view engine', 'html') //设置模板文件的类型
```
示例如下:
```js
// 1. 引入框架模块
var express = require('express')
// 2. 创建服务器
var app = express();
// 注册模板引擎
//engine就是个中间件
app.engine('html', require('express-art-template'));
app.set('view options', {
debug: process.env.NODE_ENV !== 'production'
});
// 如果不设置,默认也是views
//app.set('views','./views')
// 3. 开启服务器并监听端口
app.listen(3000,()=>{
console.log('http://127.0.0.1:3000')
})
app.get('/login',function(req,res){
// 引入express-art-template之前,会在Res上挂载一个render方法,可以实现页面的渲染
// render实现的功能有: 读取文件,动态渲染,响应, 所以不用fs去读文件
// res.render(要渲染的文件路径,[数据])
res.render('login.html', { list: [{ name: 'jack', age: 20 }, { name: 'rose', age: 18 }] })
})
```
`/views/login.html`
```html