I have created a Vue SSR application and all instructions/wikis/blogs I have read only tell you how to run the application in a development environment. They do not tell you how to run the application in a production environment.
I have previously written the same app in React SSR application. In that app the build produces a "dist" folder containing the bundle "server_bundle.js". This bundle contains the Express server (server.js) AND the React code. I can run the application from within the "dist" folder using
node dist/server_bundle.js
In the Vue SSR application, the build also produces a "dist" folder. However it contains a "vue-ssr-bundle.json" file which does NOT include the express server (server.js). To run the application in development I have to use the Express server file located in the root of my project rather than running everything from the "dist" directory
node ./server.js
This is OK in development as I am working within my project but in production this will not work as I have to run everything from the "dist" folder.
Building and Running
The app is built using the following commands (in dev i add the "--watch" argument)
webpack --config webpack.server.config.js
webpack --config webpack.client.config.js
These build scripts create and populate the dist (server) and public (client) folders
project_root
- dist
- vue-ssr-bundle.json
- public
- 0_client_bundle.js
- client_bundle.js
- any static images, such as "myImage.jpg"
To run the application I run the Express server using
node ./server.js"
See that I am using "server.js" in the root of my project. It is NOT in the dist folder.
ISSUE So how would I run the application in production which does NOT have my project code, it only has the "dist" and "public" folders produced by my builds?
My code
Project structure
project_root
- src
- assets
- any static images, such as "myImage.jpg"
- client
- client_main.js
- components
- lots of files
- pages
- components which are top level pages
- server
- server_main.js
- vuex
- folders and files containing code pertaining to vuex
- app.js
- App.vue
- router.js
- index.html (the template html file to have content inserted into)
- server.js (the Express Server file)
- webpack.base.config.js
- webpack.client.config.js
- webpack.server.config.js
src/client/client_main.js
import { createApp } from '../app'
const { app, router, store } = createApp()
...
router.onReady(() => {
app.$mount('#app')
})
src/server/server_main.js
import { createApp } from '../app'
export default context => {
return new Promise((resolve, reject) => {
const { app, router, store } = createApp()
router.push(context.url)
router.onReady(() => {
const matchedComponents = router.getMatchedComponents()
if (!matchedComponents.length) {
return reject({ code: 404 })
}
resolve(app)
}, reject)
})
}
src/app.js
import Vue from 'vue'
import App from './App.vue'
import { createStore } from './vuex/store'
import { createRouter } from './router'
import { sync } from 'vuex-router-sync'
export function createApp () {
const store = createStore()
const router = createRouter()
sync(store, router)
const app = new Vue({
router,
store,
render: h => h(App)
})
return { app, router, store }
}
server.js
const fs = require('fs');
const express = require('express');
const { createBundleRenderer } = require('vue-server-renderer');
const bundleRenderer = createBundleRenderer(
require('./dist/vue-ssr-bundle.json'),
{
template: fs.readFileSync('./index.html', 'utf-8')
}
);
const server = express();
server.use(express.static('public'));
server.get('*', (req, res) => {
const context = { url: req.url }
bundleRenderer.renderToString(context, (err, html) => {
if (err) {
if (err.code === 404) {
res.status(404).end('Page not found')
} else {
res.status(500).end('Internal Server Error')
}
} else {
res.end(html)
}
})
});
server.listen(8080);
webpack.base.config.js
const webpack = require('webpack')
module.exports = {
module: {
rules: I wont put them all here to reduce noise, but i am using 'vue-loader', 'babel-loader''file-loader'
},
resolve: {
alias: {'vue$': 'vue/dist/vue.esm.js'},
extensions: ['*', '.js', '.vue', '.json']
},
performance: {
hints: false
},
}
webpack.client.js
var path = require('path')
const merge = require('webpack-merge');
const baseConfig = require('./webpack.base.config.js');
const config = {
entry: './src/client/main.js',
output: {
filename: 'client-bundle.js',
path: path.resolve(__dirname, 'public'),
},
module: {
rules: I wont put them all here to reduce noise, but i am using 'vue-style-loader', 'css-loader'
},
devServer: {
historyApiFallback: true,
noInfo: true,
overlay: true
},
devtool: '#eval-source-map'
}
module.exports = merge(baseConfig, config);
webpack.server.js
const path = require('path')
const merge = require('webpack-merge');
const baseConfig = require('./webpack.base.config.js');
const webpackNodeExternals = require('webpack-node-externals');
const VueSSRPlugin = require('vue-ssr-webpack-plugin')
const config = {
target: 'node',
entry: './src/server/main.js',
output: {
filename: 'server-bundle.js',
path: path.resolve(__dirname, './dist'),
libraryTarget: 'commonjs2'
},
externals: [webpackNodeExternals()],
devtool: '#source-map',
plugins: [
new VueSSRPlugin(),
]
}
module.exports = merge(baseConfig, config);
What I tried
I expected the server build would have the Express server bundled in with the Vue application code just like React and therefore I would just have to run the bundle from within the dist folder. I think this is a much nicer and cleaner solution.
I tried unsuccessfully to change the Vue app to build and run like the React application.
Remove VueSSRPlugin I removed the VueSSRPlugin reference in webpack.server.config.js and saw that in the "dist" file i now have the bundle and images, like with React. However this bundle still does not have the Express server in it. I still did not know how to get the express server in the dist folder
Move express file into src/server folder I thought about moving the express file (server.js) into my projects source hoping it will be added to the bundle. However I did not know how to change the express file seeing as it has a reference to the JSON file which the express server file ends up in.