分页查询常见3种思路
- 客户端分页:直接获取所有数据返回给客户端,客户端通过展示组件进行数据分页控制。
- 数据库分页:数据库查询时,返回一页的数据以及总记录数。
- 服务器分页:从数据库获取所有数据服务器进行缓存,每次返回一页数据给客户端。
数据库分页
这就意味着,每次客户端切换页码时,都要查询数据库获取一页数据。同时,客户端也需要知道记录总数,以便分页组件得到页数。以下列出具体代码,详情在注释中解释。
客户端
-
分页组件:
pkk-pagination.vue 组件代码:[ 太长了,贴在最后 ] -
使用组件代码:
- 第一条结果在data.list[0]
- 第二条结果在data.list[1],sql中count(0)取了别名total,所以结果总数在data.list[1][0].total
<!-- 使用组件,省略其他模板代码 -->
<PkkPagination
@current-change="changePager"
:total="total"
:currentPage="reqParams.page"
/>
<!-- 使用组件,省略其他模板代码 -->
<!-- Vue3 的Setup部分 -->
setup() {
// 筛选条件准备
const reqParams = reactive({
page: 1,
pageSize: 12,
});
const productList = ref([]); //记录所有商品信息
const total = ref(0); //记录总条数
watch(
reqParams,
async () => {
const data = await getProductByCateId(
1001,
reqParams.page,
reqParams.pageSize
);
//服务器返回两条结果,由数组包裹,具体log一下查看返回的结果
//第一条结果在data.list[0]
//第二条结果在data.list[1],sql中count(0)取了别名total,所以结果总数在data.list[1][0].total
productList.value = data.list[0];
total.value = data.list[1][0].total;
},
{ immediate: true }
);
// 改变分页函数,接收子组件传来的参数:当前页码
const changePager = (np) => {
// 改变页数
reqParams.page = np;
};
return { reqParams, productList, total, changePager };
}
- axios请求代码:
- 此处已封装了axios,暴露出了request,封装代码贴在后面
- 传递数据库分页所需参数
- cate_id:是某一分类id,作为where条件筛选
- curr_page:是当前页
- page_size:是每页数量
- 数据库查询中limit n,m :n是起始记录数,m是获取多少条,n = (curr_page-1)*page_size
/**
* 获取所有商品信息
* 此处已封装了axios,暴露出了request,封装代码贴在后面
* 传递数据库分页所需参数
* cate_id:是某一分类id,作为where条件筛选
* curr_page:是当前页
* page_size:是每页数量
* 数据库查询中limit n,m :n是起始记录数,m是获取多少条,n = (curr_page-1)*page_size
*/
export const getProductByCateId = (cate_id,curr_page,page_size) => {
return request('/category/products', 'get',{cate_id,curr_page,page_size})
}
服务器(nodejs、express)、数据库
-
设置接口:‘/category/products’
-
categroy_dao.js 关于数据库的请问文件
- 定义两次数据库请求
- 将多个Promise实例包装称一个新的Promise实例,返回一个res
/**
*
* @param {*} cateId 类目id
* @param {*} currPage 当前页码
* @param {*} pageSize 每页数量
* @returns
*/
static queryProdsByCateId(cateId,currPage,pageSize){
let start = (currPage-1)*pageSize; //计算起始记录数
//使用模板字符串插入变量
//需要两次数据库查询,写两条sql语句
let sql = `SELECT * FROM pin_product WHERE pro_category_id=${cateId} limit ${start},${pageSize};`;
let sql2 = `SELECT COUNT(*) total FROM pin_product WHERE pro_category_id=${cateId};`;
//定义第一次数据库请求
const P1 = new Promise((resolve, reject) => {
this.query(sql).then(res => {
resolve(res)
}).catch(err=>{
reject(err)
})
})
//定义第二次数据库请求
const P2 = new Promise((resolve, reject) => {
this.query(sql2).then(res => {
resolve(res)
}).catch(err=>{
reject(err)
})
})
//将多个Promise实例包装称一个新的Promise实例,返回一个res
return Promise.all([P1,P2])
}
- cetegory.js 关于数据处理的文件
- 客户端将会收到一个数组,成员包含两个Promise返回的数据,{list: Array(2)}
/* 获取某一分类的所有商品 */
static async getProdsByCateId(req,res){
let cateId = parseInt(req.query.cate_id);
let currPage = parseInt(req.query.curr_page);
let pageSize = parseInt(req.query.page_size);
//返回数据给客户端
//客户端将会收到一个数组,成员包含两个Promise返回的数据,{list: Array(2)}
let result = await this.queryProdsByCateId(cateId,currPage,pageSize);
res.json({list:result})
}
附页代码
pkk-pagination.vue 组件代码
<template>
<div class="pkk-pagination">
<a v-if="myCurrentPage <= 1" href="javascript:;" class="disabled">上一页</a>
<a @click="changePage(myCurrentPage - 1)" v-else href="javascript:;"
>上一页</a
>
<span v-if="pager.start > 1">...</span>
<a
@click="changePage(i)"
href="javascript:;"
:class="{ active: i === myCurrentPage }"
v-for="i in pager.btnArr"
:key="i"
>{{ i }}</a
>
<span v-if="pager.end < pager.pageCount">...</span>
<a
v-if="myCurrentPage >= pager.pageCount"
href="javascript:;"
class="disabled"
>下一页</a
>
<a @click="changePage(myCurrentPage + 1)" v-else href="javascript:;"
>下一页</a
>
</div>
</template>
<script>
import { ref, computed, watch } from "vue";
export default {
name: "PkkPagination",
props: {
total: {
type: Number,
default: 100,
},
currentPage: {
type: Number,
default: 1,
},
pageSize: {
type: Number,
default: 10,
},
},
setup(props,{emit}) {
//总条数
const myTotal = ref(100);
//每页条数
const myPageSize = ref(10);
//当前第几页
const myCurrentPage = ref(5);
//按钮个数
const btnCount = 5;
const pager = computed(() => {
//计算总页数
const pageCount = Math.ceil(myTotal.value / myPageSize.value);
// 计算起始页码和结束页码
// 1. 理想情况根据当前页码,和按钮个数可得到
let start = myCurrentPage.value - Math.floor(btnCount / 2);
let end = start + btnCount - 1;
// 2.1 如果起始页码小于1了,需要重新计算
if (start < 1) {
start = 1;
end =
start + btnCount - 1 > pageCount ? pageCount : start + btnCount - 1;
}
// 2.2 如果结束页码大于总页数,需要重新计算
if (end > pageCount) {
end = pageCount;
start = end - btnCount + 1 < 1 ? 1 : end - btnCount + 1;
}
// 处理完毕start和end得到按钮数组
const btnArr = [];
for (let i = start; i <= end; i++) {
btnArr.push(i);
}
return { pageCount, start, end, btnArr };
});
//改变页码
const changePage = (newPage) => {
if (myCurrentPage.value !== newPage) {
myCurrentPage.value = newPage;
// 通知父组件最新页码
emit("current-change", newPage);
}
};
// 监听传入的值改变
watch(
props,
() => {
myTotal.value = props.total;
myPageSize.value = props.pageSize;
myCurrentPage.value = props.currentPage;
},
{ immediate: true }
);
return { pager, myCurrentPage, changePage };
},
};
</script>
<style scoped lang="less">
.pkk-pagination {
display: flex;
justify-content: center;
padding: 30px;
> a {
display: inline-block;
padding: 5px 10px;
border: 1px solid #e4e4e4;
border-radius: 4px;
margin-right: 10px;
&:hover {
color: @pkkColor;
}
&.disabled {
cursor: not-allowed;
opacity: 0.4;
&:hover {
color: #333;
}
}
&.active {
background: @pkkColor;
color: #fff;
border-color: @pkkColor;
}
}
> span {
margin-right: 10px;
}
}
</style>
axios封装代码
// 1. 创建一个新的axios实例
// 2. 请求拦截器,如果有token进行头部携带
// 3. 响应拦截器:1. 剥离无效数据 2. 处理token失效
// 4. 导出一个函数,调用当前的axsio实例发请求,返回值promise
import axios from 'axios'
import store from '@/store'
import router from '@/router'
// 导出基准地址,原因:其他地方不是通过axios发请求的地方用上基准地址
export const baseURL = 'http://localhost:3000/'
const instance = axios.create({
// axios 的一些配置,baseURL timeout
baseURL,
timeout: 10000
})
instance.interceptors.request.use(config => {
// 拦截业务逻辑
return config
}, err => {
return Promise.reject(err)
})
// res => res.data 取出data数据,将来调用接口的时候直接拿到的就是后台的数据
instance.interceptors.response.use(res => res.data, err => {
return Promise.reject(err)
})
// 请求工具函数
export default (url, method, submitData) => {
// 负责发请求:请求地址,请求方式,提交的数据
return instance({
url,
method,
// 1. 如果是get请求 需要使用params来传递submitData ?a=10&c=10
// 2. 如果不是get请求 需要使用data来传递submitData 请求体传参
// [] 设置一个动态的key, 写js表达式,js表达式的执行结果当作KEY
// method参数:get,Get,GET 转换成小写再来判断
// 在对象,['params']:submitData ===== params:submitData 这样理解
[method.toLowerCase() === 'get' ? 'params' : 'data']: submitData
})
}