Skip to content
Branch: master
Find file History
Guillaume Chau
Guillaume Chau chore: eslint fix
Latest commit 526f6df May 11, 2019
Permalink
Type Name Latest commit message Commit time
..
Failed to load latest commit information.
server
.gitignore Created SSR package Mar 7, 2017
.versions
CHANGELOG.md vue-ssr: JS injection Apr 30, 2017
README.md feat(ssr): support Vue 2.6 new SSR system May 10, 2019
npm.json Initial public release Mar 23, 2017
package.js feat(ssr): support Vue 2.6 new SSR system May 10, 2019

README.md

Vue+Meteor SSR

Add Server-Side Rendering in your Vue+Meteor application

Installation:

meteor add akryum:vue-ssr

Usage

⚠️ 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:

import Vue from 'vue'

// Meteor Tracker integration
import VueMeteorTracker from 'vue-meteor-tracker'
Vue.use(VueMeteorTracker)

import App from './ui/App.vue'
import router from './router'

function createApp () {
  return {
    app: new Vue({
      el: '#app',
      router,
      ...App,
    }),
    router,
  }
}

export default createApp

In your client code, start the app as usual:

import { Meteor } from 'meteor/meteor'
import CreateApp from './app'

Meteor.startup(() => {
  CreateApp()
})

In your server code, you need to set the VueSSR.createApp method with a function that returns the Vue instance:

import { VueSSR } from 'meteor/akryum:vue-ssr'
import CreateApp from './app'

VueSSR.createApp = function (context) {
  const { app, router } = CreateApp()
  // Set the url in the router
  router.push(context.url)

  // Called when Vue app has finished rendering
  context.rendered = () => {
    // Inject some arbitrary JS
    context.js = `console.log('hello')`
  }

  return app
}

Returning a promise works too:

VueSSR.createApp = function (context) {
  return new Promise((resolve, reject) => {
    const { app, router } = CreateApp()
    // Set the URL in the router
    router.push(context.url)

    router.onReady(() => {
      const matchedComponents = router.getMatchedComponents()

      // no matched routes
      if (!matchedComponents.length) {
        reject({ code: 404 })
      }

      // Can use components prefetch here...
      
      // Called when Vue app has finished rendering
      context.rendered = () => {
        // Inject some arbitrary JS
        context.js = `console.log('hello')`
      }

      resolve(app)
    })
  })
}

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:

VueSSR.outlet = 'my-app'

In this example, Vue SSR expects a <div id="my-app"> element in the HTML page.

⚠️ 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).

Example project

Head and Body injection

You can modify the head and body of the SSR render with the appendHtml function. This example uses vue-meta:

VueSSR.createApp = function (context) {
  return new Promise((resolve, reject) => {
    const { app, router, store } = createApp()

    router.push(context.url)
    context.meta = app.$meta()

    // ...

    context.appendHtml = () => {
      const {
        title, link, style, script, noscript, meta
      } = context.meta.inject()

      return {
        head: `
          ${meta.text()}
          ${title.text()}
          ${link.text()}
          ${style.text()}
          ${script.text()}
          ${noscript.text()}
        `,
        body: script.text({ body: true })
      }
    }

    resolve(app)
  })
}

LICENCE ISC - Created by Guillaume CHAU (@Akryum)

You can’t perform that action at this time.