移动端新闻头条类新闻列表页面

374 阅读5分钟

需求技术点分析

  1. 新闻列表页面需要实现下拉刷新和滑动更新内容的功能,需要通过vant组件库里的组件实现功能
  2. 列表内容后续需要复用,所以需要单独封装一个组件
  3. 更新时候需要更新和原来不同的内容,需要通过时间戳判断

静态页面搭建

  1. 使用vant组件库van-nav-bar组件搭建头部样式,并使用插槽的方式插入搜索按钮
  2. 使用vant组件库tabs组件van-tabs标签实现标签页滚动,并在右侧使用插槽技术插入汉堡按钮
  3. 使用vant组件库的van-cell单元格组件,搭建新闻列表整体框架

封装网络请求

  • 在新闻列表页面中,一共要获取两种数据,一个是频道滚动标签页数据,还有一个是新闻列表数据,所以需要封装两个axios请求函数
  1. 频道滚动标签页网络请求
import request from "@/utils/request";
/**
 * 获取首页频道列表信息
 */
 export const getUserChannels = () => {
    return request({
      method: 'GET',
      url: 'v1_0/user/channels'
    })
  }
  1. 新闻列表网络请求
  • 这个请求需要用户传入两个必须的参数,一个是请求的频道ID(channel_id),还有一个是时间戳(timestamp),请求新的推荐数据传当前的时间戳,请求历史推荐传指定的时间戳
import request from "@/utils/request";
/**
 * 获取频道的文章列表
 */
 export const getArticles = params => {
    return request({
      method: 'GET',
      url: 'v1_0/articles',
      params
    })
  }

将频道数据渲染到页面上

  1. 将请求到的频道需要滚动的内容循环渲染到van-tab组件标签上,就可以完成滚动的频道标签页

渲染文章列表内容

  • 需求技术点:这里需要考虑到,在渲染文章列表时,是要考虑到下拉刷新和滑动更新内容这两个功能的,所以需要在这两个功能的基础上来完成数据的渲染。这里我选择将整个的文章列表封装成组件,填充到van-tab 标签中,通过父传子的传值方式将对象传过去,用来获取频道的id
<van-tab :title="item.name" v-for="item,index in channels" :key="index">
    <!-- 文章列表组件 -->
    <ArticleList :channel="item"></ArticleList>
    </van-tab>
  • 完成下拉刷新和滑动更新的需求
  1. 滑动更新功能
  • 功能技术点:这里使用了vant组件库中的van-list列表组件,其中的关键点就是在于利用该组件的loading,finished属性和load事件

  • 官方文档:List 组件通过 loading 和 finished 两个变量控制加载状态,当组件滚动到底部时,会触发 load 事件并将 loading 设置成 true。此时可以发起异步操作并更新数据,数据更新完毕后,将 loading 设置成 false 即可。若数据已全部加载完毕,则直接将 finished 设置成 true 即可。

  • 注意点:需要定义一个空的数组,监听van-list的load事件,当load事件触发,发起网络请求时,把请求到的数组数据添加到新的数组当中,这里不可以直接等于,需要用push方法追加,不然就会数据覆盖永远没办法显示下一组数据

  • 遇到的问题,没有对接口需要定义的时间戳做定义和判断,导致无法刷新新的内容

  • 解决办法:在data中定义一个变量timestamp设置为null,通过请求到的数组长度作为判断条件,如果可以获取数据长度,则加载完成就获取新的事件戳,如果已经没有数据了就将finished 属性改为true结束加载

async onLoad() {
      // 1. 请求获取数据
      try {
        const { data } = await getArticles({
          channel_id: this.channel.id,
          //获取事件戳参数
          timestamp: this.timestamp || Date.now(),
          with_top: 1
        })
        const { results } = data.data
        // 2. 把请求结果数据放到 list 数组中
        //每次请求数据都往里面追加内容,才能完成滚动加载,使用展开运算符将元素追加
        this.list.push(...results)

        // 3. 本次数据加载结束之后要把加载状态设置为结束
        // loading 关闭以后才能触发下一次的加载更多
        this.loading = false
        // 4. 判断数据是否全部加载完成
        if (results.length) {
          // 更新获取下一页数据的时间戳
          this.timestamp = data.data.pre_timestamp
        } else {
          this.finished = true
        }
      } catch (err) {
        this.error = true
        this.loading = false
      }
    },
  1. 下拉刷新需求
  • 功能技术点:这里使用了vant组件库中的van-pull-refresh下拉刷新组件,其中的关键点就是在于监听该组件refresh事件,然后利用该组件的v-model属性设置为boolen变量来控制刷新状态,

  • 官方文档:下拉刷新时会触发 refresh 事件,在事件的回调函数中可以进行同步或异步操作,操作完成后将 v-model 设置为 false,表示加载完成

  • 注意点:这里在请求过数据后,同样也是追加到定义的list数组中,不过这里要使用数组的unshift方法在最前面追加,不然显示不出页面更新的效果

  // 当触发下拉刷新的时候调用该函数
    async onRefresh() {
      try {
        // 1. 请求获取数据
        const { data } = await getArticles({
          channel_id: this.channel.id, // 频道 id
          timestamp: Date.now(), // 下拉刷新每次都应该获取最新数据
          with_top: 1 // 是否包含置顶,进入页面第一次请求时要包含置顶文章,1-包含置顶,0-不包含
        })

        // 2. 将数据追加到列表的顶部
        const { results } = data.data
        this.list.unshift(...results)

        // 3. 关闭下拉刷新的 loading 状态
        this.isRefreshLoading = false

        // 提示成功
        this.refreshSuccessText = `刷新成功,更新了${results.length}条数据`
      } catch (err) {
        console.log(err)
        this.isRefreshLoading = false // 关闭下拉刷新的 loading 状态
        this.$toast('刷新失败')
      }
    }

封装文章单元格组件

  1. 因为考虑到组件复用,所以把文章列表里的单个文章单元格封装成了组件
  • 注意点:因为接收到的数据里需要显示的图片有一张和三张的,所以需要使用v-if来判断显示哪个image标签
  • 需求技术点:同样适用了vant组件库中的单元格组件和图片组件,并且从文章列表父组件中使用props方法接受了对象数据来渲染页面
 <van-cell class="article-item">
   <div slot="title" class="title">{{article.title}}</div>
    <div slot="label">
         <div v-if="article.cover.type === 3" class="cover-wrap">
        <div
          class="cover-item"
          v-for="(img, index) in article.cover.images"
          :key="index"
        >
          <van-image
           fit="scale-down"
            width="100"
            height="100"
            :src="img"
          />
        </div>
      </div>
        <div class="label-info-wrap">
            <span>{{ article.aut_name }}</span>
            <span>{{ article.comm_count }}评论</span>
            <span>{{ article.pubdate }}</span>
        </div>
    </div>
    <van-image
    v-if="article.cover.type === 1"
     fit="cover"
      slot="default"
      width="100"
      height="100"
      :src="article.cover.images[0]"
    />
  </van-cell>