vue使用keep-alive保持滚动条位置 (vux组件库中的Scroller及普通元素)

  • 需求:h5列表页,向上滑动加载下一页。点进详情页,返回列表保持滚动位置不变
  • 相关组件:vue中的keep-alive、vux组件库中的Scroller
  • 方法:其实就是通过页面组件所在的上层keepAlive组件,暴力操控该对象中的cache列表
  • 参考文章:https://github.com/vuejs/vue/issues/6509
  • 具体文件
    app.vue
<template>
// 先在vuex的state中定义keepAliveList数组(因为include需要绑定数组)
// key是:当前路由,这个路由字符串就相当于属性名
// include数组中包含各个组件的name值,存在数组中的name组件会被缓存
// include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
// https://cn.vuejs.org/v2/api/#keep-alive 详细参考官方文档
  <div id="app">
    <view-box ref="viewBox">
      <keep-alive :include="keepAliveList">
        <router-view :key="key"></router-view>
      </keep-alive>
    </view-box>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
export default {
  name: 'app',
  computed: {
    ...mapGetters(['keepAliveList']),
    key() {
      return this.$route.path
    }
  },
  data(){
    return{

    }
  },

index.js

import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)

// 路由文件不需要写meta和beforeEach导航守卫
const createRouter = () => new Router({
  routes: [{
      path: '/project/list',
      name: '项目列表',
      component: () => import('@/pages/busModule/project/projList')
    },
    {
      path: '/project/screen',
      name: '项目筛选页',
      component: () => import('@/pages/busModule/project/projScreen'),
    },
    {
      path: '/project/detail',
      name: '项目详情页',
      component: () => import('@/pages/busModule/project/projDetail'),
    },
    {
      path: '/project/pay',
      name: '项目详情下的付款计划子表',
      component: () => import('@/pages/busModule/project/payPlan'),
    },
    {
      path: '/project/contract',
      name: '项目详情下的详情合同子表',
      component: () => import('@/pages/busModule/project/projContract'),
    },
    {
      path: '/project/payment',
      name: '项目详情下的详情付款子表',
      component: () => import('@/pages/busModule/project/projPayment'),
    }],
})
const router = createRouter()

export default router

vuex
state.js

// 该数组需要绑定给include 
const state = {
    keepAliveList: []
};

export default state

mutations.js

import * as types from './mutations_types'

const mutations = {
	// findIndex 查找满足这个条件的索引,如果传入的name值相等,则获取该name值在数组中的索引
    [types.DEL_KEEP_ALIVE_LIST] (state, aliveList) {
        const index = state.keepAliveList.findIndex(item => (item === aliveList))
        // console.log('index', index)
        state.keepAliveList.splice(index, 1)
    },
	//some 是否有一项满足,传进来的name值在数组中遍历,如果在数组中存在,则return,如果在数组中不存在则push到数组中
    [types.SET_KEEP_ALIVE_LIST] (state, aliveList) {
        const flag = state.keepAliveList.some(item => (item === aliveList))
        if (flag) return
        state.keepAliveList.push(aliveList)
    },
};

export default mutations

mutations_types.js

export const SET_KEEP_ALIVE_LIST = 'SET_KEEP_ALIVE_LIST';
export const DEL_KEEP_ALIVE_LIST = 'DEL_KEEP_ALIVE_LIST';

actions.js

import * as types from './mutations_types';

const actions = {
	set_keep_alive_list: ({
		commit
	}, aliveList) => {
		return new Promise((resolve, reject) => {
			commit(types.SET_KEEP_ALIVE_LIST, aliveList);
			resolve()
		});
	},
	del_keep_alive_list: ({
		commit
	}, aliveList) => {
		return new Promise((resolve, reject) => {
			commit(types.DEL_KEEP_ALIVE_LIST, aliveList);
			resolve()
		});
	}
};
export default actions

getters.js

// getters,可以认为是store的计算属性,就是在某个数据在经过一系列的变化之后,才显示在页面上,这个时候就需要用到计算属性。
const getters = {
  keepAliveList: state => state.keepAliveList
};
export default getters

projList.vue

<template>
  <scroller v-if="listView" lock-x class="scrollerBox" @on-scroll-bottom="onScrollBottom" @on-scroll="onScroll" ref="scrollerBottom" :scroll-bottom-offst="0" style="padding-bottom:0;">
    <div class="box">
      <div class="tabHead">
        项目列表
      </div>
      <div v-for="(item, index) in list" :key="index" @click="gotoDetail(item.projId)">
      </div>
    </div>
  </scroller>
</template>

<script>
export default {
  // 给当前页面起个名字
  name: 'projList',
  data(){
    return{
      list: [],
      listParam: {},
      timer: null
    }
  },
  activated() {
    // 从详情页返回,给当前组件设置记录的滚动高度
    this.$nextTick(() => {
      this.$refs.scrollerBottom._xscroll.scrollTop(this.scrollTop)
    })
  },
  // 离开路由之前执行的函数
  // 如果跳转的是详情页,this.$vnode 相当于当前页面,将当前页面cache(缓存)给属性名为 /project/list 的值
  // 再将当前页面的名字存到vuex中
  // 如果跳转的不是详情页,那就把这个list页面缓存删掉
  beforeRouteLeave(to, from, next) {
    if (to.path == '/project/detail') {
      // console.log('this.$vnode', this.$vnode)
      this.$vnode.parent.componentInstance.cache['/project/list'] = this.$vnode
      this.$store.dispatch('set_keep_alive_list', 'projList')
    } else {
      delete this.$vnode.parent.componentInstance.cache['/project/list']
    }
    // 跳转下一个页面
    next()
  },
  created () {
     this.getList(this.listParam)
  },
  mounted () {
  	// 首次进来置为 0 
    this.$nextTick(() => {
      this.$refs.scrollerBottom._xscroll.scrollTop(0)
    })
  },
  methods:{
    getList (params) {}
    },
    // 上拉刷新
    onScrollBottom() {
      clearTimeout(this.timer)
      this.timer = setTimeout(() => {
         if (!this.noData) {
            let page = ++this.listParam.page
            this.listParam.page = page
            this.$nextTick(() => {
              this.getList(this.listParam)
            })
          }
       }, 200)
    },
    // 组件api,当组件触发滚动时获取滚动位置到顶部的top值
    onScroll(position) {
      this.scrollTop = position.top
    },
    // 跳转详情页
    gotoDetail(id) {
      this.$router.push({
        path: '/project/detail',
        query: {objId: id}
      })
    }
  },
}
</script>
  • 非滚动组件,记录滚动条位置 (长图详情页跳转子表,返回记录滚动位置)

projDetail.vue

<template>
  <div></div>
</template>

<script>
export default {
  // 给当前页面起个名字
  name: 'projDetail',
  data () {
    return {
      form: {},
      detailParams: {
        projId: '',
      },
    }
  },
  created () {
    this.getInfo(this.$route.query.objId)
  },
  methods: {
    getInfo(objId) {},
  },
  activated() {
  	// 当从详情页子表回来执行该函数,给详情页设置滚动距离
    this.$nextTick(() => {
      document.querySelector('#vux_view_box_body').scrollTop = this.scrollTop;
    })
  },
  mounted () {
    // 首次进入详情页,滚动高度为0
    this.$nextTick(() => {
      document.querySelector('#vux_view_box_body').scrollTop = 0;
    })
  },
  beforeRouteLeave(to, from, next) {
  	// 由于有三个子表,给放到一个数组中
    const keys = ['/project/pay', '/project/contract', '/project/payment']
    // 如果当前数组中有这一项 则设置当前滚动高度,缓存当前页面,并将当前name值存到vuex中
    // 如果去往的路由不在该数组中,则删除当前页面缓存 
    if (keys.includes(to.path)) {
      this.scrollTop = document.querySelector('#vux_view_box_body').scrollTop
      this.$vnode.parent.componentInstance.cache['/project/detail'] = this.$vnode
      this.$store.dispatch('set_keep_alive_list', 'projDetail')
    } else {
      delete this.$vnode.parent.componentInstance.cache['/project/detail']
    }
    next()
  }
}
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值