579 lines
13 KiB
Markdown
Executable File
579 lines
13 KiB
Markdown
Executable File
# 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. 前端耗时少<br>2. 有利SEO<br>3. 客户端速度更快, 耗电更少 |1. 不利于前后端分离, 开发效率低 <br/>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
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Document</title>
|
||
</head>
|
||
|
||
<body>
|
||
<ul>
|
||
{{each list}}
|
||
<li>{{$value.name}} -- {{$value.age}}</li>
|
||
{{/each}}
|
||
</ul>
|
||
<!--
|
||
//后台的模板不是使用script标签来创建模板
|
||
整个页面就是模板
|
||
-->
|
||
<form action="">
|
||
用户名: <input type="text"> <br>
|
||
密码 <input type="text"> <br>
|
||
<button>登陆</button>
|
||
</form>
|
||
</body>
|
||
|
||
</html>
|
||
```
|
||
|
||
## node操作mysql
|
||
|
||
```js
|
||
const express = require('express')
|
||
// 导入MySQL模块
|
||
const mysql = require('mysql');
|
||
|
||
const app = express()
|
||
|
||
app.listen('3008', () => {
|
||
console.log('http://127.0.0.1:3008')
|
||
})
|
||
|
||
// 创建一个数据库连接, 用createPool不用createConnection
|
||
var connection = mysql.createPool({
|
||
host: '127.0.0.1', // 数据库服务器的地址
|
||
user: 'root', // 数据库的账号
|
||
password: 'root', // 数据库的密码
|
||
database: 'mybase58' // 数据库名称, 不是表的名称
|
||
});
|
||
|
||
// 连接数据库或打开数据库
|
||
//connection.connect();
|
||
//不用手动打开链接
|
||
|
||
app.get('/', (req, res) => {
|
||
//查
|
||
// let sql = `select * from users`
|
||
|
||
//增
|
||
// let obj = {
|
||
// name: '赵六',
|
||
// age: '26',
|
||
// address: '赵家村'
|
||
// }
|
||
|
||
// let sql = `insert into users (name, age, address) value ('${obj.name}', '${obj.age}', '${obj.address}')`
|
||
//注意: value里面的字符串要主动加引号
|
||
//除了查询, 增删改的fields没有定义
|
||
//查询返回的是所有的数据, 增删改返回的是okPacket
|
||
|
||
//改
|
||
// let obj = {
|
||
// id: 8,
|
||
// age: 25
|
||
// }
|
||
|
||
// let sql = `update users set age = '${obj.age}' where id = '${obj.id}'`
|
||
|
||
//删
|
||
let obj = {
|
||
name: '赵六',
|
||
}
|
||
let sql = `delete from users where name = '${obj.name}'`
|
||
|
||
connection.query(sql, (err, results, fields) => {
|
||
if (err) {
|
||
console.log(err)
|
||
throw err
|
||
} else {
|
||
console.log(results)
|
||
console.log(typeof results)
|
||
console.log('-----------------')
|
||
console.log(fields)
|
||
}
|
||
})
|
||
})
|
||
```
|
||
|
||
简洁写法
|
||
|
||
```js
|
||
//参数查询
|
||
let sql = 'select * from users where name = ?'
|
||
connection.query(sql, ['张三'], (err, results, fields) => {
|
||
if (err) {
|
||
console.log(err)
|
||
throw err
|
||
} else {
|
||
console.log(results)
|
||
}
|
||
})
|
||
|
||
|
||
//增
|
||
let obj = {
|
||
name: '赵六1212',
|
||
age: '26',
|
||
address: '赵家村'
|
||
}
|
||
let sql = `insert into users set ?`
|
||
connection.query(sql, obj, (err, results, fields) => {
|
||
if (err) {
|
||
console.log(err)
|
||
throw err
|
||
} else {
|
||
console.log(results)
|
||
}
|
||
})
|
||
|
||
|
||
//改
|
||
let obj = {
|
||
id: 5,
|
||
name: '赵六1212',
|
||
age: '27',
|
||
address: '赵家村'
|
||
}
|
||
let sql = `update users set ? where id = ?`
|
||
connection.query(sql, [obj, obj.id], (err, results, fields) => {
|
||
//第二个参数, 如果只有一个是对象, 直接写, 如果是多个参数, 加上方括号成数组
|
||
if (err) {
|
||
console.log(err)
|
||
throw err
|
||
} else {
|
||
console.log(results)
|
||
}
|
||
})
|
||
|
||
|
||
//删除
|
||
let sql = 'delete from users where id = ?'
|
||
connection.query(sql, [2], (err, results, fields) => {
|
||
if (err) {
|
||
console.log(err)
|
||
throw err
|
||
} else {
|
||
console.log(results)
|
||
}
|
||
})
|
||
let sql = 'delete from users where name = ?'
|
||
connection.query(sql, ['李八'], (err, results, fields) => {
|
||
if (err) {
|
||
console.log(err)
|
||
throw err
|
||
} else {
|
||
console.log(results)
|
||
}
|
||
})
|
||
``` |