Search code examples
javascriptamazon-s3vuejs2amazon-cloudfrontvue-router

Vue SPA to force page reload when using vue-router navigation


Scenario:

I have a Vue2 SPA ready to replace a legacy LAMP website but PO would like to deploy it progressively (starting with just one single URL) to test user's reactions, analytics, ad revenue, etc.

We've built this architecture to allow this approach:

Architecture

It works. So, if you go to http://example.com/release-first (let's say this is the page we want to release publicly in the first place) CF returns the content from the S3, and any other URL is fetched from the legacy server.

But, of course, when you access /release-first you're in the SPA, and since most of the navigation is done with <router-link :to="">, you're trapped in the SPA and you can access the rest of the new version, which we don't want at this point.

Possible solutions I thought:

One solution could be to replace all <router-link> tags with standard <a> links that would force the browser to do a new petition to the CloudFront every time. That might work but I'd prefer not to do it because I have to change a lot of links and also because links are not the only way the user can move around (there's some form submission too, etc.)

So, I tried something like:

router.beforeEach((to, from, next) => {
    if (!/\/release-first\/?$/.test(to.path)) {
        window.location.href = `http://example.com${to.path}`  
        return;
    } else {
        next();
    }
});

This works fine in staging env (because it has a different subdomain, so it's redirecting to production) but if I try in production it doesn't work. I don't know if Vue hijacks the Location API or what, but it doesn't force a new petition to the CloudFront.

I've also tried with:

const absolutePath = `http://example.com${to.path}`;
window.history.pushState({ urlPath: absolutePath }, "", absolutePath);
window.location.reload();

But it doesn't work either... and I couldn't find in vue-router's Docs an option to force the page to reload on navigation. Any idea on how to achieve this?


Extra information requested in comments:

  • Router mode is history.
  • Path formats are like:
    • /
    • /single-level-pages
    • /two/level-pages
    • /two-levels-with/:param
    • /multiple/level/pages
    • /multiple/level/with/:param/
  • Not all paths share the release-first URL. The URL I wanna release in first place is like first-level-with/an-specific-param.
  • I've double-checked the RegExp (and I've used a console.log there too). The redirections in CloudFront are working well too. If you enter the different URLs in the browser you get content from Legacy server or S3 as expected. The code I've used in beforeEach navigation guard works fine in staging too, so the only issue is that this in not enough when the domain is the same because the location.href overwrite doesn't seem to force a new request when the destination is in the same domain.

Solution

  • This is how I finally solved it:

    router.beforeEach((to, from, next) => {
        if (
            window.location.host === "prod-env.example.com" &&
            /\/release-first/.test(from.path) &&
            !/\/release-first/.test(to.path)
        ) {
            let a = document.createElement("a");
            a.id = "forceReloadLink";
            a.href = to.path;
            document.querySelector("body").appendChild(a);
            document.getElementById("forceReloadLink").click();
        } else {
            next();
        }
    });