title: Vuejs 2 Server side rendering - not working
body: <p>I've been working on making my vuejs app function well with SSR but all of my tries failed. I really need help in this.</p>
  
  <p><em>Please note that I'm using normal js files not .vue files with es6 and require the html templates using webpack require function.</em></p>
  
  <p>The app works fine in development mode, however, when I start execute it using 'vue-server-renderer' and go to any route, this error will be thrown:</p>
  
  <blockquote>
    <p>Error: render function or template not defined in component: anonymous
        at normalizeRender (/Users/salaahassi/dev/vue/magicum/node_modules/vue-server-renderer/build.js:6015:13)
        at renderComponent (/Users/salaahassi/dev/vue/magicum/node_modules/vue-server-renderer/build.js:6081:3)
        at renderNode (/Users/salaahassi/dev/vue/magicum/node_modules/vue-server-renderer/build.js:6065:7)
        at render (/Users/salaahassi/dev/vue/magicum/node_modules/vue-server-renderer/build.js:6257:5)
        at RenderStream.render (/Users/salaahassi/dev/vue/magicum/node_modules/vue-server-renderer/build.js:6312:9)
        at RenderStream.tryRender (/Users/salaahassi/dev/vue/magicum/node_modules/vue-server-renderer/build.js:96:12)
        at RenderStream._read (/Users/salaahassi/dev/vue/magicum/node_modules/vue-server-renderer/build.js:125:12)
        at RenderStream.Readable.read (_stream_readable.js:348:10)
        at resume_ (_stream_readable.js:737:12)
        at _combinedTickCallback (internal/process/next_tick.js:74:11)</p>
  </blockquote>
  
  <p>Also, when I disable javascript on my browser, even home page will disappear (that's of course because it's not working from the SSR).</p>
  
  <p>Here is my webpack:</p>
  
  <pre><code>var path = require('path')
  var webpack = require('webpack')
  var HTMLPlugin = require('html-webpack-plugin');
  var CopyWebpackPlugin = require('copy-webpack-plugin');
  var ExtractTextPlugin = require("extract-text-webpack-plugin");
  var extractCSS = new ExtractTextPlugin('styles.css');
  
  var options = {
    // entry: './entry.client.js',
    entry: {
      app: './entry.client.js',
      vendor: [
        'vue',
        'vue-router',
        'vuex',
        'vuex-router-sync',
        'moment',
        'axios'
      ]
    },
    output: {
      path: path.resolve(__dirname, './dist'),
      publicPath: '/',
      filename: '[name].[hash].js',
    },
    module: {
      noParse: /es6-promise\.js$/, // avoid webpack shimming process
      rules: [
        {
          test: /\.html$/,
          loader: 'raw-loader'
        },
        {
          test: /\.js$/,
          loader: 'babel-loader',
          exclude: /node_modules/
        },
        {
          test: /\.json$/,
          loader: 'json-loader'
        },
        {
          test: /\.(png|jpg|gif|svg|woff|woff2|eot|ttf)$/,
          loader: 'file-loader',
          options: {
            name: '[name].[ext]?[hash]'
          }
        },
        {
          test: /\.scss$/,
          loader: extractCSS.extract('css-loader!sass-loader')
        }
      ]
    },
    plugins: [
      extractCSS,
      new webpack.ContextReplacementPlugin(/moment[\\\/]locale$/, /^\.\/(en|zh-tw)$/),
      new webpack.DefinePlugin({
          'process.env': {
              'NODE_ENV': JSON.stringify(process.env.NODE_ENV) || 'development',
              'VUE_ENV': JSON.stringify(process.env.VUE_ENV) || 'client',
          }
      })
    ],
    resolve: {
      alias: {
        'vue$': 'vue/dist/vue'
      }
    },
    devServer: {
      historyApiFallback: true,
      noInfo: true
    },
    devtool: '#eval-source-map'
  }
  console.log("xxxxx ---node env---- xxxx", process.env.NODE_ENV);
  console.log("xxxxx ---vue env---- xxxx", process.env.VUE_ENV);
  if (process.env.NODE_ENV != 'development') {
    options.entry = './entry.server.js';
    options.target = 'node';
    options.output.filename =  'bundle-server.js';
    options.output.libraryTarget =  'commonjs2';
    options.externals = Object.keys(require('./package.json').dependencies);
  }
  
  if (process.env.NODE_ENV == 'development') {
    options.plugins = (options.plugins || []).concat([
      new HTMLPlugin({
        template: './index.html'
      }),
      // extract vendor chunks for better caching
      new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor'
      })
    ]);
  }
  
  if (process.env.VUE_ENV == 'server') {
    options.devtool = '#source-map'
    options.plugins = (options.plugins || []).concat([
      new webpack.optimize.UglifyJsPlugin({
        //sourceMap: true,
        compress: {
          warnings: false
        }
      }),
      new webpack.LoaderOptionsPlugin({
        minimize: true
      }),
      new CopyWebpackPlugin([
        {from: './assets', to: 'assets'},
        {from: './index.html'}
      ])
    ])
  }
  
  module.exports = options;
  </code></pre>
  
  <p>And here is my server entry file:</p>
  
  <pre><code>import { app, router, store } from './src/app'
  
  export default context =&gt; {
    // set router's location
    router.push(context.url)
    // call prefetch hooks on components matched by the route
    const s = Date.now()
    return Promise.all(router.getMatchedComponents().map(component =&gt; {
      if (component.prefetch) {
        return component.prefetch(store)
      }
    })).then(() =&gt; {
      console.log(`data pre-fetch: ${Date.now() - s}ms`)
      // set initial store on context
      // the request handler will inline the state in the HTML response.
      context.initialState = store.state
      return app
    })
  }
  </code></pre>
  
  <p>Here is my server.js:</p>
  
  <pre><code>'use strict'
  const fs = require('fs')
  const path = require('path')
  const resolve = file =&gt; path.resolve(__dirname, file)
  const express = require('express')
  // const favicon = require('serve-favicon')
  const serialize = require('serialize-javascript')
  
  const createBundleRenderer = require('vue-server-renderer').createBundleRenderer
  
  const app = express()
  
  // parse index.html template
  const template = fs.readFileSync(resolve('./dist/index.html'), 'utf-8')
  
  
  // create server renderer from real fs
  const bundlePath = resolve('./dist/bundle-server.js')
  let renderer = createRenderer(fs.readFileSync(bundlePath, 'utf-8'))
  console.log(renderer);
  function createRenderer (bundle) {
    return createBundleRenderer(bundle, {
      cache: require('lru-cache')({
        max: 1000,
        maxAge: 1000 * 60 * 15
      })
    })
  }
  
  var options = {
      maxAge: '60d',
      setHeaders: function(res, path, stat) {
          // Webfonts need to have CORS * set in order to work.
          if (path.match(/ttf|woff|woff2|eot|svg/ig)) {
              res.set('Access-Control-Allow-Origin', '*');
          }
      }
  };
  
  var dist_path = '/dist/';
  app.use(express.static(path.join(__dirname, dist_path), options));
  console.log("............");
  app.get('*', (req, res) =&gt; {
    console.log(".....ROUTE.......", req.url);
    console.log('renderer', renderer);
    if (!renderer) {
      return res.end('waiting for compilation... refresh in a moment.')
    }
    var s = Date.now()
    const context = { url: req.url }
    const renderStream = renderer.renderToStream(context)
    let firstChunk = true
    // console.log(html.head);
    // res.write(html.head)
  
    renderStream.on('data', chunk =&gt; {
      if (firstChunk) {
        // embed initial store state
        if (context.initialState) {
          res.write(
            `&lt;script&gt;window.__INITIAL_STATE__=${
              serialize(context.initialState, { isJSON: true })
            }&lt;/script&gt;`
          )
        }
        firstChunk = false
      }
      res.write(chunk)
    })
  
    renderStream.on('end', () =&gt; {
      res.end(template)
      console.log(`whole request: ${Date.now() - s}ms`)
    })
  
    renderStream.on('error', err =&gt; {
      throw err
    })
  })
  
  const port = process.env.PORT || 3000
  app.listen(port, () =&gt; {
    console.log(`server started at http://localhost:${port}`)
  })
  </code></pre>