What are the implications of migrating from createWebHashHistory() to createWebHistory()? - vue.js

I have an application that I released that uses createWebHashHistory() to manage URLs. For example, a user visits the following URL to visit something called the earthquakes channel:
https://app.radar.chat/#/channel/earthquakes
I would like to switch over to using createWebHistory() to manage URLs instead (SEO, social media previews, iOS universal links configuration, etc). With that in mind, I would like it if my new URL structure looks like this:
https://app.radar.chat/channel/earthquakes
I know that to support this change I need to make a server change. The easiest way is to have the server redirect incoming requests to an index.html file (this is documented extensively on the Vue website).
However, there are URLs in the wild that link to these old pages, URLs that have been printed and that can never be updated.
Is there a convenient mechanism to continue to support the old hash-based URLs while having the non-hashed URLs be the new default?

In your router config, you could add a global beforeEach hook on the index path that resolves to the hash path in the URL if it exists:
// router.js
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [⋯]
})
router.beforeEach((to, from, next) => {
if (to.path === '/' && to.hash.startsWith('#/')) {
next(to.hash.substr(1))
} else {
next()
}
})
export default router
demo

Related

Wildcard subdomain routing with nuxt

I'm relatively new to Nuxt and I'm trying to implement subdomain routing in my Nuxt app, so that a request to:-
mysubdomain.myapp.com is routed to myapp.com/groups/:slug
mysubdomain.myapp.com/<anything else>/ is routed to myapp.com/groups/:slug/<anything else>
where slug is the subdomain.
From what I can see, there are two possible ways to do this. Either extendRoutes in the nuxt config or by using my own router.js file. But I can't work out how get the subdomain into the route.
I've got something like this in my router.js:-
import Router from 'vue-router'
export function createRouter(ssrContext, createDefaultRouter, routerOptions) {
const options = routerOptions || createDefaultRouter(ssrContext).options
const hostname = ssrContext ? ssrContext.req.headers.host : location.host
return new Router({
...options,
routes: fixRoutes(options.routes, hostname.match(/[^.]*/)[0]),
})
}
function fixRoutes(defaultRoutes, subdomain) {
return (
defaultRoutes
.map((route) => {
// TODO use the subdomain in here to route to /groups/:slug route
return route
})
)
}
It feels like I'm close but I can't figure out this last bit. Is this even possible?!

how to redirect to child route in nuxt?

i want to redirect to child route, if user entered to localhost:3000/browse,
he redirect to localhost:3000/browse/songs/new.
but i don't know how to do it!
and i don't know what is the best component structure for this example
my routes will be like this:
localhost:3000/browse/songs/new
localhost:3000/browse/songs/popular
localhost:3000/browse/songs/top
localhost:3000/browse/songs/podcasts
localhost:3000/browse/songs/playlist
and in all of theme, i have a common section, but contents are difrent.
common section
If you only have one location to redirect to, check out Andrew1325's answer. If there are multiple route-to-redirect match-ups, this thread has some ideas.
The way I do it is with "anonymous" middleware, like so:
In /pages/browse/index.js
<script>
export default {
middleware: [
function({ redirect }) {
redirect('/browse/songs/new');
},
],
};
</script>
To redirect automatically or under certain conditions you need to use middleware. This can be setup like this:
//browse/index.js
<template>
//no need for content as redirecting
</template>
<script>
export default {
middleware: 'redirect'
}
</script>
and your middleware file...
//middleware/redirect.js
export default function ({ store, redirect }) {
// automatic redirect
return redirect('/browse/songs/new')
}
Read about middleware here
Your pages structure is set up in the pages folder and you name folders and pages as you would like your routes to be.
Read about it here
To use a common theme you can use layouts. These will have a <nuxt-child/> section which will display the individual page content.
You can read about them here
All of this is pretty basic nuxt stuff, you should take some time to read the documantation or look at some tutorials listed here.
Big ups to #parker_codes, there is a little deficiency is his implementation which is circular redirection a.k.a forever redirect loop.
To solve this, add some checks in the parent component. This is because Nuxt Js calls the middleware of a parent route anytime a child route is visited.
//browse/index.js
<template>
//no need for content as redirecting
<NuxtChild />
</template>
<script>
export default {
middleware({ route, redirect, from }) {
if(route.path == "/browse" || route.path == "/browse/"){
return redirect("/browse/songs/new")
}
}
}
</script>

Cannot figure out how vue-router works

I have a Vue.js project with the following router:
import Vue from 'vue';
import Router from 'vue-router';
import Overview from '#/components/Overview';
import Experiment from '#/components/ForExperiment';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
redirect: 'test',
},
{
path: '/overview',
component: Overview,
},
{
path: '/overview/from/:from/to/:to',
name: 'overview',
component: Overview,
},
//... some other urls goes here.
{
path: '/test',
name: 'test',
component: Experiment,
},
],
});
If I open http://localhost:8080 in a browser I am redirected to http://localhost:8080/#/test. Why not just http://localhost:8080/test? Where does the '#' symbol come from?
And why if I open http://localhost:8080/test am I redirected to http://localhost:8080/test#/test?
And what is even more strange, if I open http://localhost:8080/overview I am redirected to http://localhost:8080/overview#/test, so the Overview component is not displayed.
What can cause these strange effects?
Vue router has different modes. The default mode when a browser is detected is hash. The current route is determined by the hash part of the url. The upside of this approach is that no serverside configuration is required. All urls point at the same resource (e.g. the route), which you can make your index.html file.
You can change this mode to history. The history mode uses the history api of the browser. It will only work in recent browsers, but support should not be an issue at this point. It will also require server side configuration in that you need to internally rewrite your router urls to the same file. If you would not do that, refreshing the page will show a 404 page instead of the page you want to see.
vue-router default mode is hash mode, that is why you see a # on your URL. It uses the URL hash to simulate a full URL without reloading the page if it changes.
To get rid of the hash, we can use vue-router history mode. Change the mode like so:
const router = new VueRouter({
mode: 'history',
routes: [...]
})
This leverages the History API.
If you want to use the history mode, you'll need to change your server configuration. Vue Router docs has some examples here.
The vue router defaults to hash mode. For your url to go to http://localhost:8080/test you need to go into history mode. This is done because by default web servers are not setup to redirect all requests to one html file. hash mode is used to per the docs:
The default mode for vue-router is hash mode - it uses the URL hash to simulate a full URL so that the page won't be reloaded when the URL changes.
Change your router to this to get history mode. But you will need to configure NGINX or Apache2 to redirect all requests to your vue code
const router = new VueRouter({
mode: 'history', // Add this to your router
routes: [...]
})

Vue.js with VueRouter - how to correctly route on user's refresh

I use Vue and VueRouter (and also Vuex but it is not the case here) in my project. Imagine i have 5 files:
main.js - stores all components definitions, imports them from
external files and so on.
App.vue - it is main component that stores
all other
routes.js - stores all the routing definitions
login.vue -
stores login component (login page)
content.vue - stores page
component
(quite simplified version but you surely get the idea).
Now if i open my path '/' it should reroute me to '/login' page if i am not logged in and to '/content' when i am logged in. Nothing special here.
Now my page works as intended (almost). If I enter in my browser '/content' it tries to render '/content' component with default data (ie userId = -1), then immediately it reroutes me to '/login' page. The '/content' shows just for a second. And it is my problem. I would like to reroute to '/login' without earlier rendering '/content' with default data.
It is obvious that it tries to render '/content' - maybe from cache or something, but since rerouting is my first command in created() it should not
mount /content component in app component, but /login.
Any idea how to prevent it?
I am aware that i do not attach any code, but i hope it wont be necessery for you to understand the problem and advice any solution because it would need cutting and simpliding a lot of code.
In your case, I think you should use vue router's beforeEach hook.
You can use meta field in router to indicates whether the path need authentication, and do processing in beforeEach function.
I will give the sample code.
import Router from 'vue-router';
const router = new Router({
routes: [{
path: '/content',
meta: {
auth: true,
}
}, {
path: '/login',
}]
});
router.beforeEach(async (to, from, next) => {
if (to.matched.some(m => m.meta.auth)) {
// user your authentication function
const isAuth = await getAuthentication;
if (!isAuth) {
next('/login');
}
next();
}
})
if your authentication function is not async function, you should remove async/await keywords
Except if the API in the meantime declares that you are no longer authenticated, the router will not be able to refresh itself by the beforeEach method.
Even with a loop method that retrieves data from the API, which will store them in the store as reactive data.
In Vue everything can be reactive, except Vue router

Vue Routes does not work properly in production

I am new to vuejs. For my Vuejs application, I cannot access url like '/foo/apple' in the web hosting server, after I run the "npm run build". It shows error 404 I am using the HTML5 History Mode,(https://router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations) and I implemented the "connect-history-api-fallback" like below in dev-server.js:
const express = require('express')
const app = express()
app.use(require('connect-history-api-fallback')())
My router/index.js
export default new Router({
mode: 'history',
{
path: '/Product',
name: 'Product',
component: Product,
},
{
path: '/Product/kraftbag',
name: 'Kraftbag',
component: Kraftbag
},
});
my website http://americandunnage.n55lwi.info/.
I have looked for a lot of posts regarding to this problem, but I still can find the solution.
you are likely missing the hashbang (#)
try #/foo/apple instead of /foo/apple
The default setting of the vue router is to use the hashbang method. This relies on using the index.html (or whatever defaults to / url) page, because everything after it is not taken as part of the url location but passed into the application. For example, in an anchor 1st heading this will go to a part of the page, and if you're already on that page, you will not be redirected.