2025-03-29 14:35:49 +08:00

13 KiB
Executable File
Raw Blame History

Node开发框架——Express

注意项:

  • 引入自定义模块要用相对路径, 如./

简介

官网: https://www.expressjs.com.cn/

Express是一个Node.js Web 应用程序开发框架 最肤浅理解: 路由框架

核心特性:

  • 路由: 定义了路由表用于执行不同的 HTTP 请求动作。
  • 中间件: 本质是一个函数, 在收到请求和返回响应的过程中进行处理
  • 模版引擎: ......

类似的框架

fastify koa koa2

使用

//初始化, 创建package.json文件
npm init -y

//安装express到项目
npm i express -S
//引入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()
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()函数加载路由器级中间件。

// 创建路由对象
const router = express.Router()

router.get('/', function (req, res) {
  res.send('hello, user!')
})

//安装路由
app.use(router)

错误处理中间件

//错误处理中间件始终带有四个参数, 不能省略
app.use(function (err, req, res, next) {
  console.error(err.stack)
  res.status(500).send('Something broke!')
})
//要写在路由安装之后

内置中间件

Express具有以下内置的中间件功能

  • express.static : 提供静态资产例如HTML文件图像等。
// 中间件有顺序, 哪个在前, 哪个先生效
//实现静态页面的自动访问,它会默认打开views下面的index.html
app.use('/', express.static(path.join(__dirname, 'views')))

// 响应静态资源
app.use(express.static('public'))
  • express.json: 。它使用JSON有效负载分析传入请求并基于body-parser

    app.use(bodyParser.json())
    
    //接受一个对象或数组并在发送之前将其转换为JSON
    res.json({ code: 204, msg: '注册失败' })
    

第三方中间件

通过使用第三方中间件从而为Express应用增加更加功能

安装所需要功能的node模块,并在应用中加载,可以在应用级加载,也可以在路由级加载。

  • bodyParse: 处理程序之前,在中间件中对传入的请求体进行解析
//先安装再使用
npm i body-parser -S
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接收

路由

参数:

  • 请求方式:

    app.get()
    app.post()
    app.put()
    app.delete()
    app.all()
    
  • 请求路径: 只匹配对应路径的请求进行处理

    app.get('/', function (req, res) {
      res.send('只响应根路由/')
    })
    
    app.get('/random.text', function (req, res) {
      res.send('只响应/random.text')
    })
    
  • 处理方法:

//单个回调函数
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

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

// 引入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

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

其中: 前台模板引擎 express-art-template 介绍 - art-template

使用步骤:

  • 下载安装
    • 先下载art-template:npm i art-template -S
    • 再下载express-art-template: npm i express-art-template -S
  • 给express框架设置默认的模板引擎
// 注册模板引擎
app.engine('html', require('express-art-template')); //第一个参数是渲染文件的格式
app.set('view options', {
  debug: process.env.NODE_ENV !== 'production' //设置模板引擎的使用环境, 开发和上线都要使用
});
  • 设置模板引擎文件的默认路径
// 如果不设置默认也是views
app.set('views',path.join(__dirname, './views')
app.set('view engine', 'html') //设置模板文件的类型

示例如下:

// 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

<!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

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)
        }
    })
})

简洁写法

//参数查询
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)
     }
 })