使用keep-alive保存滚动条的位置

6,520 阅读2分钟

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

两种滚动条

滚动条有两种:

  1. 整体页面滚动条:

    通过vue-router以及activated方法保存和恢复页面级别的滚动条位置。

  2. 页面内部某个组件,如table或者列表的滚动条位置:

    通过deactivated()/activated()保存组件内部某个组件的滚动条位置

组件整体的滚动条

  1. keep-alive包裹的内部组件添加id标识,以便后期设置属性或值,比如滚动条位置scrollTop:
    <!-- AppMain.vue -->
    <keep-alive :include="cachedViews">
        <router-view :id="id" :key="key" />
    </keep-alive>
  1. 路由组件添加暂存scrollTop字段

下面的关键代码在于增加scrollTop: 0

    {
      path: 'order_summary',
      component: () => import('@/views/ordermanage/OrderSummary'),
      name: 'OrderSummary',
      meta: { title: '订单数据汇总', icon: 'tab', noCache: false, roles: ['admin'], scrollTop: 0 },
      hidden: false
    }
  1. 离开此页面/路由时,将调用deactivated(),在方法内部,保存当前组件的scrollTop值到路由的scrollTop中,即上述代码的scrollTop中。但我们也可以写在vue-router的beforeEach中,这样就不需要针对每个页面的deactivated()都重写了。
其他代码...

router.beforeEach(async(to, from, next) => {
  if (from.meta.noCache === false) {
    // 启用缓存
    const $content = document.querySelector('#' + 'md5_' + md5(from.path))
    const scrollTop = $content ? $content.scrollTop : 0
    from.meta.scrollTop = scrollTop
  }
  
  // 其他代码....
}   
  1. 对应组件activated()方法设置scrollTop

只有用<keep-alive>...</keep-alive>包裹的组件才会有activated, deactivated生命周期

import md5 from 'js-md5'


activated() {
        const scrollTop = this.$route.meta.scrollTop
        const $content = document.querySelector('#md5_' + md5(this.$route.path))
        if (scrollTop && $content) {
            $content.scrollTop = scrollTop
        }
    }

备注:其中的md5是为了避免path中的非法id值字符,md5引用于js-md5 (package.json添加js-md5依赖,引用import md5 from 'js-md5')

二、组件内部的滚动条

例如某页面内部含有table,list等组件:

<el-table ref="table" ...>
    ...
</el-table>
  data() {
      return {
          tableScrollTop: 0
      }
  },
  activated() {
    // 激活后重新设置滚动条
    this.activateScroll()
  },
  mounted() {
    // 监听滚动条的位置
    this.$refs.companyTable.bodyWrapper.addEventListener('scroll', (res) => {
      const height = res.target
      this.tableScrollTop = height.scrollTop
    }, false)
  },
  beforeDestroy() {
    this.$refs.companyTable.bodyWrapper.removeEventListener('scroll', (res) => {
      const height = res.target
      this.scrollTop = height.scrollTop
    }, false)
  },
  methods: {
    activateScroll() {
      this.$nextTick(() => {
        setTimeout(() => {
          var scrollTop = this.$el.querySelector('.el-table__body-wrapper')
          scrollTop.scrollTop = this.tableScrollTop
        }, 13)
      })
      // var scrollTop = this.$el.querySelector('.el-table__body-wrapper')
      // scrollTop.scrollTop = this.tableScrollTop
    }
    .....
}