十一、Watch 与 命令式导航
为了讲解这部分的内容,首先加入新的分页组件
封装分页组件
具体分页API,参考ElementUI Pagination
创建Pager.vue组件
<template>
<div>
<el-pagination
layout="prev, pager, next"
:page-size="limit"
:current-page="page"
:pager-count="pagerCount"
:total="total"
@current-change="handleCurrentChange">
</el-pagination>
</div>
</template>
<script>
export default {
props:["limit","page","pagerCount","total"],
methods:{
handleCurrentChange(page){
console.log(page);
this.$emit("pageChange",page);
}
}
};
</script>
<style></style>
在ChannelNews.vue中使用
没有数据的时候,先模拟数据测试一下
<template>
<div>
新闻页面,频道id是===>{{$route.params.id}}
<Pager
:total="total"
:page="page"
:limit="limit"
:pagerCount="pagerCount"
@pageChange="handlePageChange"></Pager>
</div>
</template>
<script>
import Center from '@/components/Center'
import Pager from '@/components/Pager'
export default {
components:{
Center,Pager
},
data(){
return {
total:1000,
pagerCount:11,
limit:10,
page:1
}
},
methods:{
handlePageChange(page){
console.log("page=====" + page);
}
}
}
</script>
<style>
</style>
注意:ElementUI的分页组件有个小坑,页码pager-count属性的数量只能是奇数
然后,加入新闻数据:
<template>
<div>
新闻页面,频道id是===>{{$route.params.id}}
<NewList :news="news"></NewList>
<Pager
:total="total"
:page="page"
:limit="limit"
:pagerCount="pagerCount"
@pageChange="handlePageChange"></Pager>
</div>
</template>
<script>
import Center from '@/components/Center'
import Pager from '@/components/Pager'
import NewList from '@/components/NewList'
import {getNewsList} from '@/services/NewsService'
export default {
components:{
Center,Pager,NewList
},
data(){
return {
total:1000,
pagerCount:11,
limit:10,
page:1,
news:[]
}
},
methods:{
handlePageChange(page){
console.log("page=====" + page);
},
async getData(){
let resp = await getNewsList(
this.$route.params.id,
this.page,
this.limit
);
this.news = resp.contentlist;
this.total = resp.allNum;
}
},
created() {
this.getData();
},
}
</script>
<style>
</style>
新闻列表数据是显示出来了,但是现在点击分页并不能改变新闻列表的值。
关键点是,现在点击分页页码,不可能和在created中获取的数据产生任何关联,因此代码要做出修改,应该是点击分页页面的时候去获取远程数据,并且在每次改变的时候要改变数据。
试着把获取数据的内容放到触发分页的方法中:
...前后代码省略
methods:{
handlePageChange(page){
console.log("page=====" + page);
this.getData();
},
......
}
但是现在其实还是有一个小bug,如果在url地址上输入分页信息,并不是实现分页的效果
比如输入:127.0.0.1:8080/频道id?page=3
这种形式,我们并不能捕获?page=3
后面的查询参数,要实现这个效果,我们可以使用命令式导航
命令式导航
之前我们都是通过<router-link>
标签来实现路由导航,也可以通过编码的方式实现导航
this.$router.push();
我们可以直接使用下面的代码
...其他代码省略
computed:{
page(){
return +this.$route.query.page || 1;
}
},
methods:{
handlePageChange(page){
console.log("page=====" + page);
// console.log("this.$route.query.page1=====" + this.$route.query.page);
this.$router.push("?page="+page);
// console.log("this.$route.query.page2=====" + this.$route.query.page);
this.getData();
},
...其他代码省略
}
this.$router.push("?page="+page);
这句代码的意义是:如果当前url地址不变化,就将当前url地址后面加上page变量,比如之前是 http://localhost:8080/channels/5572a108b3cdc86cf39001cd
,加上这句代码之后就会变成 http://localhost:8080/channels/5572a108b3cdc86cf39001cd?page=3
,相当于把当前页面参数传递了过去
而之前放在data()
中的变量,也直接换成了计算属性,
this.$route.query.page
这句代码的意义是,从当前路由中取出page属性的值,注意值是一个字符串,所以return +this.$route.query.page || 1;
这句代码的意义是返回当前的页码,并且转换为数字类型,如果没有,就返回1
所以这一段代码加入之后,分页已经可以正常使用了。
注意:
this.$router.push("?page="+page);
只是一种简写,如果前面的参数可能发生变化,比如每次切换频道,频道id都会发生变化,那就需要使用下面的写法
this.$router.push({
name:"Channels",
params:{
id:this.$route.params.id
},
query:{
page:page
}
})
所以,最后我们的代码可以改成下面的样子:
......其他省略
computed:{
page(){
return +this.$route.query.page || 1;
}
},
methods:{
handlePageChange(page){
this.$router.push({
name:"Channels",
params:{
id:this.$route.params.id
},
query:{
page:page
}
})
this.getData();
}
....
}
现在能点击不同的页码就行分页了,但是还有一个重要问题,初始没有,当然,同样可以在created()钩子函数中调用获取异步数据的方法,让数据在初始化的时候显示一下就可以了。
Watch 侦听属性
但是现在其实还有一个更重要的bug,我们切换不同的频道,发现频道id其实有变化,但是,新闻列表界面却没有任何变化。我们来回顾一下出现这个问题的过程
页面渲染的过程
首页 -> 国内焦点
- 页面渲染的是Home.vue组件
- 点击了“国内焦点”
- 销毁“Home.vue”组件
- 创建和加载“ChannelNews.vue”组件
国内焦点 -> 国际焦点
- 页面渲染的是“ChannelNews.vue”组件
- 点击了国际焦点
- 由于同样都是“ChannelNews.vue”组件,所以组件不会销毁和重建,当然也就不会调用
created()
方法
watch的作用可以监控一个值的变换,并调用因为变化需要执行的方法。可以通过watch动态改变关联的状态。
简单来说,就是在某个值上,绑定一个事件监听,当这个值发生了变化,就会执行相关的方法
使用方式其实和计算属性,方法差不多
...
watch:{
"监听的值":{
//设置为true表示一开始就需要监听,不然第一次没有值的时候是不会监听的
immediate:true,
handler(){
//需要执行的方法
},
//开启深度监听
//deep:true
}
}
...
因此,在代码中我们可以加入watch侦听属性
...其他省略
// created() {
// this.getData();
// },
watch: {
"$route.params.id":{
immediate:true,
handler(){
console.log(this.$route);
this.getData();
}
}
},
最后,为了让界面更加的美观,可以加上加载的Loading组件以及在页面的最开始显示频道名称
ChannelNews.vue修改的代码
<template>
<div>
<div class="type-title">
{{channelName}}
</div>
<Loading v-if="isLoading"></Loading>
<NewList v-else :news="news"></NewList>
<Pager
:total="total"
:page="page"
:limit="limit"
:pagerCount="pagerCount"
@pageChange="handlePageChange"></Pager>
</div>
</template>
<script>
import Center from '@/components/Center'
import Pager from '@/components/Pager'
import NewList from '@/components/NewList'
import Loading from '@/components/Loading'
import {getNewsList,getNewsChannels} from '@/services/NewsService'
export default {
components:{
Center,Pager,NewList,Loading
},
data(){
return {
total:0, //数量默认值
pagerCount:11,//页码显示数量,必须是奇数
limit:10, //每页数据显示数量
news:[], //新闻数据
isLoading:true, //是否加载Loading组件
channelName:"",//频道名称
}
},
computed:{
page(){ //page计算属性,默认为1,其他时候从路由的url中取得page数据
return +this.$route.query.page || 1;
}
},
methods:{
//切换页码的方法
handlePageChange(page){
//命令式路由
this.$router.push({
name:"Channels",
params:{
id:this.$route.params.id
},
query:{
page:page
}
})
this.getData();
},
//设置频道名称
async setChannelName(){
var resp = await getNewsChannels();
// 由于只有阿里云只提供了获取全部频道的API
// 因此只有通过循环找到channelId对应的频道
var channel = resp.find((item)=>{
return this.$route.params.id == item.channelId;
});
this.channelName = channel.name;
},
//获取远程数据
async getData(){
this.isLoading = true;
let resp = await getNewsList(
this.$route.params.id,
this.page,
this.limit
);
this.total = resp.allNum;
this.news = resp.contentlist;
this.isLoading = false;
}
},
//watch侦听属性
watch: {
//侦听id值发生变化执行handler()函数
"$route.params.id":{
immediate:true,
handler(){
this.setChannelName();
this.getData();
}
}
},
}
</script>
<style scoped>
.type-title {
font-size: 2em;
color: #888;
border-bottom: 1px solid #ccc;
padding-bottom: 10px;
}
</style>
Comments