主页
10 - 加载频道数据
步骤:
- a. 在
mounted&created
中请求服务器接口,得到频道数据- 接口: 获取用户频道列表
- b. 将返回的频道数据保存起来
- c. 将频道数据渲染到页面上
11- 给请求设置 token
获取频道数据的接口其实是这一种情况:
没有传入 Authorization(token): 只会得到默认的 7 条频道数据
传入了 Authorization(token) :就会得到当前登录用户的频道数据
由于在项目中有很多像获取频道数据一样的接口,它们都可以传入 Authorization,来得到用户自己的信息。也可以不传 Authorization 得到默认数据
需求:如果在 localstorage(vuex) 中 token 就应该放到请求头中,如果 localstorage(vuex) 中没有频道数据,就不用传。
解决方案:在每次发送请求(不管是任意请求)时,都先判断一个 vuex 中是否有用户的 token,如果有,将 token 添加到请求头中,如果没有,不管了。可以在 axios 的请求拦截器中判断 vuex 中是否存在 token,如果存在直接将 token 携带在请求头中
步骤:
- a. 在
utils/myhttp.js
文件中的请求拦截器- 得到
store
对象 - 判断
store
中的state
下的userInfo
是否存在属性token
- 如果存在 ,就将
token
以Authorization
为键,以token
为值,传入到请求头中 - 如果不存在,不用理会
- 如果存在 ,就将
- 得到
注意点:
- 1.0 向请求头中添加
token
时:- 键为:
Authorization
- 值为:
Bearer
+token
(格式为: Bearer + 空格)
- 键为:
- 2.0 取得
store
中的 token 中时- 先将
store
对象导入
- 先将
- 3.0 如果添加了
Authroization
之后反而得到不频道数据:- 需要重新登录(token 过期了)
- 4.0 token 的效期只有 2 个小时
12 - 判断频道数据
频道数据来源:
如果登录
直接从服务器中得到当前用户的频道数据
如果没有登录
判断:当前 localstorage 中是否存在频道数据
存在:直接从 localstorage 中取出频道数据,进行渲染
不存在:直接从服务器中得到默认的 7 条频道数据
步骤:
- 1.0 找到
mounted
, 将直接获取频道数据的操作删除掉 - 2.0 进行判断:
- 判断用户是否登录(判断
vuex
中是否存在token
)- 如果登录:
- 直接从服务器中得到用户自己的频道信息
- 如果未登录:
- 判断
localstorage
中是否存在频道数据- 如果存在:
- 直接取出渲染到页面上
- 如果不存在:
- 直接从服务器中得到默认的 7条频道数据
- 如果存在:
- 判断
- 如果登录:
- 判断用户是否登录(判断
13 - 将不同频道下的数据分开
由于不同的频道下的数据源是不一样的,所以将来要正确渲染数据需要给每个频道都设置一组自己的数据
文章的数据源:articleList
list 组件的加载状态: loading
list 组件的加载完结状态: finished
pull-refresh 组件的加载状态: isLoading
步骤:
-
1.0 当我们从服务器中得到了频道数据之后:
{ channels: [ {id:0,name:'推荐'}, {id:11,name:'后端'} ] }
-
2.0 应该在每个频道数据下添加这些额外属性: articleList & loading & finished & isLoading
{ channels: [ {id:0,name:'推荐',articleList:[],loading:false,finished:false,isLoading:false}, {id:11,name:'后端',articleList:[],loading:false,finished:false,isLoading:false} ] }
-
3.0 将添加的数据在页面上动态渲染
14 - 渲染频道下的文章数据
得到当前选中频道的数据对象:可以给 van-tab 设置一个
v-model
属性
步骤:
- 1.0 确定当前切换的频道的 id
- 给 van-tbas 添加一个属性:
v-model=“active”
- 在 data 中添加一个属性:
active: 0
- 可以通过以下表达式得到频道的
id
:this.channelList[this.active].id
- 给 van-tbas 添加一个属性:
- 2.0 在
list
组件的onload
事件中添加逻辑代码- 得到当前切换的频道的
id
- 发送请求到服务器去得到当前频道对应的文章数据
- 接口:频道新闻推荐_V1.1
- 将数据渲染到页面上
- 得到当前切换的频道的
注意点:
- 1.0 请求频道下的新闻有两个接口,需要使用第二个:频道新闻推荐_V1.1
- 2.0 加载完文章数据之后,页面不会马上显示这个数据,先切换到另一个频道之后再切换回来才会执行
- 这个问题的解决方案:this.$set()
15 - 解决数据无法直接渲染的 bug用$set
表现:打开页面时,文章数据已经加载了,但是无法渲染到页面上
原因:因为 articleList
数据是 channelList
定义之后,动态添加上去的数据,而这样的数据是没有响应式特点的
解决方案:可以使用 $set()
基本用法:
this.$set(obj, prop, value)
// obj:要添加属性的对象
// props:要添加的属性
// value:添加属性对应的值
16 - 上拉加载更多
- 问题1:文章数据渲染完成之后,页面上一直处于加载状态。
- 原因: list 刚执行完 load 事件,load事件会将 List的加载状态改为 true。
- 解决方式:将当前频道下的 list 的加载状态手动改为 false
- 问题2:当修改加载状态之后,页面就像抽风了一样。请求发一直发送,页面上显示的数据会一直切换。
- 原因:就是我们每次得到数据之后,使用新的数据将老的数据覆盖掉了,我们页面上的数据永远只有一页的数据(10条)
- 解决方式:只需要将每次得到的数据接收到原来的数据中就可以了
- 问题3:当我们切换到某一频道下时, 会一直发送请求,但是没有数据
- 原因:是因为当前频道下面没有数据
- 解决方式:只需要判断一个从服务器中返回的文章数据的长度是否为 0
- 如果为 0 :将当前频道下的 finished 属性改为 true
- 如果不为0:不用理会
17 - 下拉刷新
步骤:
- a. 在
onRefresh
事件中- 清除当前频道下的所有数据
- 重新加载数据
注意点:
- 如果 10 条数据的高度不能让容器的高度占满一个屏幕就无法继续上拉加载更多:所以需要给每个单元格设置一个高度
主页index的详细代码。style没复制:
<template>
<div class="index">
<!-- 头部标题-->
<van-nav-bar title="主页" fixed />
<!-- 频道区域 -->
<van-tabs v-model="active">
<van-tab v-for="(item,index) in channelList" :title="item.name" :key="index">
<!-- 下拉刷新 -->
<van-pull-refresh v-model="item.isLoading" @refresh="onRefresh">
<!-- 上拉显示下面数据 -->
<van-list v-model="item.loading" :finished="item.finished" finished-text="没有更多了" @load="onLoad">
<van-cell style="height:60px" v-for="(subitem,subindex) in item.articleList" :key="subindex" :title="subitem.title" />
</van-list>
</van-pull-refresh>
</van-tab>
</van-tabs>
<!-- 操作按钮 -->
<div class="process" @click="show=true">
<van-icon name="bars" />
</div>
<!-- 弹出层 -->
<!-- 把show与频道列表带过去给子组件 -->
<com :show = "show" :channelList="channelList"></com>
</div>
</template>
<script>
// 导入子组件
import com from './com/index'
// 导入网络请求方法
import { apiGetChannel } from '@/api/channel'
import { apiGetArticleList } from '@/api/article'
// 导入得到本地数据的方法
import { getLocal } from '@/utils/mylocal'
export default {
components: {
com
},
data () {
return {
// 存放用户频道的数据
channelList: [],
// 当前频道所在的下标
active: 0,
// 操作频道的子组件
show: false
}
},
mounted () {
this.getChannels()
},
methods: {
// 当list组件滚动到底部是会触发这个事件
async onLoad () {
console.log('到底')
// 1.0 得到当前选中的频道
const currentChannel = this.channelList[this.active]
// 2.0 得到当前有切换的频道的 id
const currentId = currentChannel.id
// 3.0 发送请求到服务器中,得到对应的文章数据
const res = await apiGetArticleList(currentId)
// console.log(res.data.data.results)
// 4.0 把获取到的res.data.data.results数组(文章列表)与当前所在的频道列表的articleList融合起来保存到当前选中的频道中
currentChannel.articleList = [...currentChannel.articleList, ...res.data.data.results]
// 5.0 将 list 组件的加载状态改为 false 因为有数据不要加载动画了
currentChannel.loading = false
// 6.0 判断当前频道的文章列表有没有数据,没有数据的话应该将list组件的数据状态设置为finished就会显示没有更多了
if (res.data.data.results.length === 0) {
currentChannel.finished = true
}
// console.log(this.channelList)
},
// 下拉刷新触发的方法
onRefresh () {
console.log('到头')
// 获得当前频道
const currentChannel = this.channelList[this.active]
// 清空当期频道的所有数据
currentChannel.articleList = []
currentChannel.isLoading = false
currentChannel.loading = true
currentChannel.finished = false
// 重新加载数据
this.onLoad()
},
// 获取用户频道
async getChannels () {
try {
// 1.0 判断用户有没有登录,有登录就有token
// 有token就直接发送请求得到用户频道
if (this.$store.state.userInfo.token) {
const res = await apiGetChannel()
// console.log(res)
this.channelList = res.data.data.channels
} else {
// 2.0 没有登录的话就判断本地有没有数据
const myList = getLocal('channelList')
if (myList) {
// 2.1 没有登录用户频道列表 就是本地那个列表
this.channelList = myList
} else {
// 2.2 本地没有数据就发送请求得到默认的数据而不是用户账户的数据
const res = await apiGetChannel()
this.channelList = res.data.data.channels
}
}
} catch (error) {
console.log('出错了')
}
// 添加额外属性
this.addOther()
},
addOther () {
// 遍历每个用户频道列表,给每个用户频道列表添加一些自己的属性
this.channelList.forEach((item) => {
// item.articleList = []
this.$set(item, 'articleList', []) // 添加额外的文章数据
// item.loading = false
this.$set(item, 'loading', false) // 添加额外的上拉加载更多的状态属性
// item.isLoading = false
this.$set(item, 'isLoading', false) // 添加额外的下拉刷新属性
// item.finished = false
this.$set(item, 'finished', false) // 添加 list 的完结状态
})
}
}
}
</script>
效果演示: