Splitting webpack.config.js

A different webpack config is required, depending on whether the application is rendered on server or the client. We want to support both — for development, webpack-dev-server is a powerful tool, which delegates the processing and rendering to the client. In production, we will render on the server. A lot of the webpack config can be shared, such as module , where we declare loaders . Create a folder and two new files for the non unique webpack settings:

The current webpack.config.js contains four properties:

entry module plugins dev-server

module contains the loading rules for .vue files, which both server and client rendering requires. The rest of the properties will be unique the client rendering, so move them to config/client.js :

Add a minimal setup in config/server.js :

Also, move template.html into config :

And update webpack.config.js :

const server = require("./config/server")

const client = require("./config/client") module.exports = {

// ...

plugins: client.plugins.concat(VueLoaderPlugin()) // ...

}

npm run dev shoud still work.

Adding webpack-merge

There is some duplication in webpack.config.js now. We have to join the base config and with client by typing

When we add some server configuration, it will then look like:

This gets confusing very quickly. There is a better way: webpack-merge , which will handle the merging for us.

Now use webpack-merge to clean up webpack.config.js :

Now module.exports returns a function. Webpack checks for the presence of a function exported from webpack,config.js , and if it is one, calls it with a mode argument. mode , oddly enough, corresponds to the --env argument, not --mode , so update the scripts section in package.json :

npm run dev should still be working fine. If you visit localhost:8080 , inspect the source of the page (not using the devtools, the actual page source) you should see:

Notice the msg , Hello does not appear here - that is because it is rendered on the client. We will see a different page source when rendering on the server.

The server side entry

The server rendering bundle will use a different entry point. Create a file for it:

This fuction will create the Vue app which we want to render. Add the following code:

Look familiar? It is similar to the code in src/index.js . Take a look:

The difference is document.addEventListener... . document and the other Web APIs are not available in Node.js, which is why we need two different renderers. Refactor src/index.js :

Lastly, update config/server.js to use create-app :

// ...

module.exports = {

entry: "../src/create-app"

}

Let’s try out new production config with npm run build .

Looks good. Let’s see if we can use the module in a Node.js environment:

We have a problem.

output.library and output.libraryTarget

We need to set some output options in config/server.js . The documetation for output is here.

We are interested in library and libraryTarget . The defaults are:

var means webpack will assign our exports to var , which are attached to the window object for use in the browser (a UMD. Since library is undefined, however, webpack simply does nothing. Although the variable isn't assigned, in src/index.js we do:

So the Vue app still mounts. To get an idea of the options and what they do, run

And try adding the following:

And run npm run build . Let's compare the two:

Now our bundle is assigned to a variable called Bundle . If you want, cd dist && python -m SimpleHTTPServer , then in the browser console and check:

We want to execute in a Node.js environment. We we need to target commonjs2 . Update config/server.js :

And run npm run build . Let's compare the outputs again with diff dist/main.js dist/main_2.js

Looks good! Now we have module.exports . We can check using Node:

There are other things you can pass to library and libraryTarget - find out more in the documentation.

Adding a server (express)

Now that we have a way to create the Vue app on the server, we need to serve it somehow. The easiest way to see this in action is with an express server. I personally like Rails better, and will go into that in a future article.

Anyway, add express , and vue-server-renderer

Create a file for the server code with touch src/server.js .

There are two interesting parts here:

Which instantiates an server renderer instance, and:

Which takes our app, renders it and returns the markup as a string. All that is left is to serve the markup to the user. More info about Vue server renderer is here.

We can try out by running:

If everything it working, you can now visit localhost:8000 and you should see the same old hello message. However, inspecting the element using the devtools shows:

Indicating it was rendered on the server. Let’s compare the page source to the client rendered source, shown earler:

client rendered:

server rendered:

Notice <div app> is not shown anywhere? It is replaced when we we generate the markup using the server renderer, so all that is served is the actual markup. We will add a correct html template in the future for the server side renderer to use.

Conclusion

In this article, we covered:

the different environments webpack can target

how to use webpack-merge

how to user library and libraryTarget

and refactor and share code between the server and client

Improvments

Many improvements are left, which will be covered in the future, such as:

hydrating the app on the server (server rendering using some dynamic data like the user id)

routing (both client and server)

use ES6 syntax, like import and export in Node.js with babel

The source code can be found here.