Vue.js轻松实现页面后退时,还原滚动位置

栏目: JavaScript · 发布时间: 7年前

内容简介:Vue.js轻松实现页面后退时,还原滚动位置

前言

Vue.js 2.x 发布之后,陆陆续续做了七八个项目,摸索出来了一套自己的状态管理模式,我将之称为 Vuet 。它以规则来驱动状态更新,它带来的是开发效率上的飙升,它就像草原,而你是野马,任你随意驰骋,总之它是为敏捷开发而诞生。

缘由

在大型的Vue应用程序开发中,多组件通信、多页面通信,往往是跨不过的坎,一个页面组件中往往参杂着页面获取数据的代码和响应用户操作的代码,稍有不慎,就使得代码混乱不堪。A、B、C三个页面中,都需要同样的数据,然后每一个页面都写一次、发送一次请求,不久之后,代码就十分臃肿了。因此我们就需要 vuex 这样的第三方库来管理状态了

Vuet诞生初衷

从列表点击进去到详情,从详情返回后,我们期望能显示回原来的位置,而不是整个页面重新初始化,重新请求数据,这样带来的是用户体验的极度糟糕的,我们期望能有一种规则来定义状态应该如何更新,这便是 Vuet.js 诞生的初衷。它以规则来定义状态的更新,它也是一种Vue.js全新的状态管理模式。天生的规则驱动,使得本次教程的主题,也将变得异常简单,因为我们只需要定义好页面更新的规则即可实现。

有了Vuex还需要Vuet做什么?

Vuex和Vuet的出发点不一样,Vuex不建议直接更新状态,而是通过提交mutation来更新状态,而Vuet则是允许的。因此Vuex和Vuet是可以配合使用的,并且有着不同的应用场景, 该用 Vuex的地方就用Vuex, 可用 Vuet的地方,就可以使用Vuet

开始

上面废话了那么久,也是因为 Vuet.js 才刚刚诞生,急需大家的支持。嗯,接下来我们开始本次的主题!

目录结构

|-- pages                 // 页面组件
|   |-- topic             // 主题模块
|       |-- Detail.vue    // 主题详情
|       |-- List.vue      // 主题列表
|-- router                // router相关
|   |-- index.js          // 入口文件
|   |-- router.js         // 实例化VueRouter
|-- vuet                  // vuet相关
|   |-- index.js          // 入口文件
|   |-- topic-detail.js   // 主题详情的状态
|   |-- topic-list.js     // 主题列表的状态
|   |-- vuet.js           // 实例化Vuet
|- index.html             // 程序页面入口文件
|- main.js                // Vue实例化入口文件

上面是我们本次项目的基本目录结构

依赖模块

npm install vue vue-router vuet --save

这些都是基本的模块,相比不用多说,大家都知道的。

route规则

先给出官方文档地址

本章的主题,核心就是在 route 规则身上,它能帮你获取、更新、重置页面的状态,配合 v-route-scroll 指令就能帮你处理页面的全局滚动条和div元素自身的滚动条

code社区api为例子

  • main.js

    import Vue from 'vue'
      import router from './router/'
      import vuet from './vuet/'
    
      export default new Vue({
        el: '#app',
        vuet,
        router,
        render (h) {
          return h('router-view')
        }
      })
  • vuet/index.js

    import vuet from './vuet'
    
      export default vuet
  • vuet/vuet.js

    import Vue from 'vue'
      import Vuet from 'vuet'
      import topicList from './topic-list'
      import topicDetail from './topic-detail'
    
      Vue.use(Vuet)
    
      const vuet = new Vuet({
        data () {
          return {
            loading: true, // 请求中
            loaderr: false // 请求失败
          }
        },
        pathJoin: '-', // 父子模块的连接路径
        modules: {
          topic: {
            list: topicList,
            detail: topicDetail
          }
        }
      })
    
      vuet.beforeEach(({ path, params, state }) => {
        state.loading = true
        state.loaderr = false
      })
    
      vuet.afterEach((err, { path, params, state }) => {
        state.loading = false
        state.loaderr = !!err
      })
    
      export default vuet
  • vuet/topic-list.js

    export default {
        routeWatch: 'query', // 定义页面的更新规则
        data () {
          return {
            data: [],
            tabs: [
              {
                label: '全部',
                value: 'all'
              },
              {
                label: '精华',
                value: 'good'
              },
              {
                label: '分享',
                value: 'share'
              },
              {
                label: '问答',
                value: 'ask'
              },
              {
                label: '招聘',
                value: 'job'
              }
            ]
          }
        },
        async fetch ({ route }) {
          const { tab = '' } = route.query
          const { data } = await window.fetch(`https://cnodejs.org/api/v1/topics?mdrender=false&tab=${tab}`).then(response => response.json())
          return {
            data
          }
        }
      }
  • vuet/topic-detail.js

    export default {
        routeWatch: 'params.id', // 定义页面的更新规则
        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
            }
          }
        },
        async fetch ({ route }) {
          const { data } = await window.fetch(`https://cnodejs.org/api/v1/topic/${route.params.id}`).then(response => response.json())
          return {
            data
          }
        }
      }
  • router/index.js

    import router from './router'
    
      export default router
  • router/router.js

    import Vue from 'vue'
      import VueRouter from 'vue-router'
      import TopicList from '../pages/topic/List'
      import TopicDetail from '../pages/topic/Detail'
    
      Vue.use(VueRouter)
    
      const RouterView = {
        render (h) {
          return h('router-view')
        }
      }
    
      const router = new VueRouter({
        routes: [
          {
            path: '/',
            component: RouterView,
            children: [
              {
                path: '',
                name: 'topic-list',
                component: TopicList
              },
              {
                path: '/:id',
                name: 'topic-detail',
                component: TopicDetail
              }
            ]
          }
        ]
      })
    
      export default router
    • pages/topic/List.vue

      <template>
        <!-- 
            设置指令监听全局滚动条,
            注意了,光是设置指令可不行,还需要在组件中使用route,
            来更新指令绑定的模块才行,
            局部滚动条直接去掉.window即可
            如果需要同时记录全局滚动条和div滚动条直接设置.window.self即可
        -->
        <div v-route-scroll.window="{ path: 'topic-list' }">
          <header>
            <ul>
              <li v-for="item in list.tabs">
                <router-link :to="{ name: 'topic-list', query: { tab: item.value } }">{{ item.label }}</router-link>
              </li>
            </ul>
          </header>
          <ul class="list">
            <li v-for="item in list.data">
                <router-link :to="{ name: 'topic-detail', params: { id: item.id } }">{{ item.title }}</router-link>
            </li>
          </ul>
        </div>
      </template>
      <script>
        import { mapRules, mapModules } from 'vuet'
      
        export default {
          mixins: [
            // 设置模块的更新规则
            mapRules({
              route: 'topic-list'
            }),
            // 连接模块的状态
            mapModules({
              list: 'topic-list'
            })
          ]
        }
      </script>
      <style scoped>
      
      </style>
    • pages/topic/Detail.vue

      <template>
        <!-- 
            设置指令监听全局滚动条,
            注意了,光是设置指令可不行,还需要在组件中使用route,
            来更新指令绑定的模块才行,
            局部滚动条直接去掉.window即可
            如果需要同时记录全局滚动条和div滚动条直接设置.window.self即可
        -->
        <div v-route-scroll.window="{ path: 'topic-detail' }">
          <h3>{{ detail.data.title }}</h3>
          <div v-html="detail.data.content"></div>
        </div>  
      </template>
      <script>
        import { mapRules, mapModules } from 'vuet'
      
        export default {
          mixins: [
            // 设置模块的更新规则
            mapRules({
              route: 'topic-detail'
            }),
            // 连接模块的状态
            mapModules({
              detail: 'topic-detail'
            })
          ]
        }
      </script>
      <style scoped>
      
      </style>

总结

咋的一看,Vuet看起来也不是很复杂,只需要定义好模块,然后在组件中设置对应的规则来更新模块的状态极客。


以上所述就是小编给大家介绍的《Vue.js轻松实现页面后退时,还原滚动位置》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Persuasive Technology

Persuasive Technology

B.J. Fogg / Morgan Kaufmann / 2002-12 / USD 39.95

Can computers change what you think and do? Can they motivate you to stop smoking, persuade you to buy insurance, or convince you to join the Army? "Yes, they can," says Dr. B.J. Fogg, directo......一起来看看 《Persuasive Technology》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具