vue js two SPAs - vue.js

I am building a web app with two layouts (for login page, and dashboard). Each of them are represented as SPA application, so each has router-view. The main problem is 'How to connect them and redirect from one to another?'.
I have a App.vue - check if user is authorized. if yes - redirect to Dashboard.vue, else - redirect to Login.vue. Each of them has there own router-view.

An SPA should be a single html file which serves up your app and all the routes, so the basic structure should be:
HTML
<div id="app">
</div>
<!-- bundled file -->
<script src="app.js"></script>
app.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
import App from './components/App.vue' // import Base component
// Import views to register with vue-router
import Login from './components/views/Login.vue'
import Dashboard from './components/views/Dashboard.vue'
const guard = function(to, from, next) {
// Check if user is logged in (you will need to write that logic)
if (userIsLoggedIn) {
next();
} else {
router.push('/login');
}
};
const routes = [{
path: '/login',
component: Login
},{
path: '/dashboard',
component: Dashboard,
beforeEnter: (to, from, next) => {
guard(to, from, next); // Guard this route
}
}]
const router = new VueRouter({
mode: 'history', // history mode
routes
})
new Vue({
el: '#app',
router,
render: h => h(App) // mount base component
})
App.vue
<template>
<div>
<!-- Your layout -->
<!-- All views get served up here -->
<router-view></router-view>
</div>
</template>
I haven't tested that, but in this scenario every view component gets served up by App.vue which is mounted on the main vue instance. You then use the beforeEach guard to check that the user is logged in, if they are then you call next() which takes them to the route, if they are not then you redirect them to login.

vue-router has the ability to create custom guards for any route. You do not need 2 separate applications, just some safety with the routes in your router.
https://router.vuejs.org/en/advanced/navigation-guards.html
Your guard could be a function that checks for authentication.
Here's a full implementation tutorial from Auth0: https://auth0.com/blog/vuejs2-authentication-tutorial/

Related

How do I router-link to the same page with an updated query & reload the page? [duplicate]

As part of my Quasar app, I have the following route:
import { RouteRecordRaw} from 'vue-router'
import { uid } from 'quasar'
const routes: RouteRecordRaw[] = [
{
path: '/',
redirect: () => {
console.log('matched /')
return {path: `/${uid()}`}
}
},
{
path: '/:uuid',
component: () => import('pages/User.vue')
// component: User,
},
];
export default routes;
This works fine when going to /: the URL is changed to /73a219e5-2cf2-4dd0-8... and User.vue is executed (specifically there a fetch inside that retrieves some data based on the :uuid parameter.
If I force a route from within a component (User.vue for instance), via
import { useRouter } from 'vue-router'
const router = useRouter()
router.push('/')
I do see that the URL changes to a new UUID but User.vue is not executed. Specifically, a reference to route.params.uuid where const route = useRoute() is not reactive.
Is this normal (= I have to look for anther way to trigger), or is there a misuse (erroneous use) on my side?
The core of the issue is that you're (re)using the same component for rendering the page you're navigating from and the page you're navigating to.
By design, Vue optimises DOM rendering and will reuse the existing component instance. This means certain hooks won't be triggered (e.g: mounted, created, etc...) when changing route.
To force Vue into creating a different component instance when the route changes, use the current route's .fullPath as key on <router-view>:
<template>
...
<router-view :key="route.fullPath"></router-view>
...
</template>
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute();
</script>

How can I redirect using vue-router?

I have tried to access to others pages via url, but those pages never loads
http://localhost:8080/index
http://localhost:8080/about
This is my main.js file
import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify';
import VueRouter from 'vue-router'
Vue.config.productionTip = false
Vue.use(VueRouter)
import Index from './views/Index';
const About = { template: '<p>about page</p>' }
const routes = [
{ path: '/index', name: 'index', component: Index },
{ path: '/about', name: 'about', component: About }
]
var router = new VueRouter({
routes: routes,
mode: 'history'
})
new Vue({
router: router,
vuetify,
render: h => h(App)
}).$mount('#app')
Does anyone can help me with vue-router? i am new in this framework
Does it work if you access them like this:
http://localhost:8080/#/about
If yes, your vue-router is working in the default hash-mode. To get rid of the hash and get "normal" URLs you'll need to set it to history mode.
Edit:
As I see you're already using history mode. Do you use Vue CLI for local development? This should normally work out of the box. If not, you need to setup some redirect rules on other web servers. Please see the examples here: Example Server Configurations
Edit 2:
Can you show your App component?
I tried to reproduce your problem in a sandbox, but it works: https://codesandbox.io/s/confident-voice-zyg07
The App component here looks like this, including the router-view:
<template>
<div id="app">
<router-view id="page"/>
</div>
</template>
<script>
export default {
name: "App",
components: {}
};
</script>

Should VueJS re-run main.js on every route change

Vue.js will re-run main.js where the main Vue instance is defined every time the route changes. Is that a normal behavior?
According to vue-router documentation, only router-specific components get their content changed along with routes. However, I added a simple console log call inside the main Vue instance created hook and it gets called after every route change.
// main.js
import App from './App.vue'
new Vue({
created() {
console.log('main created hook')
this.$store.dispatch(listenToAuthChanges)
},
render: h => h(App),
router,
store
}).$mount('#app')
// /router/index.js
import Router from 'vue-router'
import routerConfig from '#/config/routes'
import { requireAuth } from './navigationGuards'
const routes = Object.values(routerConfig)
.map(route => ({
...route,
component: () =>
import(
/* webpackChunkName: "route.component" */
`#/views/${route.component}.vue`
)
}))
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes
})
router.beforeEach(requireAuth)
<!-- App.vue -->
<template>
<router-view />
</template>
I would expect only the content inside 'router-view' to update after every route without triggering a full app reload which causes all initial data fetching and breaks authentication navigation guards.

Vue-router component reusing

I would like to know how can I stop component reusing in Vue-router.
I'm building a simple page application and I am unable to update data after clicking the same link twice.
Is it possible to somehow force reloading or what are the best practices in my situation?
Use the key attribute on router-view set to current url. It's built in, so no need to write any code.
<router-view :key="$route.fullPath"></router-view>
Vue Router reuses the same component therefore the mounted hook won't be called. As stated in the documentation:
The same component instance will be reused [...] the lifecycle hooks of the component will not be called.
If you want to update the data you have two options:
Watch the $route object
const User = {
template: '...',
watch: {
'$route' (to, from) {
// react to route changes...
}
}
}
Use the beforeRouteUpdate navigation guard
const User = {
template: '...',
beforeRouteUpdate (to, from, next) {
// react to route changes...
// don't forget to call next()
}
}
For a more detailed explanation you can check the section Reacting to Param Changes of the Vue Router documentation: https://router.vuejs.org/guide/essentials/dynamic-matching.html#reacting-to-params-changes.
One way to do this is to put a key on the router-view and append a timestamp querystring to your router-link
const Home = {
template: '<div>Home</div>',
created() {
console.log('This should log everytime you click home.');
},
};
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/', component: Home },
]
});
new Vue({
router,
el: '#app',
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<router-link :to="`/?q=${Date.now()}`">/home</router-link>
<router-view :key="$route.fullPath"></router-view>
</div>
One reason not to do it this way is because it'll force rerenders on components that you may want to be reused such as on routes like
/posts/1
/posts/2

vue-router not routing properly, no components are shown

I'm trying to use vue-router to show different components for different route. However it doesn't seem to be working.
I have a codepen of the compiled program here.
My main.js is just defining the router and starting the vue application. It's importing the components and setting the routes.
import scss from './stylesheets/app.styl';
import Vue from 'vue';
import VueRouter from 'vue-router';
import Resource from 'vue-resource';
import App from './components/app.vue';
import Home from './components/home.vue';
import Key from './components/key.vue';
import Show from './components/show.vue';
// Install plugins
Vue.use(VueRouter);
Vue.use(Resource);
// Set up a new router
var router = new VueRouter({
mode: 'history',
routes:[
{ path: '/home', name: 'Home', component: Home },
{ path: '/key', name: 'Key', component: Key },
{ path: '/show', name: 'Show', component: Show },
// catch all redirect
{ path: '*', redirect: '/home' }
]
});
// For every new route scroll to the top of the page
router.beforeEach(function () {
window.scrollTo(0, 0);
});
var app = new Vue({
router,
render: (h) => h(App)
}).$mount('#app');
My app.vue is really very simple, just a wrapper div and the router-view
<script>
export default {
name: "app"
}
</script>
<template lang="pug">
.app-container
router-view
</template>
The three other components that router should be showing are equally as simple, each looks basically the same. Just the name and the h1 content are different.
<script>
export default {
name: "home"
}
</script>
<template lang="pug">
h1 Home
</template>
Webpack build everything into an app.js without any errors. I have a super simple index.html file that I open in Chrome.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sagely Sign</title>
</head>
<body>
<div id="app"></div>
<script src="js/app.js"></script>
</body>
</html>
Looking at the console I see no errors. What I do notice is the URL stays the same, it looks like the router is not redirecting to /home.
file:///Users/username/Development/test-app/build/index.html
I would have expected it to change to the new route.
file:///Users/username/Development/test-app/build/index.html#/!/home
But even if I goto that route directly the home.vue component is not displayed.
The function you are using in the beforeEach method is a navigation guard. Navigation guards receive 3 parameters: to, from and next. From the Navigation Guards documentation:
Make sure to always call the next function, otherwise the hook will never be resolved.
Here, you just scroll at the top of the page but the hook is never resolved, therefore the router stops here, right after the scroll.
Write your function like this:
router.beforeEach(function (to, from, next) {
window.scrollTo(0, 0);
next();
});
And it should work.