Vue-router: iOS Safari on iPhone, history mode triggers page refresh

Created on 9 Nov 2016  路  17Comments  路  Source: vuejs/vue-router

Hello, came across a bug with VueRouter 2.0.1 and iOS Safari on iPhone only.

If I use the history mode of the Router which is supposed to be supported by the browser it fails and triggers a page refresh instead of changing the state.

You can try it with the official example fiddle here: http://jsfiddle.net/yyx990803/xgrjzsup/
Just change the mode to history and test iOS Safari on an iPhone.

The workaround is simple, I use userAgent detection instead of feature detection to check for iPhone and Safari and change the mode back to hash.
I have no idea of what is causing the page refresh, it's not the history API as using a regular window.History.pushState() works in the browser.

Most helpful comment

On a related note, I get the same problem when saving the web-app to home page on iOS. Removing history-mode makes it run correctly for the simulator (but not actual iphone), while having history mode on causes it to reload every time url changes.

All 17 comments

which version of iOS?

@posva iOS 10.1

Tested on the simulator and works fine 馃槄

Yes, I also tested on the Simulator and didn't catch the bug until it was reported using an iPhone SE and later confirmed on an iPhone 6.

same issue. It's works fine in simulator. But in iPhone 6S iOS 10.1 will refresh the page.

Tested on a real iPhone 6 iOS 10.1 10.0 and cannot reproduce. Might be a 10.1 Safari regression or security change...

Tested on real iPhone 6 iOS 10.1 Safari and still cannot reproduce. Please re-open if a proper reproduction can be provided.

Same here on iPhone SE (iOS 10.2) and on iPhone 6 (10.1.1), unfortunately I can't reproduce it. I have another iPhone SE with the same iOS version and it works fine there.

EDIT: Looks like this Problem only exists if you're using incognito mode.

I can confirm what @helloiamlukas reports - switching from incognito to normal mode fixed the issue. Using iPhone 6S with iOS 10.2.1

User agent of Safari:
Mozilla/5.0 (iPhone; CPU iPhone OS 10_2_1 like Mac OS X) AppleWebKit/602.4.6 (KHTML, like Gecko) Version/10.0 Mobile/14D27 Safari/602.1

I'm getting this in normal mode too. hashchange is being fired twice: first with the new route and then again with the previous one.

This dirty hack works, by blocking the consecutive call:

  setupListeners () {
    let last = 0
    window.addEventListener('hashchange', () => {
      let now = Date.now()
      if (now - last < 100) return
      last = now
      if (!ensureSlash()) {
        return
      }
      this.transitionTo(getHash(), route => {
        replaceHash(route.fullPath)
      })
    })
  }

On https://github.com/vuejs/vue-router/blob/dc9d52f0f1314ee84792350c8c412e00719c3742/src/history/hash.js#L20-L29

Can reproduce, iOS 10.2.1 on iPhone 5. Using Vue-Router in hash mode

I'm seeing this as well with 10.3.1 and history mode.

Any way around this? I need to use history mode due to hosting at S3 and needing to send to deep urls.

Similarly, vue-router fails in Safari 8 and 9 because those versions fires popstate on page load, causing it to transition to / no matter the URL.

One can normalize this behaviour by adding the popstateevent listener in a setTimeout after load, similar to this issue: https://github.com/visionmedia/page.js/issues/213

diff --git a/src/history/html5.js b/src/history/html5.js
index 3716370..cefb808 100644
--- a/src/history/html5.js
+++ b/src/history/html5.js
@@ -16,14 +16,18 @@ export class HTML5History extends History {
       setupScroll()
     }

-    window.addEventListener('popstate', e => {
-      const current = this.current
-      this.transitionTo(getLocation(this.base), route => {
-        if (expectScroll) {
-          handleScroll(router, route, current, true)
-        }
-      })
-    })
+    window.addEventListener('load', function () {
+      setTimeout(function () {
+        window.addEventListener('popstate', e => {
+          const current = this.current
+          this.transitionTo(getLocation(this.base), route => {
+            if (expectScroll) {
+              handleScroll(router, route, current, true)
+            }
+          })
+        })
+      }, 0)
+    }, false)
   }

   go (n: number) {

reproduce,
iPhone SE(iOS 10.3.1)
vue-router history mode
safari's incognito mode

the safari's normal mode is fine,only reproduce on incognito mode

On a related note, I get the same problem when saving the web-app to home page on iOS. Removing history-mode makes it run correctly for the simulator (but not actual iphone), while having history mode on causes it to reload every time url changes.

I couldn't reproduce it on iPhone 6 with iOS 10. Please open a new issue with a reproduction if you manage to reproduce this

This issue is back. iOS 14.0.1
I managed to stop it happening all the time but no reason I can fathom. Visit on iOS device, must be actual device. https://alpha.nodenogg.in

Tap my lists
Give you self a name (lowercase no spaces)
Tap store
Create a microcosm (lowercase no spaces)
Two Join

Now without creating a node
Tap to another view such as cards
Screen will hit refresh problem.

Now a workaround seems to be to create a node on that view first.

Doesnt effect Android or desktop
Latest Vue 2

Will open new issue as advised

Was this page helpful?
0 / 5 - 0 ratings