how to write global router-function in nuxt.js - vue.js

I am using Vue.js with Nuxt.js, but I got a problem in router's functions.
In the pure Vue, i can write in main.js like this:
val route = new Router({
routes:{
[...]
}
})
route.beforeEach(to,from,next){
//do something to validate
}
And how to do the same in nuxt.js ? I can not find any file like main.js.
Also, all i know is to deal with the pages folder to achieve router, I can not set the redirect path
please help, thx :)

You can create a plugin for Nuxt
create a plugins/route.js file:
export default ({ app }) => {
// Every time the route changes (fired on initialization too)
app.router.afterEach((to, from) => {
//do something to validate
})
}
and update your nuxt.config.js file:
plugins: ['~/plugins/route']
More details about Nuxt plugins: https://nuxtjs.org/guide/plugins

If anybody might be still interested, it's possible to setup global middleware in nuxt.config.js like this:
router: { middleware: ['foo'] },
then in your middleware/foo.js you do whatever...
export default function({ route, from, store, redirect }) {}
Beware: You can't use this for static sites (nuxt generate), because middleware is not executed on page load, but only on subsequent route changes. Thanks #ProblemsOfSumit for pointing that out.

Related

Lazy Route gets prefetched, ignoring webpack magic comments (Vue)

I need to develop an application with server side authentication with a login view. If I want to use Vue Router to dynamically switch between login and index (the protected view), I need to avoid login view downloading (prefetching) index before succesful authentication, because if not, server will answer with the login page to the index prefetching request.
I'm trying to achieve this in the original Vue Router example that has two routes. Home and about. The first one is included and the second one is lazy loaded (but prefetched) which would be the protected page in the real application.
In order to avoid prefetching I have tried all the webpack magic comments I have found, but the prefetching is still hapenning.
Here is the code:
import Vue from "vue"
import VueRouter from "vue-router"
import Home from "../views/Home.vue"
Vue.use(VueRouter);
const routes = [
{
path: "/",
name: "Home",
component: Home
},
{
path: "/about",
name: "About",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about"*/ /* webpackMode: "lazy" */ /* webpackPrefetch: false */ /* webpackPreload: false */ "../views/About.vue")
}
];
const router = new VueRouter({
routes
});
export default router;
And here the result:
I don't want to disable the feature from the general webpack configuration because I want it for the rest of the application links. I want to disable it only for this link.
How I should configure the router to achieve it?
Thanks for your time,
H25E
There is a discussion on Github which offers some tips relevant to your situation.
// vue.config.js
module.exports = {
chainWebpack: config => {
config.plugin('prefetch').tap(options => {
options.fileBlackList.push([/MyChunkName(.*)\.js$/]);
return options;
});
}
};
Vue-CLI by default automatically prefetches all dynamic imports - so you have to add a blacklist.
The magic comments for Webpack (webpackPrefetch and probably webpackPreload too) accept either true or a number (index) - but do not accept false argument.

How to pre-render multiple Vue app pages?

I'm trying (unsuccessfully) to pre-render the HTML of multiple Vue apps within the same project scaffolded with Vue CLI. I do not want to use Vue Router or Nuxt etc for multiple reasons.
I've tried using prerender-spa-plugin, but since I don't use routes, it only pre-renders the index.
My vue.config.js looks like this:
const path = require('path');
const PrerenderSPAPlugin = require('prerender-spa-plugin');
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer;
module.exports = {
pages: {
index: 'src/index.js',
about: 'src/about.js',
},
configureWebpack: {
plugins: [
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, 'dist'),
routes: ['/'],
postProcess(route) {
route.html = route.html.replace('</script><div>', '</script><div id="app" data-server-rendered="true">');
return route;
},
renderer: new Renderer({
headless: true,
renderAfterDocumentEvent: 'render-event',
}),
}),
],
},
};
and my index.js and about.js essentially look like this:
import Vue from 'vue';
import App from './Index.vue';
new Vue({
render: h => h(App),
mounted() {
document.dispatchEvent(new Event('render-event'));
},
}).$mount('#app');
I also have unique public/ index.html and about.html pages.
The routes parameter of prerender-spa-plugin doesn't seem to recognise things like '/about.html'. Is there an easy way of achieving multiple pre-rendered pages?
Do I have to wrestle with the SSR module?
Thanks in advance.
The solution I've found is to call new PrerenderSPAPlugin multiple times, one for each route.
I'm also facing the same issue, i have static html uses vue component and i want to pre-render the vue component in output build directory. I'm using laravel-mix package for build process.
Could you post the full solution for this i.e calling new PrerenderSPAPlugin multiple times, one for each route.
If i can get the full webpack.config.js, it would easy for me to understand and implement the same using laravel-mix.

Nuxt - How to find the previous route?

I am able to use this.$router.go(-1) to redirect a user to the previous route. However, I am not able to understand how I get get the information about the previous route before redirecting.
Basically, by first reading what the previous route was, I want to make sure that the previous route was from the same domain and only then redirect to it, otherwise do something else.
in nuxt static methods where you have access to nuxt context ex: asyncData or middlewares :
asyncData({from}){
// do something with from
}
but if you want to get the prev route in vue instance you can use
this.$nuxt.context.from
also, remember that from can be undefined if there wasn't any prev page in the browser history
In your page you can add asyncData hook which have access to context object with from property:
asyncData({ from }) {
console.log(from);
}
You can achieve this by implementing the Vue Navigation guards, on the component/page.
<script>
export default {
beforeRouteEnter(to, from, next) {
console.log(from)
next()
},
data() {
return {
...
prevRoute: null,
}
}
}
</script>
Or there is this guy https://gist.github.com/zolotyx/b4e7fda234b3572fb7adaf11391f8eef#file-auth-js, he has made a script to help in redirecting
There is no out of the box way to get this information.
What you can do is attach a beforeRouteEnter as a global guard and save the route before navigating to a new route.
Then you can check to see if there is a previous route saved and execute this.$router.go(-1)
If you are using the router in history mode you could be tempted to use the History api that vue-router is using to get this information. But HistoryApi doesn't allow for this as this would be a huge privacy problem. You could see the entire history of the user in the current tab.
In the [middleware] directory you can put this script [routing.js]:
/* eslint-disable no-undef */
/* eslint-disable no-console */
export default function (context) {
// current route
console.log('route=', context.route.name)
// previous route
if (process.client) {
const from = context.from
console.log('from=', from)
}
}
In [nuxt.config.js]:
router: {
...
middleware: 'routing'
},
Whenever you change the current page in the client you should see a log showing the previous page. Maybe it can help you.

Populate router with external json

I would like to add routes from an external json file, which can change at runtime, to my Nuxt application. A similar topic can be found here.
I've overridden the default Nuxt router with my own implementation. If I import the routes async using axios + router.addRoutes(), I seem to loose the server side rendering. It seems like createRouter will have async support, but it's not in an official release of Nuxt yet.
How do I import a js/json file synchronously to my router.js below, so that I can populate the routes? I want to be able to configure the routes at runtime, so I don't want it to be a part of the bundle.
modules/router.js:
const path = require('path')
module.exports = function () {
this.nuxt.options.build.createRoutes = () => {}
this.addTemplate({
fileName: 'router.js',
src: path.resolve(`${this.options.srcDir}`, 'router.js')
})
}
nuxt.config.js:
modules: ['~/modules/router']
router.js:
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export function createRouter () {
const router = new Router({
mode: 'history',
routes: [/* ... */]
})
return router
}
You could try with sync-request.
It is a NPM package aimed to perform synchronous web requests. It is available here.
Please note that, as stated in the documentation of the package itself, it is not suitable for production environment, probably because of application hanging in case of missing data.
So await would be an answer but I guess you already tried that? So, something like this.
const routeFile = await fetch('pathToTheJsonFile');
const routes = await routeFile.json();
In case you can't make the method async, as a workaround maybe use jQuery. I don't like this but if there's no other option, for now, use async: false in jQuery get.
jQuery.ajax({
url: 'pathToYourJsonRoutes',
success: function (result) {
},
async: false
});

Nuxt - define a const once and which can use across all pages

I'm trying to implement Shopify JS SDK in Nuxt
So this is what I did, a plugin
// plugins/shopify.js
import Vue from 'vue'
import 'isomorphic-fetch'
import Shopify from 'shopify-buy'
export default ({ app }, inject) => {
app.shopify = Shopify.buildClient({
domain: 'aaa.myshopify.com',
storefrontAccessToken: 'aaa'
});
}
nuxt config
//nuxt.config.js
plugins : [{ src : '~/plugins/shopify', ssr: false}]
vendor : ['shopify-buy']
index
asyncData ({ app }) {
return app.shopify.product.fetchAll().then((products) => {
// Do something with the products
console.log(products);
return { products : products }
});
}
The result is
TypeError Cannot read property 'product' of undefined
But it works if I removed the asyncData, refresh my page, and add the code back without refreshing.
I believe this has something to do with the lifecycle.
Can anyone please tell me if I'm doing it the right way, or there's other proper way to define such const which can be use across pages, components etc
And if this is the right way, what I did wrong?
Thanks in advance.
My reference are Nuxt guides as well as examples.
I tried google around but can't locate what I need, or maybe I just didn't get the right keywords.
FROM DOCUMENTATION
Nuxt.js lets you create environment variables that will be shared for
the client and server-side.
To do this, you can use the env property:
nuxt.config.js:
module.exports = {
env: {
baseUrl: process.env.BASE_URL || 'http://localhost:3000'
}
}
Then to access it from anywhere, just use it like so:
process.env.baseEnv
For example, in an axios plugin:
import axios from 'axios'
export default axios.create({
baseURL: process.env.baseUrl
})