Let's make a project first and install some dependencies:
Here is what we get in package.json:
Now we can render a Vue instance to string:
node ssr.js comes out the html string:
The root element has a
server-rendered="true" attribute. We'll talk about it later.
In most cases, we use numerous .vue files. As we have:
In our server-side entry point, export a function. The function will receive the render context object (passed to bundleRenderer.renderToString or bundleRenderer.renderToStream), and should return a Promise, which should eventually resolve to the app's root Vue instance:
We do need to use a slightly different webpack.config.js and entry point for our server-side bundle.
And .babelrc is needed:
webpack --config webpack/webpack.server.js we'll get a dist/bundle.server.js file as the server-side bundle which can be read in server-side.
node ssr-bundle.js comes out a similar html string:
Client Side Hydration
First of all, we have to create another bundle file for client-side. A webpack.client.js is needed. It is almost the same with webpack.server.js except NOT having
target: 'node' and
output.libraryTarget: 'commonjs2', which bundle the client-side entry file:
Then we will improve the above ssr-bundle.js example to an express server:
For each render call, the code will be re-run in a new context using Node.js' vm module. This ensures your application state is discrete between requests, and you don't need to worry about structuring your application in a limiting pattern just for the sake of SSR.
Now, start the server and hit http://127.0.0.1:3000/ on your favorite browser. But we get a warning message in the DevTool:
[Vue warn]: The client-side rendered virtual DOM tree is not matching server-rendered content. Bailing hydration and performing full client-side render.
Cause we have the
data() function returning a random value in src/App.vue, so we got different html string form server and client via virtual-DOM.
In server-rendered output, the root element will have the server-rendered="true" attribute. On the client, when you mount a Vue instance to an element with this attribute, it will attempt to "hydrate" the existing DOM instead of creating new DOM nodes.
Let's make some change:
Refresh the page and click the
+1 button. See what's happening on both server and client side. Checkout this repo to see the full example.