How to pre-render multiple Vue app pages? - vue.js

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.

Related

VueJS cshtml component

I want to use VueJS with .NET Core Razor pages.
boot.ts
import './css/site.css';
import 'bootstrap';
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const routes = [
{ path: '/', component: require('./components/home/home.vue.html') },
{ path: '/counter', component: require('./components/counter/counter.vue.html') },
{ path: '/fetchdata', component: require('./components/fetchdata/fetchdata.vue.cshtml') }
];
new Vue({
el: '#app-root',
router: new VueRouter({ mode: 'history', routes: routes }),
render: h => h(require('./components/app/app.vue.html'))
});
Notice the fetchdata.vue.cshtml in the code above. When I run the application, I get the following error:
ERROR in ./ClientApp/components/fetchdata/fetchdata.vue.cshtml
Module parse failed: .\NetCoreVueJs\ClientApp\components\fetchdata\fetchdata.vue.cshtml Unexpected token (1:0)
You may need an appropriate loader to handle this file type.
| <template>
| <div>
| <h1>Weather forecast</h1>
# ./ClientApp/boot.ts 9:37-91
# multi event-source-polyfill webpack-hot-middleware/client?
path=__webpack_hmr&dynamicPublicPath=true ./ClientApp/boot.ts
Is it possible to use .cshtml files as components in VueJS?
Yes, you can use Vue in your razor pages, but I think you're approaching it in the wrong way. Remember that the .cshtml files are compiled in the backend side into html markup that is sent to the browser.
If you want to use razor views you will have to mount your VueJS code on the HTML that is generated and sent to the browser. See [his example I created on Codepen to illustrate this, I'm using Pug to mimic something that generates the HTML template and once it is generated I mount Vue JS on it. https://codepen.io/jaireina/pen/MNvvgd
Pug
#app
input(type="text", placeholder="type your name", v-model="name")
each val in [1,2,3]
p.name {{name}}
JS
var app = new Vue({
el: '#app',
data: {
name: ''
}
})
You won't be able to use Vue in the way you're trying, as a single page application and passing it the cshtml files as templates. If you really need to use razor views you can do something to what I mentioned before. But if you want to create a SPA, I would recommend creating an API with .NET and having your Vue app consuming that API.
I hope this helps.

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
});

I'm using prerender-spa-plugin and it works, but Google doesn't see the prerendered pages

I have a Vue.js application and I want to use prerendering for SEO purposes.
I followed the github, this tutorial, this one, and this video to understand everything and I ended up with this implementation in my app:
in webpack.prod.conf.js:
...
const path = require('path')
const webpack = require('webpack')
...
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const PuppeteerRenderer = PrerenderSPAPlugin.PuppeteerRenderer
...
plugins: [
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, '../dist'),
routes: ['/', '/about-gy', '/gy-quest'],
headless: false,
renderer: new PuppeteerRenderer()
})
]
in main.js (if it matters somehow):
new Vue({
router,
store: store,
components: { App },
render: h => h(App)
}).$mount('#app');
The results I get are as expected I think; in the dist folder it looks like this:
dist
--about-gy
+--index.html
--gy-quest
+--index.html
--static
--index.html
Where the html files have the CSS and HTML rendered.
The problem is that when I test the same way the guy in the video tests (with view page source on the rendered pages) I get only the "app" div inside and not the already rendered one.
I thought maybe it's because it's on a local server or something, tried to Fetch as Google on the Search Console tool they offer, and still, they fetch the not-rendered version of the pages.
I'm stuck because it's not an issue I can write on the github of the plugin because the plugin does its job, but I'm not sure what I'm doing incorrectly.
In your webpack.prod.conf.js add an option to renderer: new PuppeteerRenderer() that tells it to run once the app has been mounted:
renderer: new PuppeteerRenderer({
renderAfterElementExists: "#app"
})

How to load routes on initialization (Framework7+Vue)

I am trying to build an app with framework7 and vue. All is well, except that when I start the app, it does not load the component specified on routes.js. I want it to load the component specified by the root url. How can I do this?
main.js
new Vue({
el: '#app',
// Init Framework7 by passing parameters here
framework7: {
root: '#app',
/* Uncomment to enable Material theme: */
material: true,
swipePanel: 'left',
routes: Routes,
pushState: true,
animateNavBackIcon: true,
input: {
}
},
})
routes.js
export default [
{
path: '/',
component: require('./assets/vue/pages/home.vue')
}
]
I decided to use the preroute object on framework7 Initialization, so that even before '/' route is loaded, I can manipulate the request and redirect to a particular route. Read more on preroute here: https://framework7.io/docs/init-app.html
I'm not sure it is the same case as yours:
At first time, I did not add f7-view in my application.html, and the route did not work.
After adding <f7-view main>, the route started work and show the page.
I think f7-view is almost like router-view, is a output interface of the routing result.
If you add f7-view already, then just ignore this answer :-)

how to write global router-function in nuxt.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.