Django-Vue搭建个人博客:翻页与监听
7546 views, 2023/10/16 updated Go to Comments
上一章的详情页面跳转,用到了 vue-router 动态匹配路由的能力。而翻页功能通常不会直接改变当前路由,而是修改 url 中的查询参数来实现。它两的区别如下:
# 改变路由
https://abc.com/2
# 改变查询参数
http://abc.com/?page=2
这一点微妙的区别就导致翻页的实现方式和详情页跳转不太一样。
本章实现的是文章列表的翻页,所有内容均在
ArticleList.vue
中完成。
模板
模板改动如下:
<!-- frontend/src/components/ArticleList.vue -->
<template>
...
<div id="paginator">
<span v-if="is_page_exists('previous')">
<router-link :to="{ name: 'Home', query: { page: get_page_param('previous') } }">
Prev
</router-link>
</span>
<span class="current-page">
{{ get_page_param('current') }}
</span>
<span v-if="is_page_exists('next')">
<router-link :to="{ name: 'Home', query: { page: get_page_param('next') } }">
Next
</router-link>
</span>
</div>
</template>
...
这里面实际上只用到了两个新的方法(马上要写):
is_page_exists(...)
用于确认需要跳转的页面是否存在,如果不存在那就不渲染对应的跳转标签。它的唯一参数用于确定页面的方向(当前页、上一页或下一页?)。get_page_param(...)
用于获取页码,它的参数作用也类似,基本上就是个标记。
router-link 通过 query 传递参数,看来还是挺方便的。
脚本
脚本部分是本章的核心内容,看仔细了:
<!-- frontend/src/components/ArticleList.vue -->
...
<script>
import axios from 'axios';
export default {
name: ...,
data: ...,
mounted() {
this.get_article_data()
},
methods: {
formatted_time: ...,
// 判断页面是否存在
is_page_exists(direction) {
if (direction === 'next') {
return this.info.next !== null
}
return this.info.previous !== null
},
// 获取页码
get_page_param: function (direction) {
try {
let url_string;
switch (direction) {
case 'next':
url_string = this.info.next;
break;
case 'previous':
url_string = this.info.previous;
break;
default:
return this.$route.query.page
}
const url = new URL(url_string);
return url.searchParams.get('page')
}
catch (err) {
return
}
},
// 获取文章列表数据
get_article_data: function () {
let url = '/api/article';
const page = Number(this.$route.query.page);
if (!isNaN(page) && (page !== 0)) {
url = url + '/?page=' + page;
}
axios
.get(url)
.then(response => (this.info = response.data))
}
},
watch: {
// 监听路由是否有变化
$route() {
this.get_article_data()
}
}
}
</script>
...
改动内容较多,让我们逐个拆解。
// 判断页面是否存在
is_page_exists(direction) {
if (direction === 'next') {
return this.info.next !== null
}
return this.info.previous !== null
},
...
这里就可以看出参数的作用了,根据参数不同确定所要查询页码的方向,返回不同的数据。
// 获取页码
get_page_param: function (direction) {
try {
let url_string;
switch (direction) {
case 'next':
url_string = this.info.next;
break;
case 'previous':
url_string = this.info.previous;
break;
default:
return this.$route.query.page
}
const url = new URL(url_string);
return url.searchParams.get('page')
}
catch (err) {
return
}
},
...
try
是为了避免潜在的取值问题(比如网速缓慢时info
还未获取到数据);一般来说catch
语句应该含有对应报错的措施,教程就略过了。switch
同样是用来控制翻页方向,有点点不同的是它默认查询了当前的页码,用于显示。- 根据翻页方向,构建
URL
对象并获取到其中的页码参数。
// 获取文章列表数据
get_article_data: function () {
let url = '/api/article';
const page = Number(this.$route.query.page);
if (!isNaN(page) && (page !== 0)) {
url = url + '/?page=' + page;
}
axios
.get(url)
.then(response => (this.info = response.data))
}
把获取数据的逻辑抽离为一个单独的方法,它根据当前的页码,向后端查询对应的数据。如果页码不存在,则返回首页。因此 mounted()
修改为调用此方法就可以了。
watch: {
// 监听路由是否有变化
$route() {
this.get_article_data()
}
}
...
这个 watch
就非常重要了,划重点。它的作用是监听 Vue 管理的数据,一旦发生变化就执行对应的方法。比如这里,我们已经知晓 this.$route
是 Vue 的路由对象了,因此将其注册到 watch
中,每当其变化(也就是 url 中的页码参数 ?page
变化了)则立即根据当前页码更新对应的文章数据。
你可能会问,既然首页的文章数据是在页面初始化时通过 mounted()
加载的,那为什么翻页后的数据不在 mounted()
中更新?很遗憾这是不行的。因为参数变化在 vue-router 看来不算是真正的路径变化,因此不会触发 mounted()
这类生命周期钩子。
关于 watch 更多内容请见文档。
样式
样式改动部分如下:
<!-- frontend/src/components/ArticleList.vue -->
...
<style scoped>
...
#paginator {
text-align: center;
padding-top: 50px;
}
a {
color: black;
}
.current-page {
font-size: x-large;
font-weight: bold;
padding-left: 10px;
padding-right: 10px;
}
</style>
CSS 样式通常都很直白,没有多少可讲的知识点,因此这里就简单贴出来。
若你有更好看的外观解决方案,大胆的更改,通常样式不影响功能,只影响个人审美。
测试
完成之后,启动前后端服务器,看一眼成果:
注意看页面和 url
是如何发生变化的。
课后作业
新手写代码时 90% 的错误都是低级的,比如忘记启动后端服务器、忘记配置参数、没注意跨域问题等等之类的。那么如果开发时页面没获取到后端数据,一片空白通常让人无所适从。
本章用到的 try...catch
捕获代码中可能出现的错误,类似的能力 axios
也有:
axios
.get('https://xxx.com/api/...')
.then(...)
.catch(error => console.log(error))
.catch
到错误后,可以简单粗暴打印到控制台,也可以专门做一个调试信息显示到页面上,提示你错误的原因。完善代码中可能会出错的地方(并给开发者亲切的错误反馈),就是课后作业了,可参考文档。
另外很重要的就是出错后一定要多多查看前后端的控制台,逐字逐句阅读报错信息。切记。