How to use SSR with Vue 3 - Vue packages version mismatch

13.8k views Asked by At

I have a working Vue 2 app with server side rendering. Now I'm trying to upgrade to Vue 3 but stuck on the SSR part cuz the vue-server-renderer package throws the following error:

Vue packages version mismatch: - [email protected] - [email protected] This may cause things to work incorrectly. Make sure to use the same version for both.

But there is no version 3.0.0 for vue-server-renderer and I have an "Vue packages version mismatch" kind of error.

With googling I found this issue on the vue-next repository: https://github.com/vuejs/vue-next/issues/1327

But for me it is still unclear how to achieve SSR with version 3 of vue. Is it already possible? Is there an example how to use SSR with Vue 3?

1

There are 1 answers

5
Raukaute On BEST ANSWER

Vue-server-renderer can only be used with vue version 2. One of the big changes with version 3 is that it now has SSR support baked in.

So instead of using the vue-2.0 server-renderer you now just have to use vue's createSSRApp. On the server, to render the thus created app to a string that you can send to the browser you'd use renderToString method which you can import from @vue/server-renderer (note that you have to install this package seperately).

As a super basic (without bundler or anything) example this would look sth like this:

const express = require('express');
const { createSSRApp } = require('vue');
const { renderToString } = require('@vue/server-renderer');

const app = express();

const example= {
  template: `
    <div>
      Hello World
    </div>`,
};

function renderVueApp(req, res) {
  const vueApp = createSSRApp(example);

  (async () => {
    const html = await renderToString(vueApp);

    res.send(`
      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <script src="https://unpkg.com/vue@next"></script>
          <title>About blank</title>
        </head>
        <body>
          <div id="app">${html}</div>
          <script>
            const example = { template: '<div>Hello World</div>}; 
            Vue.createSSRApp(example).mount('#app', true);
          </script>
        </body>
      </html>
    `);
  })();
}

app.get('/', renderVueApp);

const port = process.env.PORT || 8080;
app.listen(port, () =>
  console.log(`Server started at localhost:${port}. Press ctrl+c to quit.`)
);

In the front-end you let vue take over the markup from the server, i.e. you create and mount the app in hydration mode.

The bundle renderer you referenced in your question is more or less just extracted from the vue-2.0 server-renderer. In order to use it, you will have to use the client-plugin and server-plugin from the vue-2.0 server-renderer package and plug them into your webpack process to get the server-bundle and the client manifest.

Note that with this setup, the bundle renderer will only inject entry/initial scripts with rel="preload". Right now, the 'new' vue-loader won't inject any component registration logic into components (like the 'old' vue loader does). Still, vue-bundle-renderer can and will inject async chunks with rel="preload", as long as they are referenced in ssrContext._registeredComponents. So if you need this feature in your app, you will have to write up that logic yourself.

Of course, this is one way to do it. I'm sure there plenty more roads that lead to the same destination.

I wrote up a vue3 version of the vue2 hackernews clone, which uses all the described/ mentioned things.

You can find it here: https://github.com/raukaute/vue-hackernews-3.0