Django-Vue搭建个人博客:翻页与监听
6231 views, 2021/03/22 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
到错误后,可以简单粗暴打印到控制台,也可以专门做一个调试信息显示到页面上,提示你错误的原因。完善代码中可能会出错的地方(并给开发者亲切的错误反馈),就是课后作业了,可参考文档。
另外很重要的就是出错后一定要多多查看前后端的控制台,逐字逐句阅读报错信息。切记。