Vue记住滚动条位置 scrollBehavior + debounce

2023-10-26

使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 可以自定义路由切换时页面如何滚动。

注意:这个功能只在支持history.pushState的浏览器中可用

1. 先给占位符加一层缓存

Home 是需要记住滚动条位置的组件

<!-- 路由占位符 -->
<keep-alive include="Home">
    <router-view></router-view>
</keep-alive>

2. 在路由上加一个源信息meta,记录滚动条的top值

const routes = [
  {
    path: '/home',
    component: Home,
    meta: {
      isRecord: true,
      top: 0
    }
  }
]

3. 使用 scrollBehavior

​ 在文档页面(http://localhost:8080/document)拉动滚动条,然后刷新浏览器会发现滚动条依然在原来的位置,这是浏览器的默认行为,会记录浏览器滚动条默认位置。

​ 但是点击浏览器“前进/后退”按钮,会发现当初那个页面的滚动条从0开始了,没有记录上一次滚动条的位置。现在要求点击浏览器“前进/后退”按钮,页面滚动条要记录上一次的位置,这时需要设置它的的滚动行为。

​ 这时候需要在路由配置中设置 scrollBehavior(to,from,savePosition)函数,函数有三个参数。scrollBehavior() 函数在点击浏览器的“前进/后退”,或者切换导航的时候触发

  • to:要进入的目标路由对象,到哪里去
  • from:离开的路由对象,哪里来
  • savePosition:会记录滚动条的坐标,点击前进/后退的时候记录值{x:?,y:?}
const router = new VueRouter({
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // return 期望滚动到哪个的位置
  }
})

第三个参数 savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。
在该方法内,可以通过判断路由to,from两个对象来做一些必要的判断;
savedPosition 参数是记录的上次滚动的位置;
通过return {x:number,y:number}来控制页面滚动的位置;

  • 对于所有路由导航,简单地让页面滚动到顶部。
scrollBehavior (to, from, savedPosition) {
    return{x:0, y:0}
}
  • 想要在后退时,滚动到上次滚动的位置,如果满足条件,savedPosition有值的情况下:
scrollBehavior (to, from, savedPosition) {
  if (savedPosition) {
    return savedPosition
  } else {
    return { x: 0, y: 0 }
  }
}
  • 新增情况:异步滚动

    项目中经常会遇到异步请求接口数据,这些异步操作通常会放在created、mounted钩子里面
    分析:通过在不同的生命周期钩子和scrollBehavior()使用console.log()打印顺序如下:

    • created钩子

    • mounted钩子

    • to参数: {} from参数: {}

    • 异步请求数据:get data

    • 打印位置信息 savedPosition {x: 0, y: 630}

      可知:必须使用异步滚动,否则,请求的数据重新赋值,将导致页面重新渲染

当页面数据需要请求加载有延迟的情况下,页面如果直接滚动,会出现滚动后,页面数据请求回来,DOM重新渲染,滚动失效的情况;

必须使用异步滚动,利用setTimeout跳出主线程将回调事件放到队列中。由于mouted比scrollBehavior函数早执行,所以异步请求的回调事件优先进入队列,接下去才是setTimeout的回调事件。根据队列 先进先出的原理。先执行了异步请求回调事件对data中的变量a做赋值操作。此时相当于这已经是个静态页面了,接着我只要执行return { x:0, y: 100 }。这样就已经触发了页面滚动到100px的效果。但是由于data数据发生改变,页面重新渲染又回到顶部。这时整个轻触滚动效果已经暗中执行完成,不会再出现遮罩层了。

官方文档给补充了异步滚动的方法:

scrollBehavior (to, from, savedPosition) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ x: 0, y: 0 })
    }, 500)
  })
}

这个会在返回后,有一定延迟再滚动,可以根据自己项目的具体情况进行一定修改,兼容;

  • 最终结果

    // 创建路由对象
    const router = new VueRouter({
      routes,
      scrollBehavior(to, from, savedPosition) {
        console.log(to.meta.top)
        // if (savedPosition) {
        //   return savedPosition
        // } else {
        //   return { x: 0, y: to.meta.top || 0 }
        // }
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            if (savedPosition) {
              resolve(savedPosition)
            } else {
              resolve({ x: 0, y: to.meta.top || 0 })
            }
          }, 100)
        })
      }
    })
    

4. 给页面添加事件监听

Home.vue

// 当组件第一次被创建的时候 先触发 created 再触发 activated
// 之后被激活的时候 触发 activated
activated() {
    fn = this.recordTopHandler()
    // 添加滚动的事件监听
    window.addEventListener('scroll', fn)
},
// 当组件被缓存的时候 触发 deactivated
deactivated() {
   // 移除事件监听
   window.removeEventListener('scroll', fn)
 }

fn 函数 用来记录滚动条的位置

  • 使用了一个防抖函数 _.debounce(func, [wait=0], [options=]),官方文档:lodash.debounce
  • _.debounce方法的作用是防抖动,当你的事件在不断触发的时候,会根据你设置的间隔时间只触发一次回调。也就是说当调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。(空闲时间大于,设定的时间是才会执行!!!)
recordTopHandler() {
    return _.debounce(
        () => {
            this.$route.meta.top = window.scrollY
        },
        50,
        { trailing: true }
    )
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Vue记住滚动条位置 scrollBehavior + debounce 的相关文章

  • 如何将 Live API for Contacts 的响应中的哈希值转换为文本

    我集成了 Live JS api 来获取用户的实时联系人 它以哈希格式 email hash 返回电子邮件 我如何使用 javascript 或 c net 转换为可读文本 非常感谢 我遇到了同样的问题 并且找到了解决方案 您所需要做的就是
  • 如何查询和过滤 Firebase 实时数据库 [重复]

    这个问题在这里已经有答案了 我想从数据库中获取所有人员 其中名字和姓氏由用户输入给出 到目前为止 这是我的代码 admin database ref persons orderByChild Firstname equalTo firstN
  • 从多层嵌套数组 JavaScript 中获取所有键值

    我有一个这样的对象 var data id 36e1e015d703120058c92cf65e6103eb title Alex McGibbon id 60beb5e7d7600200e5982cf65e6103ad title Ale
  • RxJS - 我需要取消订阅吗

    如果我有这样的事情 class MyComponent constructor this interval Observbale interval 1000 const c new MyComponent const subscriptio
  • Javascript 函数指针,以参数作为函数中的参数

    不确定标题的措辞是否正确 或者是否有更好的表达方式 但我认为还可以 无论如何 到目前为止我了解以下内容 a b a b c foo 其中 foo 是在其他地方定义的函数 不接受任何参数 只会导致函数 a b 使用上述参数运行 然后可以在函数
  • 使用ajax发送表单数据

    我想用 ajax 以表单形式发送所有输入 我有一个这样的表单
  • 在 d3 中应用转换时出现错误

    我正在尝试对我在 d3 中设计的条形图应用一些过渡效果 这是我的代码 svg selectAll bar data data enter append g attr class bar append rect attr rx barRadi
  • Sequelize 关联 - 请改用 Promise 风格

    我正在尝试将 3 张桌子连接在一起Products Suppliers and Categories然后排SupplierID 13 我读过了如何在sequelize中实现多对多关联 https stackoverflow com a 25
  • 使用 jQuery live() 初始化插件?

    使用 jQuery 在特定类的所有当前和未来元素上自动初始化插件的最佳方法是什么 例如 假设我想要全部
  • 如何对像 Excel Pivot 这样两个键必须匹配的数组求和?

    我尝试对 Datum 和 Material 必须匹配的所有 Menge 和 Fehler 值求和 结果应类似于 Excel 数据透视表 到目前为止 这是我的代码 但我不知道如何添加也必须匹配的第二个键 Material 我希望你能理解我试图
  • Durandal SPA 与打字稿有关的问题

    我使用 TypeScript 1 8 将我的 durandal SPA 应用程序从 VS 2012 更新到 VS 2015 它将生成 JavaScript ECMA5 我解决了所有构建错误 但我无法修复一个名为 return 语句只能在函数
  • 如何使用 JavaScript 压缩文件?

    有没有办法使用 JavaScript 来压缩文件 例如 在雅虎邮件中 当您选择下载电子邮件中的所有附件时 它会被压缩并下载到单个 zip 文件中 JavaScript 能够做到这一点吗 如果是这样 请提供一个编码示例 我发现这个图书馆叫js
  • 如何使用javascript将视频文件转换为字符串?

    我在 signalR 工作 我想通过将视频文件拆分为不同部分来将视频文件从一个客户端发送到另一个客户端 我已经通过分割图像源数据发送图像并在另一个客户端上接收该图像 document getElementById fileUpload ad
  • 如何在没有 jQuery 或延迟加载的情况下推迟背景图像

    根据帕特里克 塞克斯顿tutorial https varvy com pagespeed defer images html 我想以与这里相同的方式推迟背景图像img img src data image png base64 R0lGO
  • Lodash _.hasIntersection?

    我想知道两个或多个数组是否有共同的项目 但我不在乎这些项目是什么 我知道 lodash 有一个 intersection方法 但我不需要它来遍历每个数组的每个项目 相反 我需要类似的东西 hasIntersection一旦找到第一个常见的出
  • 使标签充当输入按钮

    我怎样才能做一个 a href http test com tag test Test a 就像表单按钮一样 通过充当表单按钮 我的意思是 当单击链接执行操作时method get 或 post 以便能够通过 get 或 post 捕获它
  • 如何从 Visual Studio Code API 打开浏览器

    我只是在探索一种从用于开发扩展的 Visual Studio Code API 打开默认浏览器的方法 以下是我的代码 var disposable vscode commands registerCommand extension brow
  • 数组长度未定义[关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions 我试图按如
  • 如何在 JavaScript 中将日期时间微格式转换为本地时间?

    我有一个页面当前正在使用日期时间微格式 http microformats org wiki datetime design pattern显示时间戳 但我只显示我自己的时区的人类可读时间
  • 了解客户端文件的对象 URL 以及如何释放内存

    我在用createObjectURL获取本地图像文件的引用 URL 当我完成文件 图像后 我打电话revokeObjectURL释放该内存 一切对我来说都很好 但我只是想确保我释放了我能释放的所有内存 我检查后出现了我的担忧chrome b

随机推荐

  • 在spring引入log4j

    在spring中使用log4j 引入log4j软件包 配置log4j属性 加载log4j配置文件 默认加载 手动加载 使用logger 本文的整体代码结构是在已经引入spring基本应用的前提下 在spring配置文件中通过 Bean注解创
  • git---查看当前账户和切换账户

    查看当前用户名和邮箱 git config user name git config user email 修改用户名和邮箱 git config global user name Your username git config glob
  • GPT-Tools Cookbook by Eric

    1 账号申请和登录 1 1 OpenAI 账号注册 可以参考 2 ChatGPT 的注册与登录 来自 涛哥ChatGPT和Python变现圈 选择账号邮箱 我们在尝试过直接使用邮箱注册OpenAI账号 试验了两次 都没有成功 Note 第二
  • SpringBoot 集成SpringBatch 批处理框架

    SpringBatch 核心组件简介 1 JobRepository 用来注册Job容器 设置数据库相关属性 2 JobLauncher 用来启动Job的接口 3 Job 我们要实际执行的任务 包含一个或多个 4 Step 即步骤 包括 I
  • 自然语言处理实战项目18-NLP模型训练中的Logits与损失函数的计算应用项目

    大家好 我是微学AI 今天给大家介绍一下 自然语言处理实战项目18 NLP模型训练中的Logits与损失函数的计算应用项目 在NLP模型训练中 Logits常用于计算损失函数并进行优化 损失函数的计算是用来衡量模型预测结果与真实标签之间的差
  • QQkey后门

    今天凌晨收到 LX2222 的举报反馈被盗号 发布的加速器盗取账号信息 https www 52pojie cn thread 785555 1 1 html 我们着手分析一下看看软件确实发现了问题 被添加了盗取QQkey的后门 通过拿到Q
  • SQL查询语句之查询数据

    目录 1 基本查询语句 2 单表查询 2 1 查询所有字段 1 列出表中的所有字段 2 使用 查询所有字段 2 2 查询指定字段 2 3 查询指定记录 2 4 带 in 关键字的查询 2 5 带 between and 的范围查询 2 6
  • HTML中Table表格的使用与漂亮的表格模板

    1 表格标记 表格是网页中十分重要的组成元素 表格用来存储数据 包含标题 表头 行和单元格 在HTML语言中 表格标记使用符号 table 表示 定义表格光使用 table 是不够的 还需要定义表格中的行 列 标题等内容 标记 说明 表格标
  • Vue2 项目里,使用 Element 的 dialog 里嵌套 tabs,导致关闭 dialog 时浏览器卡死问题

    Vue2 项目里 使用 Element 的 dialog 里嵌套 tabs 导致关闭 dialog 时浏览器卡死问题 解决办法 给 tab 添加 v if 绑定 dialog 的 visible sync
  • 如何查看awr/statspack报表,来定位系统的问题

    author skate time 2010 03 25 如何查看awr statspack报表 来定位系统的问题 数据库的性能指标一般都有什么 只有定好指标才能判定系统的性能 性能参考指标一般有如下几个 1 响应时间 平均每事务的响应时间
  • QT 使用QLibrary加载动态库

    简介 使用QLibraryi可以在程序运行时加载动态链接库 一个QLibrary的实例作用于一个单一的共享库上 QLibrary提 供了一种平台无关的方式访问库中的函数 可以在构建QLibrary的实例时将要加载的库文件传入 也可以在创建
  • python/元组、列表、字典

    Python 的元组与列表类似 不同之处在于元组的元素不能修改 元组使用小括号 列表使用方括号 字典使用 下面用一个实例来体验一下 s list input r 创建一个字典 for i in s r i r get i 0 1 看下文解释
  • 引爆点--读书有感

    有个理念一直在我心里 都说流行都是轮回 如何去让产品抓住流行趋势 究竟是什么造就了流行 如何去学习 看看周围的世界吧 它看上去似乎雷打不动 无法改变 但只要你找准位置 轻轻一触 它就可能倾斜 书籍理念 个别人物法则 附着力因素法则和环境威力
  • 单片机控制直流电机(风扇)电路详解

    单片机引脚为什么无法直接控制电机或风扇 我们在使用单片机去控制 5V的直流电机或者散热风扇时 可能会有一种疑惑 51单片机的引脚电压为 5V 为什么不直接用单片机引脚去驱动电机或者风扇 实际上单片机的控制引脚 不管是51单片机或者stm32
  • Linux安装python显示“软件包python没有可安装候选”

    在Linux中安装python时 调用 sudo apt get install python 命令出现以下问题 sudo apt get install python sudo lyx 的密码 正在读取软件包列表 完成 正在分析软件包的依
  • java中public/private/protected的具体区别

    java中public private protected的具体区别 public public表明该数据成员 成员函数是对所有用户开放的 所有用户都可以直接进行调用 private private表示私有 私有的意思就是除了class自己
  • 深度学习——更深层次的神经网络

    一个深层次的CNN网络结构 这里使用的卷积层全都是3 3的小型滤波器 特点是随着层的加深 通道数变大 卷积层的通道数从前面的层开始按顺序以16 16 32 32 64 64的方式增加 此外 插入了池化层 以逐渐减小中间数据的空间大小 并且
  • C++ 字符串

    C 提供了以下两种类型的字符串表示形式 C 风格字符串 C 引入的 string 类类型 C 风格字符串 C 风格的字符串起源于 C 语言 并在 C 中继续得到支持 字符串实际上是使用 null 字符 0 终止的一维字符数组 因此 一个以
  • OpenMV的单颜色识别讲解

    OpenMV的官方教程 寻找色块 single color rgb565 blob tracking示例讲解 视频讲解 需要提前看的文章 程序烧录 颜色阈值设置 目录 threshold index和thresholds解析 固定代码部分
  • Vue记住滚动条位置 scrollBehavior + debounce

    Vue记住滚动条位置 1 先给占位符加一层缓存 2 在路由上加一个源信息meta 记录滚动条的top值 3 使用 scrollBehavior 4 给页面添加事件监听 使用前端路由 当切换到新路由时 想要页面滚到顶部 或者是保持原先的滚动位