2.0.2, 2.0.1
Go to… linksGo back links until you're backscrollBehavior callback should return all saved positions on all pages back to the start page.
Instead the callback returns all except the very first page saved positions. It looks like there should be some replaceState call with a generated key when history mode initializes to be able to catch the key later in popstate.
The third argument, savedPosition,
is only available if this is a popstate navigation
(triggered by the browser's back/forward buttons).
Click <router-link> won't work.
As above, thanks for the explanation by @woshizja.
http://router.vuejs.org/en/advanced/scroll-behavior.html
Unfortunately, you're wrong because clicking on the tag only emulates clicking the back button. There's a popstate event listener in the code. You can run it outside the fiddle and click the back button, it won't fix the issue. :)
when trigger browser's back, the browser itself will scroll to original location,
so, vue DON'T need to save the page's scrolling position.
Yes, you're right about the browser scrolling behavior but it's inconsistent in a way you have to write tests and look at the source to know savedPosition is null on unvisited pages (which is right) and undefined on the first page.
The official docs has this sample:
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
Maybe it's better to rewrite this sample as the following?
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else if (savedPosition === null) {
return { x: 0, y: 0 }
}
}
It works okay in this way.
I read the vue-router.js source code, when you click browser history back button,the following things will happen:
window.addEventListener('popstate', function (e) {
_key = e.state && e.state.key
var current = this$1.current
this$1.transitionTo(getLocation(this$1.base), function (next) {
if (expectScroll) {
this$1.handleScroll(next, current, true)
}
})
})
Note that the original history entry (for http://example.com/example.html) has no state object associated with it.
So, _key will be null.
handleScroll function:HTML5History.prototype.handleScroll = function handleScroll (to, from, isPop) {
......
// wait until re-render finishes before scrolling
router.app.$nextTick(function () {
......
})
};
return HTML5History;
}(History));
$nextTick function, so program will run into it:// content of $nextTick()
var position = getScrollPosition(_key)
// injection position only when isPop
var shouldScroll = behavior(to, from, isPop ? position : null)
if (!shouldScroll) {
return
}
var isObject = typeof shouldScroll === 'object'
// deal hash string ......
if (position) {
window.scrollTo(position.x, position.y)
}
Because the _key is null, so getScrollPosition(_key) will return null.
My config:
const router = new VueRouter({
routes,
mode: 'history',
scrollBehavior(to, from, savedPosition) {
console.log(savedPosition)
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 100 }
}
}
});
shouldScroll will be {x:0, y:0}, program will run window.scrollTo(position.x, position.y), then page will scroll to top temporarily.
Continue running the program, The page will scroll to its original location, scrollTop(0, 100) will be invalid!
It doesn't work like the doc said:
This will simply make the page scroll to top for all route navigations.
I am very confused!
vue-router will listen 'scroll' event:if (expectScroll) {
window.addEventListener('scroll', function () {
saveScrollPosition(_key)
})
}
_key is null, won't save position.
But, when you already back to last page,your scroll behavior also trigger this,and the _key will have a value, because:
var genKey = function () { return String(Date.now()); }
var _key = genKey()
scrollBehavior.不太理解在以下情况:
<router-link>跳转;$router.push());vue-router处理scroll behavior是什么策略?
_key和时刻监听scroll事件以savePosition的设计是为什么?
@fnlctrl 能解答以下我的疑惑吗?
yep big problem, because not even afterEach: => window.scrollTo(0,0) works anymore.
Hi, I've got a similar problem. I'm not sure if it should be reopened or it is a different bug..
Version of vue-router - [2.5.3]
I render page on backend. URL is exactly the same as requested(i think this is important).
new _key generated. For example 100
history.state have some garbage from previous attempts. Lets say 234
I click on<route-url>
before route changes it try to save current offset by key 100, but current history.state has the same garbage (234). Its not used anywhere for now.
new _key generated(lets say 300), offset stored by this key.. and pushState({key:300}) performed its not important now.
Then i click Back. popstate fired, event has 234 key, and vue-router tries to get offset from 234 which is obviously undefined.
Then I discovered that doing something like router.history.replace will force write current key to current state and everything will be just great, but it is encapsulated so much so I forced to write this report instead of putting in some cludge and go resting :+1: .
The solution:
Looks like history.ensureURL should do exact the same native way, but condition does not satisfied for some reason to fire the replaceState with current _key
I've been struggling with the same problem.
When the router is initialized, the current route R1 does not have state registered in the history (window.history.state -> null). Let's say that R1 contains a long list view and the user scrolls to the bottom, so that window.scrollY becomes 2500.
Now the user navigates to another route R2 by clicking one of the list items. The scroll position of R1 ({ x: 0, y: 2500 }) is saved to the positionStore (scroll.js#L7). When clicking the back button, popstate event callback receives info about R1. Since it does not have its history state registered (e.state = null), no expected scrolling takes place. That's where it breaks.
My current workaround is to replace the history state of the current route when the router is initialized. I just added pushState(cleanPath(this.getCurrentLocation()), true) to the HTML5History constructor. The critical part is to have the generated key added to the first route's state.
@kadfak could you provide a code example of your solution? this bug/inconsistency is causing some headache
Most helpful comment
I read the
vue-router.jssource code, when you click browser history back button,the following things will happen:1.popstate event triggered,then we here:
Note that the original history entry (for http://example.com/example.html) has no state object associated with it.
So,
_keywill be null.2.Then, the program will run into this
handleScrollfunction:3.There is a
$nextTickfunction, so program will run into it:Because the
_keyis null, sogetScrollPosition(_key)will return null.My config:
shouldScrollwill be{x:0, y:0}, program will runwindow.scrollTo(position.x, position.y), then page will scroll to top temporarily.Attention: temporarily!
Continue running the program, The page will scroll to its original location,
scrollTop(0, 100)will be invalid!It doesn't work like the doc said:
I am very confused!
4.
vue-routerwill listen 'scroll' event:_keyis null, won't save position.But, when you already back to last page,your scroll behavior also trigger this,and the
_keywill have a value, because:Now, I'm not sure I know how to use vue-router
scrollBehavior.