Node.js 如何使用MySQL进行分页
Node.js是一个类似Chrome V8 JavaScript引擎的运行时环境。Node.js是一个开源的、跨平台的后端运行时环境,可以在web浏览器之外执行。
MySQL是一个开源的关系数据库管理系统,快速、可靠、灵活和强大。无论在构建web应用程序时,MySQL和Node.js都是时尚的选择。MySQL的另一个优点是它提供了内置的分页工具支持。
什么是分页,为什么它很重要?
分页就是将数据划分为离散的块或页面。在页面上显示成千上万条记录被划分为页面的方式更具吸引力和互动性,同时对于应用程序的性能也更好。分页有助于更好的展示、更好的性能、通过避免用户被数据压倒和避免长时间的滚动来提供更好的用户体验。
我们可以做客户端分页和服务器端分页。在本文中,我们将看到一个服务器端分页的示例。
服务器端分页:
根据IBM的说法,服务器端分页适用于:
- 大数据集。
- 更快的初始页面加载。
- 对于不运行JavaScript的用户可访问性。
- 复杂的视图业务逻辑。
- 对并发更改的弹性。
服务器端分页通常在中间件(业务逻辑)代码或数据库层中完成。服务器端分页通常比客户端分页更具挑战性,但扩展性更好。
客户端分页:
当:
- 数据集较小时。
- 更快的后续页面加载。
- 完全支持排序和筛选要求(除非结果大于最大大小)。
客户端分页更容易实现,但扩展性不太好。
实现服务器端分页的步骤:
我们将查看一个服务器端分页的示例。我们将在数据库层面处理分页。使用MySQL进行分页,我们需要使用带有偏移值的LIMIT子句。LIMIT子句只检索记录的一部分。LIMIT子句的基本语法如下:
Select <column-names>
From <table-names>
Where <conditions>
LIMIT <offset>, <number_of_records>;
偏移量是可选的,默认值为0,但可以是小于数据集中记录数的任何正值。
示例应用程序: 我们将使用Node.js、Express.js、MySQL和Sequelize ORM构建一个简单的应用程序。确保您的系统上安装了Node.js和MySQL。我们将使用Visual Studio Code来开发应用程序。
创建一个项目文件夹并切换到该文件夹
mkdir PaginationExample
cd PaginationExample
初始化应用
npm init
生成 package.json 文件。
接下来,我们安装 express.js、sequelize 和 pug 用于模板。
npm install -g express sequelize pug dotenv express-paginate
我们还需要安装dotenv和express-paginate包。Express-paginate包提供了各种方法,例如href和middleware。有关函数的详细信息请参阅文档。 我们的文件夹结构如下。
node server.js
如果一切正常运行,您应该在终端中看到类似于这样的输出:
一旦你看到这条信息,你可以打开浏览器并访问链接:localhost:8000
我们已经在应用程序中进行了调用的结构化处理,我们应该直接看到一个带有记录的表格,并有一个分页选项。
应用程序代码: 我们将逐层查看应用程序代码文件。
Server.js文件: server.js是包含所有与express相关的配置和我们用于获取记录和调用服务文件的唯一路由的主文件。服务器文件具有以下代码。
// Required External modules
const express = require("express");
const path = require("path");
require("dotenv").config();
const paginate = require("express-paginate");
// Required code files
const services = require("./service/services.js");
// Application Variables
const app = express();
const port = 8000;
// Server
app.listen(port, () => {
console.log(`App running on port ${port}.`);
});
// Configuration
app.set("views", path.join(__dirname, "./views"));
app.set("view engine", "pug");
app.use("/static", express.static(
path.join(__dirname, "public")));
app.use(paginate.middleware(10, 50));
// Routes
app.get("/", (req, res) => {
const limit = req.query.limit || 10;
const offset = req.offset;
services.findRecords({
offset: offset,
limit: limit
}).then((results) => {
const pageCount = Math.ceil(results.count / limit);
res.render("paginatedTable", {
data: results.rows,
pageCount,
pages: paginate.getArrayPages(req)
(3, pageCount, req.query.page),
});
});
});
Sequelize文件: 我们将数据库配置、Sequelize模型和调用分成了三个独立的文件,以便在应用程序扩展时更易于维护。
- 服务文件保存了我们所有的Sequelize调用。
- models.js保存了我们用于查询的表结构。我们在这个示例中使用SAKILA数据库中的nicer_but_slower_film_list表。
- dbconfig.js文件保存了Sequelize对象。与文件相关的整个代码都在这个存储库中。
- Sequelize提供了一个内置方法:findAndCountAll,非常适合分页。findAndCountAll方法使用偏移量和限制参数,并根据限制和偏移量的值返回可用的总记录数和实际记录。代码如下:
dbconfig.js: dbconfig保存了Sequelize对象。创建Sequelize对象的属性来自.env文件,根据您的数据库设置。在这里我们创建了一个简单的数据库对象。
const Sequelize = require("sequelize");
module.exports = new Sequelize({
dialect: "mysql",
username: process.env.DB_USER,
password: process.env.DB_PASS,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_DATABASE,
logging: (log) => console.log("logging:", log),
});
models.js:
models.js文件中保存着我们在查询中正在使用的表的描述。它是关系数据库表的Sequelize表示。
var Sequelize = require("sequelize");
db = require("../config/dbconfig.js");
const nicer_but_slower_film_list = db.define(
"nicer_but_slower_film_list", {
FID: {
type: Sequelize.SMALLINT,
// To ensure that Sequelize
// does not use id by default
primaryKey: true,
},
title: Sequelize.STRING,
description: Sequelize.STRING,
category: Sequelize.STRING,
price: Sequelize.DECIMAL,
length: Sequelize.SMALLINT,
rating: Sequelize.ENUM("G", "PG", "PG-13", "R", "NC-17"),
actors: Sequelize.STRING,
},
{
// This is to ensure that Sequelize
// does not pluralize table names
freezeTableName: true,
// This is to ensure that Sequelize
// does not add its own timestamp
// variables in the query.
timestamps: false,
createdAt: false,
updatedAt: false,
}
);
module.exports = nicer_but_slower_film_list;
services.js: services.js文件保存我们执行的Sequelize调用。这个文件将保存搜索、创建、更新、删除等调用。该文件依赖于Sequelize对象(dbconfig.js)和Sequelize模型(models.js)。
const Sequelize = require("sequelize");
// Model file
var model = require("../models/models.js");
// db Configuration
db = require("../config/dbconfig.js");
let findRecords = async (req, res) => {
return model.findAndCountAll({
offset: req.offset,
limit: req.limit
});
};
module.exports = { findRecords: findRecords };
那么,如果我们设置的限制是10,偏移量是20(即第3页的记录),那么通过findAndCountAll方法形成的查询并在数据库中触发的查询是:
SELECT FID
, title
, description
, category
, price
, length
, rating
, actors
FROM nicer_but_slower_film_list
AS nicer_but_slower_film_list
LIMIT 20, 10;
查询结果如下:
屏幕上显示的输出如下:
应用程序用户界面: 除了上述提到的文件外,项目结构还有node_modules文件夹、node和express安装文件以及.env文件。.env文件包含与数据库相关的信息,如用户名、密码、MySQL端口号等,在dbconfig.js文件中使用这些信息来构建Sequelize连接对象。
用户界面: 为了处理用户界面,我们使用PUG模板。我们使用express-paginate方法来处理PUG模板中的分页控制。以下代码处理是否显示上一个和下一个按钮链接。
paginatedTable.pug: 这是带有分页结果的用户界面。
html
head
link(rel='stylesheet' href='https://getbootstrap.com/docs/4.4/dist/css/bootstrap.min.css')
style
include ../public/style.css
body
h1 Movies
table
thead
tr
th Title
th Description
th Category
th Length
th Rating
th Actors
tbody
each dat in data
tr
td #{dat.title}
td #{dat.description}
td #{dat.category}
td #{dat.length}
td #{dat.rating}
td #{dat.actors}
if paginate.hasPreviousPages || paginate.hasNextPages(pageCount)
.navigation.well-sm#pagination
ul.pager
if paginate.hasPreviousPages
a(href=paginate.href(true)).prev
i.fa.fa-arrow-circle-left
| Previous
if pages
each page in pages
a.btn.btn-default(href=page.url)= page.number
if paginate.hasNextPages(pageCount)
a(href=paginate.href()).next
| Next
i.fa.fa-arrow-circle-right
script(src='https://code.jquery.com/jquery-3.4.1.slim.min.js')
script(src='https://getbootstrap.com/docs/4.4/dist/js/bootstrap.bundle.min.js')
hasPrevious和hasNext是由express-paginate包公开的两个方法,它们返回布尔值。根据这些布尔值的值,UI显示下一页和上一页按钮。
style.css
页面的样式表如下所示:
table {
width: 100%;
border: 1px solid #fff;
border-collapse: collapse;
border-radius: 8px;
}
th,
td {
text-align: left;
text-transform: capitalize;
border: 1px solid darkgrey;
color: black;
}
th {
padding: 8px 10px;
height: 48px;
background-color: #808e9b;
}
td {
padding: 6px 8px;
height: 40px;
}
a:hover {
background-color: #555;
}
a:active {
background-color: black;
}
a:visited {
background-color: #ccc;
}
应用程序如何工作?
- 第一次访问URL localhost:8000时,由于express-paginate中间件,默认情况下,Limit值设置为10,offset设置为0。因此,从数据库中检索出并显示前十条记录。
- 当用户点击下一个按钮或页面编号(1、2或3)时,paginate中间件会计算偏移量。计算偏移的公式很简单:
pageNumber(we see in the URL on the UI) -1 * limit
pageNumber从1开始。
- 我们还可以将限制增加到50条记录。由于我们在中间件函数中指定了50条记录作为最大限制,所以我们不能进一步增加限制。限制已经在server.js文件中设置。
app.use(paginate.middleware(10, 50));
触发类似这样的查询:http://localhost:8000/?page=1&limit=500 不会导致错误,但显示的记录数量仍将是50条。我们还可以增强功能,显示一些只能同时看到50条记录的消息。
总结: 本文介绍了如何使用Node.js和MySQL以及来自MySQL的示例数据库来实现分页功能。我们还看到了如何限制用户在页面上看到一定数量的记录,以避免造成UI的干扰。