/* @flow */import type Router from '../index'
import {
assert } from './warn'
//getStateKey: 用于获取时间戳key;
//setStateKey: 用于设置时间戳key;
import {
getStateKey, setStateKey } from './state-key'
//浅拷贝对象的属性。
import {
extend } from './misc'//用于保存对应的页面的 scrollposition 位置。
const positionStore = Object.create(null)export function setupScroll() {
/*history.scrollRestoration。它提供两个值,auto,作为它的默认值,可以像你所见的大多数情况一样工作,另一个manual,意味着作为一个开发者你拥有了自主掌控任何所需的scroll改变,当用户循环往复于app的history中。如果需要,你可以跟踪scroll的位置轨迹,当你使用history.pushState(),push history的时候。*///用于组织浏览器在 history popstate 事件中自动滚动窗口。// Prevent browser scroll behavior on History popstateif ('scrollRestoration' in window.history) {
window.history.scrollRestoration = 'manual'}// Fix for #1585 for Firefox// Fix for #2195 Add optional third attribute to workaround a bug in safari https://bugs.webkit.org/show_bug.cgi?id=182678// Fix for #2774 Support for apps loaded from Windows file shares not mapped to network drives: replaced location.origin with// window.location.protocol + '//' + window.location.host// location.host contains the port and location.hostname doesn't//浏览器地址为: http://192.168.10.73:10095/message/inforList//protocolAndPath: "http://192.168.10.73:10095"const protocolAndPath = window.location.protocol + '//' + window.location.host//absolutePath: message/inforListconst absolutePath = window.location.href.replace(protocolAndPath, '')// preserve existing history state as it could be overriden by the user// stateCopy: {
// key: "777.900"// }const stateCopy = extend({
}, window.history.state)//替换掉 stateCopy 的 key 的值。stateCopy.key = getStateKey()//repalce 方式跳转到 absolutePath 路径。window.history.replaceState(stateCopy, '', absolutePath)//开始监听 history 的 popstate 事件。window.addEventListener('popstate', handlePopState)return () => {
//移除对 history 的 popstate 事件的监听。window.removeEventListener('popstate', handlePopState)}
}/*handleScroll() 函数router: 路由对象。to: 前往的路由from: 上一个路由。isPop: 是否是退出。 */
export function handleScroll(router: Router,to: Route,from: Route,isPop: boolean
) {
//不存在 router 当前指向的 vue 实例。if (!router.app) {
return}//获取创建 router 时,配置的 scrollBehavior 函数。const behavior = router.options.scrollBehavior//如果 scrollBehavior 没有配置,则直接返回。if (!behavior) {
return}//scrollBehavior 必须是一个函数。否则警告提示。if (process.env.NODE_ENV !== 'production') {
assert(typeof behavior === 'function', `scrollBehavior must be a function`)}// wait until re-render finishes before scrolling//在下一个 event loop 周期执行一下函数。router.app.$nextTick(() => {
//获取最后一次保存的滚动位置记录。const position = getScrollPosition()//shouldScroll 是对于滚动位置的配置。const shouldScroll = behavior.call(router,to,from,isPop ? position : null)//如果shouldScroll 为 null。只直接返回。if (!shouldScroll) {
return}//如果 shouldScroll 是 prommise 对象。if (typeof shouldScroll.then === 'function') {
//通过 promise.then() 的方式获取要滚动的坐标。shouldScroll.then((shouldScroll) => {
scrollToPosition((shouldScroll: any), position)}).catch((err) => {
//如果不是生产环境,且产生了异常,则输出错误信息。if (process.env.NODE_ENV !== 'production') {
assert(false, err.toString())}})} else {
//滚动到指定的位置。// shouldScroll: 是关于滚动行为的配置。// position: 是上一次窗口滚动的位置。scrollToPosition(shouldScroll, position)}})
}/*** 保存当前滚动的位置。*/
export function saveScrollPosition() {
//获取一个根据当前时间的时间戳生成的key。const key = getStateKey()//如果key存在if (key) {
//则 positionStore[key] 形式记录滚动的x,y位置。positionStore[key] = {
x: window.pageXOffset,y: window.pageYOffset,}}
}/* 用户触发了 popState 事件时,会调用 handlePopState() 方法。1、调用history.pushState()或者history.replaceState()不会触发popstate事件. 2、popstate事件只会在浏览器某些行为下触发, 比如:(1)用户主动触发的:点击后退、前进按钮。(2)代码主动触发:在JavaScript中调用history.back()、history.forward()、history.go()方法。 */
function handlePopState(e) {
//记录当前 window 滚动的位置。saveScrollPosition()//e.state 就是 pushState, replaceState 的第一个参数。if (e.state && e.state.key) {
//更新存储的 key。setStateKey(e.state.key)}
}/*获取最后一次保存的滚动记录。 */
function getScrollPosition(): ?Object {
//获取时间戳生成的 key。const key = getStateKey()//如果 key 存在,返回以该 key 存储的滚动位置记录。if (key) {
return positionStore[key]}
}/*el: 被 selector 选择器指定的 dom 节点。offset: 偏移量 */
function getElementPosition(el: Element, offset: Object): Object {
const docEl: any = document.documentElement//Element.getBoundingClientRect() 方法返回元素的大小及其相对于视口的位置。const docRect = docEl.getBoundingClientRect()//获取被选中元素的元素的大小,以及相对于视口的位置。const elRect = el.getBoundingClientRect()return {
//elRect.left - docRect.left 表示相对于网页 x 轴方向的间距。x: elRect.left - docRect.left - offset.x,//elRect.top - doctRect.top 表示相对于网页 y 轴方向的间距。y: elRect.top - docRect.top - offset.y,}
}/*isValidPosition() 判断是不是可用的坐标。只需要 x 或者 y 一个方向有值即可。 */
function isValidPosition(obj: Object): boolean {
//判断 x 或者 y 坐标有值,且不为0;return isNumber(obj.x) || isNumber(obj.y)
}/*归一化坐标位置。 */
function normalizePosition(obj: Object): Object {
//针对只需要 x 或者 y 方向滚动的坐标。不需要滚动的坐标进行数据补齐。return {
x: isNumber(obj.x) ? obj.x : window.pageXOffset,y: isNumber(obj.y) ? obj.y : window.pageYOffset,}
}/*归一化偏移位置 */
function normalizeOffset(obj: Object): Object {
return {
x: isNumber(obj.x) ? obj.x : 0,y: isNumber(obj.y) ? obj.y : 0,}
}/*判断是不是 number 类型的数据。 */
function isNumber(v: any): boolean {
return typeof v === 'number'
}//用于判断是不是 “#数字” 的形式开头的字符串的 的正则表达式对象。
const hashStartsWithNumberRE = /^#\d//*** position: 为上一次保存下来的滚动坐标。* shouldScroll: 包含滚动位置,元素等的对象。 {* selector: "xxx" //如果指定了该属性,那么就是根据指定的dom元素的位置计算滚动位置。* offset: { x, y } //指定 dom 元素的偏移量。* x, //x坐标* y, //y坐标* behavior: "auto|smooth" //指定滚动行为。* }** 会将滚动坐标转化为 window 窗口的滚动坐标。*/
function scrollToPosition(shouldScroll, position) {
const isObject = typeof shouldScroll === 'object'//如果 shouldScroll.selector 存在, 且是字符串类型。// 则指定的滚动偏移位置,是针对 selector 指定的 dom 元素的偏移位置。if (isObject && typeof shouldScroll.selector === 'string') {
//获取 selector 指定的锚地 dom 节点。const el = hashStartsWithNumberRE.test(shouldScroll.selector) // $flow-disable-line? //shouldScroll.selector.slice(1) 用于去掉 # 号document.getElementById(shouldScroll.selector.slice(1)) // $flow-disable-line: document.querySelector(shouldScroll.selector)//dom元素存在if (el) {
//获取 shouldScroll 的 offset,作为要滚动的坐标。let offset =shouldScroll.offset && typeof shouldScroll.offset === 'object'? shouldScroll.offset: {
}//归一化偏移位置。offset = normalizeOffset(offset)//需要滚动到的坐标。position = getElementPosition(el, offset)} else if (isValidPosition(shouldScroll)) {
//需要滚动的坐标。position = normalizePosition(shouldScroll)}//如果 shouldScroll 是对象类型,且x,y都是数字。} else if (isObject && isValidPosition(shouldScroll)) {
position = normalizePosition(shouldScroll)}}/* 如果 scrollBehavior( to, from, savedPosition ){if( savedPosition ){return savedPosition;} else {return { x:0, y:0 }}}返回的是一个对象,则必须带有滚动的坐标。则不再使用 scrollToPosition() 的第二个参数的数据。如果返回的不是对象,则滚动的坐标就使用 scrollToPosition() 的第二个参数的数据。*///将两种滚动方式的 position 归一化为窗口滚动的坐标后if (position) {
//判断当前浏览器是否支持 scrollBehavior 的 scroll-behavior 的属性。如果支持,则可以指定 behavior 属性。// scroll-behavior 属性的值有: smooth,auto; 其中 auto 是默认值。// (1) auto: 默认值,表示滚动框立即滚动到指定位置。// (2) smooth: 表示允许滚动时采用平滑过度,而不是直接滚动到相应的位置。if ('scrollBehavior' in document.documentElement.style) {
window.scrollTo({
left: position.x,top: position.y,behavior: shouldScroll.behavior,})} else {
//将窗口滚动到 { x:"xxx", y:"xxx" } 的位置。window.scrollTo(position.x, position.y)}}
}
详细解决方案
vue-router3 源码注释系列 /src/util/scroll.js
热度:51 发布时间:2023-10-26 11:36:21.0
相关解决方案
- java.lang.NoSuchMethodError: org.springframework.util.ReflectionUtils.makeAccess,该如何解决
- jsp 页面 安插ArrayList 报错。为什么,已经加了import="java.util."了呀!
- java.util.ArrayList cannot be cast to com.hxll.hr.entity.SalaryStandard解决方法
- 项目打包后报java.lang.NoClassDefFoundError: com/sun/tools/javac/util/List错误是咋回事
- java.util.Date转 json有关问题
- java.lang.ClassCastException: java.util.ArrayList解决思路
- java.util.vector中的vector的详细用法解决方法
- org.apache.tomcat.util.http.Parameters processParameters,该怎么解决
- 对 Date的引用不明确,java.util中的类java.util.Date和java.sql中的类java.sql.Date都匹配,该如何处理
- simpleDateFormat 转换 java.util.Date解决方法
- cannot be cast to java.util.Collection解决方法
- 关于java.util.List的一个有关问题
- java.util.Timer,该如何处理
- Caused by: java.util.jar.JarException: Cannot
- ~关于java.util.Calendar.setTime(Unknown Source)的有关问题 !
- 急java.util.ConcurrentModificationException错误有关问题
- 在java.util.Date中,怎么比较两个Date的大小,精确到分
- java.util.List 求详解,该如何解决
- J# 中有没有java.util.Arrays.equals(byte[],byte[])这个步骤
- java.util.concurrent详解(二)Se地图hore/FutureTask/Exchanger
- 在rational中不能解析导入 java.util.Scanner 急解决方法
- 在rational中不能解析导入 java.util.Scanner 急该如何处理
- 在rational中不能解析导入 java.util.Scanner 急解决方案
- java.util.Date 问题
- java.util.zip.ZipException:missing entry name是什么异常?
- [求助]java.util.zip 可以这样写吗?
- [讨论]有关java.util.Hashtable类的用法
- [求助]使用java.util.Hashtable出现错误?
- java.util.Date简单操作
- 求高级线程java.util.concurrent包学习笔记