Vue+Meteor SSR
Add Server-Side Rendering in your Vue+Meteor application
Installation:
meteor add welkinwong:vue-ssr
Usage
:warning: All your client-side code should be available on the server (which means they shouldn't be in a client
folder), and the code and libraries should be able to run on the server.
Wrap your Vue root instance in a createApp
function and export it, alongside with the router instance:
1import Vue from 'vue' 2 3// Meteor Tracker integration 4import VueMeteorTracker from 'vue-meteor-tracker' 5Vue.use(VueMeteorTracker) 6 7import App from './ui/App.vue' 8import router from './router' 9 10function createApp () { 11 return { 12 app: new Vue({ 13 el: '#app', 14 router, 15 ...App, 16 }), 17 router, 18 } 19} 20 21export default createApp
In your client code, start the app as usual:
1import { Meteor } from 'meteor/meteor' 2import CreateApp from './app' 3 4Meteor.startup(() => { 5 CreateApp() 6})
In your server code, you need to set the VueSSR.createApp
method with a function that returns the Vue instance:
1import { VueSSR } from 'meteor/akryum:vue-ssr' 2import CreateApp from './app' 3 4VueSSR.createApp = function (context) { 5 const { app, router } = CreateApp() 6 // Set the url in the router 7 router.push(context.url) 8 9 // Called when Vue app has finished rendering 10 context.rendered = () => { 11 // Inject some arbitrary JS 12 context.js = `console.log('hello')` 13 } 14 15 return app 16}
Returning a promise works too:
1VueSSR.createApp = function (context) { 2 return new Promise((resolve, reject) => { 3 const { app, router } = CreateApp() 4 // Set the URL in the router 5 router.push(context.url) 6 7 router.onReady(() => { 8 const matchedComponents = router.getMatchedComponents() 9 10 // no matched routes 11 if (!matchedComponents.length) { 12 reject({ code: 404 }) 13 } 14 15 // Can use components prefetch here... 16 17 // Called when Vue app has finished rendering 18 context.rendered = () => { 19 // Inject some arbitrary JS 20 context.js = `console.log('hello')` 21 } 22 23 resolve(app) 24 }) 25 }) 26}
Add the <div id="app"></div>
element in you HTML where you want to render the Vue app. If you don't, the app will be rendered at the beginning of the page body.
You can change the id of the element by setting the VUE_OUTLET
environment variable, or by setting the VueSSR.outlet
property:
1VueSSR.outlet = 'my-app'
In this example, Vue SSR expects a <div id="my-app">
element in the HTML page.
:warning: The CSS can flicker in developpement mode and load after the app is rendered. This is due to the HMR system having to append dynamic style tags in the page to get the fastest reloading possible. This is not the case in production mode (try running your app with meteor --production
).
Head and Body injection
You can modify the head and body of the SSR render with the appendHtml
function. This example uses vue-meta:
1VueSSR.createApp = function (context) { 2 return new Promise((resolve, reject) => { 3 const { app, router, store } = createApp() 4 5 router.push(context.url) 6 context.meta = app.$meta() 7 8 // ... 9 10 context.appendHtml = () => { 11 const { 12 title, link, style, script, noscript, meta 13 } = context.meta.inject() 14 15 return { 16 head: ` 17 ${meta.text()} 18 ${title.text()} 19 ${link.text()} 20 ${style.text()} 21 ${script.text()} 22 ${noscript.text()} 23 `, 24 body: script.text({ body: true }) 25 } 26 } 27 28 resolve(app) 29 }) 30}
LICENCE ISC - Created by Guillaume CHAU (@Akryum)