【项目实战】基于Vue3+Vant3造一个网页版的类掘金app项目 - 列表数据过滤

150 阅读5分钟

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

前言

大家好,经过一波三折终于结束了优秀作者榜页面的功能实现,接下来的分享中我们继续回到首页来完善首页中的功能。在首页中除了“关注”和“推荐”标签外,其它几个标签都是动态渲染的,并且对应标签页中的列表数据也是根据标签动态加载的。然而在这几个动态渲染的标签中还有一个额外的功能没做,就是进入标签页后这个标签下面还会分为很多子标签,并且点击子标签后列表数据会自动过滤刷新。接下来的分享中就来实现一下该功能。

点击子标签属性数据列表

在前面实现数据列表功能时,特意针对这几个动态标签封装来一个子标签组件JTags和列表组件JList,然而就是这两个封装给我们接下来的分享会带来很多麻烦

先来看下我们接下来要实现的功能:如下图

test2.gif

  • 首先在点击任意一个动态标签时会跳转到该标签对应的页面,该页面包括一个JTags组件(子标签)和一个JList组件(列表),默认进来后会根据主标签ID动态加载出该标签下所有的子标签(在JTags中展示),然后列表则会加载主标签下的所有数据内容。
  • 当我们尝试去点击某个子标签时,则列表会根据主标签和子标签过滤出符合条件的文章。而就是这个功能,由于前面两个组件的分开封装,实现起来变得有些复杂有些绕。 在前面封装JTags和JList时,我们是通过定义url和其它的参数属性(props)来实现数据请求与加载的,而不是直接将数据源传给组件去渲染,这样就会带来一个问题:当url或其它的参数属性发生变化时,组件不会自动触发更新操作,也就是说一旦数据已经渲染完成,不管是url还是其它的props怎么变化都不再重新请求数据,这就是问题所在。

而我们要实现的功能是:点击某个子标签后会根据子标签的id重新去后台请求列表数据并重新渲染,但点击标签操作是发生在JTags组件中的,而数据渲染需要加载在JList的组件中,而我们知道在没有任何外界帮助的情况下,在一个组件中是无法实现对另一个组件中的属性或方法进行操作的。

最初想到的解决方案是:在JTags组件中再定义一个自定义点击事件,在该自定义事件中将对应子标签的id传递出去,然后在Home.vue中定义一个函数来接收该标签id并通过props的形式传递给JList组件。然而结果可想而知:并没有达到我们预期的效果,子标签点击后列表数据并未发生任何变化。

在经过一番绞尽脑汁的思索过后,受兄弟组件间传值的启发,突然灵光一现想到一个可行但不一定是最优的方案:虽然点击和加载不是发生在同一个组件中,但是我们可以把两个组件间要用到的数据保存在全局状态vuex中,这样一来就可以实现两个组件间的数据共享了。大概思路如下:

  • 在store下的index.js中定义三个状态属性:
    • url:String类型,默认值:“/juejin/recommend_cate_feed”,用于请求后台接口的url(给JList.vue组件使用)
    • tagId:String类型,默认为空,保存子标签的id(给JList.vue组件使用)
    • refreshList:function类型,用于点击子标签后刷新列表数据(给JTags使用)
  • 在JTags中给标签绑定一个click事件,并在该事件中做如下操作:
    • 设置vuex中的url属性值为“/juejing/recommend_cate_tag_feed”,用于同时根据父标签id和子标签id共同过滤数据
    • 设置vuex中的tagId属性值为当前选中的子标签的id值
    • 最后调用vuex中的refreshList方法,属性JList列表数据
  • 在JList组件中做如下修改:
    • 删除之前定义的props:url(或者保留也不影响)
    • 在onLoad方法中定义两个新的变量url和tagId,值则从vuex中的url和tagId中或取,并将两个参数传递给htt.post axios请求
    • 最后再在onRefresh方法的下面将该方法(onRefresh)的定义赋值给vuex中的refreshList,以便在JTags中调用
  • 在首页Home.vue中,由于点击不同的父标签会根据不同的请求接口加载不同的数据,因此还需在标签的click-tab事件中根据不同的标签设置不同的后台接口给vuex中url 核心代码及实现效果:
  • store/index.js
export default createStore({
    state:{
        url:'/juejin/recommend_cate_feed',
        tagId:'',
        refreshList:function(){}
    },
    mutations:{
        setRefresh(state,payload){
            state.refreshList = payload
        },
        setUrl(state,url){
            state.url = url
        },
        setTagId(state,tagId){
            state.tagId = tagId
        }
    }
})
  • JTags.vue
<ul class="tag-list">
    <li class="tag-item"
    <!--省略...-->
    :data-id="tag.tag_id"
     @click="clickTag(tag.tag_id)"
    >
    {{tag.tag_name}}
    </li>
</ul>
const clickTag = function(tagId){
    const tags = document.querySelectorAll(".tag-item");
    tags.forEach(item=>{
        item.classList.remove('active');
        if(item.getAttribute("data-id") == tagId){
            item.classList.add('active');
        }
    });
    store.commit('setUrl',"/juejin/recommend_cate_tag_feed")
    store.commit('setTagId',tagId);
    store.state.refreshList();
}
& .tag-item.active{
    background:#1e80ff;
    color:#fff;
}
  • JList.vue
const onLoad = function(){
    let api_url = store.state.url,
        tagId = store.state.tagId;
    http.post(api_url,{
        //...
        tagId:tagId
    })
}
const onRefresh = function(){}

store.commit("setRefresh",onRefresh);
  • Home.vue
const clickTab = function(tab){
    if(tab.name === "关注"){
        store.commit("setUrl", "/juejin/recommend_follow_feed")
    }else if(tab.name === "推荐"){
        store.commit("setUrl", "/juejin/recommend_all_feed")
    }else{
        store.commit("setUrl", "/juejin/recommend_cate_feed")
    }
}

test2.gif 总结

到此关于首页列表的功能就算是完全实现了,由于一次封装事故,我们还意外的引入了vuex的使用,也算是借助本次实战来练习一下vuex的使用吧。今天的分享就先到这里了,欢迎点赞加关注哦!!