资讯专栏INFORMATION COLUMN

Vue.js项目重构,轻松实现上拉加载滚动位置还原

junbaor / 2703人阅读

摘要:这是基于进行状态管理的完整项目,包含了用户的登录退出,路由页面,滚动位置还原,帖子编辑状态保存等等,麻雀虽小,却是五脏俱全。

前言

上一篇《Vue.js轻松实现页面后退时,还原滚动位置》只是简单的实现了路由切换时进行的滚动位置还原,很多朋友就来问上拉加载怎么实现啊!于是我想起了以前做过一个叫vue-cnode的项目,于是花了两天时间进行了重构,完全的移除了Vuex,使用了Vuet来做为状态的管理工具。如果关注Vuet的朋友就会发现,版本更新得好快,简直就是版本帝啊!!!其实Vuet的版本升级,都是向下兼容的,每次的版本发布都会经过完整的单元测试和e2e测试,极大的保证了发布版本的稳定性。

项目源码

Vuet状态管理工具

vue-cnode Github和效果预览地址

需求分析

记录上拉请求时的页数

页面后退时,还原之前列表页面的状态

列表分类切换时,进行状态重置

从列表A点击详情A,页面后退,重新打开详情A,还原之前访问详情A状态

从列表A点击详情A,页面后退,重新打开详情B,清除详情A的状态,初始化详情B的状态

安装
npm install --save vuet
Vuet实例
import Vue from "vue"
import Vuet from "vuet"
import utils from "utils"
import http from "http"

Vue.use(Vuet)

export default new Vuet({
  pathJoin: "-", // 定义模块的连接符
  modules: {
    topic: {
      create: {
        data () {
          return {
            title: "", // 标题
            tab: "", // 发表的板块
            content: "" // 发表的内容
          }
        },
        manuals: {
          async create ({ state }) {
            if (!state.title) {
              return utils.toast("标题不能为空")
            } else if (!state.tab) {
              return utils.toast("选项不能为空")
            } else if (!state.content) {
              return utils.toast("内容不能为空")
            }
            const res = await http.post(`/topics`, {
              ...state
            })
            if (res.success) {
              this.reset()
            } else {
              utils.toast(res.error_msg)
            }
            return res
          }
        }
      },
      /********* 实现列表上拉加载滚动位置还原的核心代码开始 *************/
      list: {
        data () {
          return {
            data: [], // 列表存储的数据
            loading: true, // 数据正在加载中
            done: false, // 数据是否已经全部加载完成
            page: 1 // 加载的页数
          }
        },
        async fetch ({ state, route, params, path }) {
          // 注,在vuet 0.1.2以上版本,会多带一个params.routeWatch参数,我们可以根据这个来判断页面是否发生了变化
          if (params.routeWatch === true) { // 路由发生了变化,重置模块状态
            this.reset(path)
          } else if (params.routeWatch === false) { // 路由没有变化触发的请求,可能是从详情返回到列表
            return {}
          }
          // params.routeWatch 没有参数,则是上拉加载触发的调用
          const { tab = "" } = route.query
          const query = {
            tab,
            mdrender: false,
            limit: 20,
            page: state.page
          }
          const res = await http.get("/topics", query)
          const data = params.routeWatch ? res.data : [...state.data, ...res.data]
          return {
            data, // 更新模块的列表数据
            page: ++state.page, // 每次请求成功后,页数+1
            loading: false, // 数据加载完成
            done: res.data.length < 20 // 判断列表的页数是否全部加载完成
          }
        }
      },
      /********* 实现列表上拉加载滚动位置还原的核心代码结束 *************/
      detail: {
        data () {
          return {
            data: {
              id: null,
              author_id: null,
              tab: null,
              content: null,
              title: null,
              last_reply_at: null,
              good: false,
              top: false,
              reply_count: 0,
              visit_count: 0,
              create_at: null,
              author: {
                loginname: null,
                avatar_url: null
              },
              replies: [],
              is_collect: false
            },
            existence: true,
            loading: true,
            commentId: null
          }
        },
        async fetch ({ route }) {
          const { data } = await http.get(`/topic/${route.params.id}`)
          if (data) {
            return {
              data,
              loading: false
            }
          }
          return {
            existence: false,
            loading: false
          }
        }
      }
    },
    user: { // 登录用户的模块
      self: {
        data () {
          return {
            data: JSON.parse(localStorage.getItem("vue_cnode_self")) || {
              avatar_url: null,
              id: null,
              loginname: null,
              success: false
            }
          }
        },
        manuals: {
          async login ({ state }, accesstoken) { // 用户登录方法
            const res = await http.post(`/accesstoken`, { accesstoken })
            if (typeof res === "object" && res.success) {
              state.data = res
              localStorage.setItem("vue_cnode_self", JSON.stringify(res))
              localStorage.setItem("vue_cnode_accesstoken", accesstoken)
            }
            return res
          },
          signout () { // 用户退出方法
            localStorage.removeItem("vue_cnode_self")
            localStorage.removeItem("vue_cnode_accesstoken")
            this.reset()
          }
        }
      },
      detail: {
        data () {
          return {
            data: {
              loginname: null,
              avatar_url: null,
              githubUsername: null,
              create_at: null,
              score: 0,
              recent_topics: [],
              recent_replies: []
            },
            existence: true,
            loading: true,
            tabIndex: 0
          }
        },
        async fetch ({ route }) {
          const { data } = await http.get(`/user/${route.params.username}`)
          if (data) {
            return {
              data,
              loading: false
            }
          }
          return {
            existence: false,
            loading: false
          }
        }
      },
      messages: {
        data () {
          return {
            data: {
              has_read_messages: [],
              hasnot_read_messages: []
            },
            loading: true
          }
        },
        async fetch () {
            // 用户未登录,拦截请求
          if (!this.getState("user-self").data.id) return
          const { data } = await http.get(`/messages`, { mdrender: true })
          return {
            data
          }
        },
        count: {
          data () {
            return {
              data: 0
            }
          },
          async fetch () {
            // 用户未登录,拦截请求
            if (!this.getState("user-self").data.id) return
            const res = await http.get("/message/count")
            if (!res.data) return
            return {
              data: res.data
            }
          }
        }
      }
    }
  }
})

在Vuet实例创建完成后,我们就可以在组件中连接我们的Vuet了。

首页列表


页面详情


总结

因为篇幅有限,所以只列出了列表和详情的代码,大家有兴趣深入的话,可以看下vue-cnode的代码。这是基于Vuet进行状态管理的完整项目,包含了用户的登录退出,路由页面,滚动位置还原,帖子编辑状态保存等等,麻雀虽小,却是五脏俱全。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/83541.html

相关文章

  • 2017-06-18 前端日报

    摘要:前端日报精选精读高阶组件知乎专栏是如何重新定义前端开发的知乎专栏为您的性能选择最佳的引擎知乎专栏中的尺寸单位掘金一种生成雪碧图的懒惰姿势中文第期编写现代代码周刊第期的平凡之路我们到底可以通过多少种方式修改元素样式掘金 2017-06-18 前端日报 精选 精读 React 高阶组件 - 知乎专栏React 是如何重新定义前端开发的 - 知乎专栏为您的 Node 性能选择最佳的 JS 引...

    Yang_River 评论0 收藏0
  • Vue.js轻松实现页面后退时,还原滚动位置

    摘要:前言从发布之后,陆陆续续做了七八个项目,摸索出来了一套自己的状态管理模式,我将之称为。其实自带的规则能够支持同时记录全局滚动条自身的滚动条,这样就能大大的提升了我们的用户体验例子源码官方 前言 从Vue.js 2.x发布之后,陆陆续续做了七八个项目,摸索出来了一套自己的状态管理模式,我将之称为Vuet。它以规则来驱动状态更新,它带来的是开发效率上的飙升,它就像草原,而你是野马,任你随意...

    赵连江 评论0 收藏0
  • H5下拉刷新和拉加实现原理浅析

    摘要:原文链接前言在移动端网页中,下拉刷新和上拉加载更多数据的交互方式出现频率很高,开源社区也有很多类似的解决方案,如,库等。 原文链接:justrockit.top 前言 在移动端H5网页中,下拉刷新和上拉加载更多数据的交互方式出现频率很高,开源社区也有很多类似的解决方案,如iscroll,pulltorefresh.js库等。下面是对这两种常见交互基本实现原理的阐述。 实现原理 下拉刷新...

    X1nFLY 评论0 收藏0

发表评论

0条评论

junbaor

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<