【项目实战】基于Vue3+Vant3造一个网页版的类掘金app项目 - 列表组件封装

738 阅读5分钟

「这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战

前言

大家好,前面两次分享我们已经实现了首页中推荐标签页的基本功能,包括热榜文章、热榜作者轮播展示,以及推荐文章列表展示、下拉刷新和滚动翻页自动加载数据功能。在首页中除了“推荐”标签页外还有其它10个额外的标签,例如:关注、热榜、前端、后端等等。接下来分享中我们就挨个来实现一下这些标签页的功能。

打开官方app,我们会发现这些标签页中都有一个共同的模块,那就是文章列表,并且都有下拉刷新和滚动翻页功能,样式也基本相同,这样一来我们就可以把推荐标签中的列表进行一下封装,以实现代码复用节省开发时间。

另外在上一篇分享中还有一个小功能点没有实现,就是点击文章列表中的点赞图标可以实现对文章的点赞和取消点赞功能,在本次对列表的封装中我们一并将该功能实现进去。

本次分享实现如下功能:

  • 文章点赞功能实现,及图标样式切换
  • 文章列表组件JList封装

文章点赞功能实现,及图标样式切换

还有一个细节功能没实现,在每篇文章栏的底部都会有查看,点赞和评论3个图标,当点击查看和评论图标时都会跳转到文章详细页面,但是当点击点赞图标时则直接会让点赞数加1或减1,同时点赞图标的状态变为实心蓝色或空心灰色。这个功能实现起来也比较简单,下面我们就来分析一下:

  • 首先在官方后台接口返回给我们的数据中会包含一个名为“user_interect”的对象,在这个对象中有这么个布尔类型的属性:is_digg表示是否已经点赞,如下图: image.png
  • 那么在渲染文章列表时我们就可以根据这个属性来确定点赞图标的样式应该是实心还是空心,其实就是给这个标签“
  • ” 添加或删除名为“active”的样式即可
  • 然后再给该标签绑定一个click事件,在该事件中通过调用save或cancel两个后台接口来实现对文章的点赞和取消点赞
    • 如果已经点赞则调用cancel接口取消点赞,同时点赞数减1并且去掉active样式
    • 如果还未点赞则调用save接口进行点赞操作,同时点赞数加1并且添加active样式 核心代码及效果图如下:
<li
  :id="`like${
    item.article_info
      ? item.article_info.article_id
      : item.item_info.article_info.article_id
  }`"
  class="item like"
  :class="{
    active: item.user_interact
      ? item.user_interact.is_digg
      : item.item_info.user_interact.is_digg,
  }"
  @click="
    digg(
      item.article_info
        ? item.article_info.article_id
        : item.item_info.article_info.article_id
    )
  "
>
const digg = function digg(article_id) {
      const likeli = document.querySelector(`#like${article_id}`),
        likespan = document.querySelector(`#likecount${article_id}`);
      if (likeli.classList.contains("active")) {
        likeli.className = "item like";
        api.cancelDigg(article_id).then((res) => {
          console.log(res);
        });
        likespan.innerText = parseInt(likespan.innerText) - 1;
      } else {
        likeli.className = "item like active";
        // [].push.call(likeli.classList, "active");
        api.saveDigg(article_id).then((res) => {
          console.log(res);
        });
        likespan.innerText = parseInt(likespan.innerText) + 1;
      }
    };

test2.gif

列表组件封装

首页中推荐列表功能我们基本已经实现,但还剩下其它几个标签,如前端,后端等页面还是空白,上面已经提到:这几个标签中的内容结构大同小异,基本都有个文章列表。为了实现代码重用节省开发时间,我们把已经实现了的推荐列表进行封装。由于每个标签中调用的接口和展示的数据内容都不一样,因此对于不同标签中的不同数据需要动态传给列表组件,但这样又会引出一个新的问题:由于列表中涉及到下拉刷新和滚动分页自动加载数据功能,这个功能需要在接口请求成功后再去操作,因此我们的下拉刷新、数据加载以及接口请求的相关方法也得封装在组件中才能满足以上要求。这样一来我们就不能将数据动态传入来,而是需要将接口地址作为属性传进去。具体实现思路如下:

  • 新建一个JList.vue组件,将Home.vue中的列表相关HTML、css及js代码拷贝到JList中
  • 给JList组件定义3个props属性:
    • url:后端接口地址
    • limit:每次请求的数据条数
    • sort_type:排序类型,如推荐、热榜等调用的都是同一个接口,只是排序类型不同
  • 将onLoad方法中的api.recommendAllFee()方法改为直接通过http.post的形式调用,并将url,limit和sort_type作为参数传进去
  • 另外推荐页面与其他几个标签页面调用的接口都不相同,且返回的数据结构也不相同(如下图),为了能够让组件通用性更强,更能兼容所有数据结构,在绑定数据时还需做下兼容处理,就是有点麻烦,如:
<div>{{ item.article_info ? item.article_info.title : item.item_info ? item.item_info.article_info.title : ''}} </div>
  • 最后再在首页的每个标签页中使用JList组件 image.png 代码及效果如下:

  • Home.vue中使用JList组件

<!--Home.vue中使用JList组件-->
<j-list
    url="/juejin/recommend_all_feed"
    :limit="20"
    :sort_type="200"
    />
  • JList.vue组件封装
 <div class="article-list">
    <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
      <van-list
        v-model:loading="loading"
        :finished="finished"
        finished-text="没有更多了"
        @load="onLoad"
      >
        <van-cell
          v-for="item in recommendList"
          :key="item.item_info.article_id || item.item_info.advert_id"
        >
          <div class="article-list">
            <div
              v-if="item.item_type === 14"
              class="advertisement"
            >
             <!--广告内容代码省略-->
            </div>
            <div             
              class="entry"
              v-else
            >
                <!--文章内容代码省略-->
            </div>
          </div>
        </van-cell>
      </van-list>
    </van-pull-refresh>
  </div>
props: {
    url: {
      type: String,
      required: true,
    },
    limit: {
      type: Number,
      default: 20,
    },
    sort_type: {
      type: Number,
      default: 200,
    },
  },
setup(props,context){
    const {url,limit,sort_type} = props;
    let pageNo = 0;
    const onLoad = function () {
     http.post(url,{pageNo,limit,sort_type}}).then((res) => {
     if(state.refreshing){
         state.recommendList = [];
         state.refreshing = false;
     }
     state.recommendList.push(...res.data);
     state.loading = false;      
     
     if(pageNo >= 50){//只加载50页(1000条)数据
        state.finished = true;
     }
     pageNo++;
  });
};

const onRefresh = function (params) {
  pageNo = 0;//从第一页重新加载
  state.finished = false;//清空列表数据,重新加载
  state.loading = true;
  onLoad();
};
}

test2.gif

总结

本次分享中我们实现来对文章的点赞和取消点赞功能,以及封装来一个通用列表组件,相当于一次性实现来首页中所有标签中的列表功能,然而虽然每个标签页都有相同的列表功能,但是不同的标签之间还是存在着一些细微的差别的,下一次分享中我们将继续来分析这些不同的地方。本次分享就先到这里了,欢迎点赞加关注哦