Vue实战篇三十二:实现新闻的无限加载

系列文章目录

Vue基础篇一:编写第一个Vue程序
Vue基础篇二:Vue组件的核心概念
Vue基础篇三:Vue的计算属性与侦听器
Vue基础篇四:Vue的生命周期(秒杀案例实战)
Vue基础篇五:Vue的指令
Vue基础篇六:Vue使用JSX进行动态渲染
Vue提高篇一:使用Vuex进行状态管理
Vue提高篇二:使用vue-router实现静态路由
Vue提高篇三:使用vue-router实现动态路由
Vue提高篇四:使用Element UI组件库
Vue提高篇五:使用Jest进行单元测试
Vue提高篇六: 使用Vetur+ESLint+Prettier插件提升开发效率
Vue实战篇一: 使用Vue搭建注册登录界面
Vue实战篇二: 实现邮件验证码发送
Vue实战篇三:实现用户注册
Vue实战篇四:创建多步骤表单
Vue实战篇五:实现文件上传
Vue实战篇六:表格渲染动态数据
Vue实战篇七:表单校验
Vue实战篇八:实现弹出对话框进行交互
Vue实战篇九:使用省市区级联选择插件
Vue实战篇十:响应式布局
Vue实战篇十一:父组件获取子组件数据的常规方法
Vue实战篇十二:多项选择器的实际运用
Vue实战篇十三:实战分页组件
Vue实战篇十四:前端excel组件实现数据导入
Vue实战篇十五:表格数据多选在实际项目中的技巧
Vue实战篇十六:导航菜单
Vue实战篇十七:用树型组件实现一个知识目录
Vue实战篇十八:搭建一个知识库框架
Vue实战篇十九:使用printjs打印表单
Vue实战篇二十:自定义表格合计
Vue实战篇二十一:实战Prop的双向绑定
Vue实战篇二十二:生成二维码
Vue实战篇二十三:卡片风格与列表风格的切换
Vue实战篇二十四:分页显示
Vue实战篇二十五:使用ECharts绘制疫情折线图
Vue实战篇二十六:创建动态仪表盘
Vue实战篇二十七:实现走马灯效果的商品轮播图
Vue实战篇二十八:实现一个手机版的购物车
Vue实战篇二十九:模拟一个简易留言板
Vue项目实战篇一:实现一个完整的留言板(带前后端源码下载)
Vue实战篇三十:实现一个简易版的头条新闻
Vue实战篇三十一:实现一个改进版的头条新闻


一、背景

  • 在上一篇文章中,我们加入了新闻频道,可以让用户选择不同的频道,阅读新闻。
  • 但每次点击频道时,APP只能加载固定的10条新闻列表。
  • 我们这次需要实现新闻的无限加载:即当用户滚动新闻列表到达底部时,APP自动加载下一页新闻。

在这里插入图片描述

二、引入无限滚动组件

  • 我们引入element-uiInfiniteScroll组件,该组件可以判断容器的垂直滚动条滚动至底部时,自动执行加载方法。

在这里插入图片描述

  • 基础代码
<template>
  <ul class="infinite-list" v-infinite-scroll="load" style="overflow:auto">
    <li v-for="i in count" class="infinite-list-item">{
    
    {
    
     i }}</li>
  </ul>
</template>

<script>
  export default {
    
    
    data () {
    
    
      return {
    
    
        count: 0
      }
    },
    methods: {
    
    
      load () {
    
    
        this.count += 2
      }
    }
  }
</script>

三、加载新闻事件

3.1 接口解析

  • 我们仍然通过极数数据接口,来获取新闻频道的数据
  • 在这个接口中,我们观察到请求参数中,有个start参数,可以通过传入偏移值offset,来获取下一页的新闻。
  • 即当前浏览的是第1页的新闻,当滚动条到达底部触发自动加载方法时,需要给start参数传值为2,即加载第2页的新闻,依次类推。

在这里插入图片描述

3.2 编写加载方法

methods: {
    
    
	  // 自动加载方法,获取下一页新闻
    load() {
    
    
     // 从状态管理器中获取当前新闻列表是第几页
      let start = this.$store.state.news.start
      start++
      // 接口限制最大不超过400页
      if (start < 400) {
    
    
        this.getNews(this.$store.state.news.channel, start, this.$store.state.news.num).then(res => {
    
    
          console.log('loading more news', res)
          if (res && res.data.result) {
    
    
            const newsData = this.$store.state.news.newsData
            newsData.push.apply(newsData, res.data.result.list)
            this.$store.commit('SET_NEWS', newsData)
            this.$store.commit('SET_START', start)
          }
        })
      }
    },
    ...
  }
  • 附上状态管理器源码
// 用于存储各种变量
const news = {
    
    
  state: {
    
    
    // 频道
    channel: '',
    // 起始位置
    start: 0,
    // 一次向接口接取新闻的条数
    num: 10,
    // 存放拉取下来的新闻
    newsData: [],
    // 当前正在查看的新闻
    newsIndex: -1,
    // 是否加载状态
    loading: false
  },

  mutations: {
    
    

    SET_CHANNEL: (state, channel) => {
    
    
      state.channel = channel
    },
    SET_START: (state, start) => {
    
    
      state.start = start
    },
    SET_NUM: (state, num) => {
    
    
      state.num = num
    },

    SET_NEWS: (state, news) => {
    
    
      state.newsData = news
    },

    SET_NEWS_INDEX: (state, newsIndex) => {
    
    
      state.newsIndex = newsIndex
    },
    SET_LOADING: (state, loading) => {
    
    
      state.loading = loading
    }

  },

  actions: {
    
    
    setChannel({
     
      commit }, channel) {
    
    
      return new Promise(resolve => {
    
    
        commit('SET_CHANNEL', channel)
      })
    },

    setStart({
     
      commit }, start) {
    
    
      return new Promise(resolve => {
    
    
        commit('SET_START', start)
      })
    },

    setNum({
     
      commit }, num) {
    
    
      return new Promise(resolve => {
    
    
        commit('SET_NUM', num)
      })
    },

    setNews({
     
      commit }, news) {
    
    
      return new Promise(resolve => {
    
    
        commit('SET_NEWS', news)
      })
    },
    setNewsIndex({
     
      commit }, newsIndex) {
    
    
      return new Promise(resolve => {
    
    
        commit('SET_NEWS_INDEX', newsIndex)
      })
    },
    setLoading({
     
      commit }, loading) {
    
    
      return new Promise(resolve => {
    
    
        commit('SET_LOADING', loading)
      })
    }
  }
}

export default news

四、改造主页,实现无限加载

1、在新闻列表中引入无限滚动加载功能,并关联自动加载方法
2、在底部放入加载条,显示正在努力加载

在这里插入图片描述
3、向接口获取数据成功后,重新渲染新闻列表
在这里插入图片描述

  • 以下是改造后的完整源码:
<template>
  <div>
    <!-- 标题栏 -->
    <div class="header">
      <span />
      <span>新闻</span>
      <span />
    </div>
    <channel />
    <div ref="container" class="nav-content">
      <!-- 在新闻列表中引入无限滚动加载功能 -->
      <div v-if="loading == false" v-infinite-scroll="load" class="news-list">
        <div
          v-for="(item, index) in newData"
          :key="index"
          class="section"
          @click="toNews(index)"
        >
          <div class="news">
            <div class="news-left">
              <img :src="item.pic" alt="">
            </div>
            <div class="news-right">
              <div class="newsTitle">{
    
    {
    
     item.title }}</div>
              <div class="newsMessage">
                <span>{
    
    {
    
     item.time }}</span>
                <span>{
    
    {
    
     item.src }}</span>
              </div>
            </div>
          </div>
        </div>
        <!-- 在底部放入加载条 -->
        <div class="loading-more">正在努力加载</div>
      </div>
      <el-main
        v-else
        v-loading="loading"
        class="load"
        element-loading-background="rgba(0,0,0,0)"
        element-loading-text="正在加载中"
      />
    </div>
  </div>
</template>

<script>
import Channel from './channel'
import {
    
     getNewList } from '@/api/news'
export default {
    
    
  name: 'Home',
  components: {
    
     Channel },
  data() {
    
    
    return {
    
    
    }
  },
  computed: {
    
    
    newData() {
    
    
      return this.$store.state.news.newsData
    },
    loading() {
    
    
      return this.$store.state.news.loading
    }
  },
  methods: {
    
    
    load() {
    
    
      // 获取下一页新闻
      console.log('已到达底部,自动触发加载方法')
      let start = this.$store.state.news.start
      start++
      if (start < 400) {
    
    
        this.getNews(this.$store.state.news.channel, start, this.$store.state.news.num).then(res => {
    
    
          console.log('加载下一页新闻列表', res)
          if (res && res.data.result) {
    
    
            const newsData = this.$store.state.news.newsData
            newsData.push.apply(newsData, res.data.result.list)
            this.$store.commit('SET_NEWS', newsData)
            this.$store.commit('SET_START', start)
          }
        })
      }
    },
    // 异步获取新闻
    async getNews(channel, start, num) {
    
    
      const data = await getNewList(channel, start, num)
      return data
    },
    // 打开新闻阅读
    toNews(index) {
    
    
      this.$store.commit('SET_NEWS_INDEX', index)
      this.$router.push('/news')
    }
  }

}
</script>

<style lang="scss"  scoped>
.header {
    
    
  width: 100%;
  height: 1.2rem;
  background-color: #d43d3d;
  display: flex;
  justify-content: space-between;
  align-items: center;
  color: #fff;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 3px;
  z-index: 99;
  position: fixed;
  top: 0;
  img {
    
    
    width: 0.67rem;
    height: 0.67rem;
    cursor: pointer;
  }
}

.nav-content {
    
    
  margin-top: 2.4rem;
}

.news-list {
    
    
  position: relative;
  height:calc(100vh - 2.4rem - 49px);
  overflow-y:auto;
  width: 100%;
}

.section {
    
    
  width: 100%;
  height: 2.5rem;
  border-bottom: 1px solid #ccc;
}

.news {
    
    
  height: 2.25rem;
  box-sizing: border-box;
  margin: 10px 10px;
  display: flex;
}
.news-left {
    
    
  height: 100%;
  width: 2.8rem;
  display: inline-block;
}
.news-left img {
    
    
  width: 100%;
  height: 100%;
}
.news-right {
    
    
  flex: 1;
  padding-left: 10px;
}
.newsTitle {
    
    
  width: 100%;
  height: 62%;
  color: #404040;
  font-size: 17px;
  overflow: hidden;
}
.newsMessage {
    
    
  width: 100%;
  height: 38%;
  display: flex;
  align-items: flex-end;
  color: #888;
  justify-content: space-between;
}
.load {
    
    
  width: 100%;
  height: 100%;
  overflow: hidden;
}
 .loading-more {
    
    
  margin-top: 5px;
  width: 100%;
  height: 20px;
  text-align: center;
 }
</style>

五、效果演示

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/jpgzhu/article/details/125894921
今日推荐