最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Vuejs 2 Server side rendering - not working - Stack Overflow

programmeradmin4浏览0评论

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.

Please note that I'm using normal js files not .vue files with es6 and require the html templates using webpack require function.

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:

Error: render function or template not defined in ponent: 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 _binedTickCallback (internal/process/next_tick.js:74:11)

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).

Here is my webpack:

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 =  'monjs2';
  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,
      press: {
        warnings: false
      }
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: true
    }),
    new CopyWebpackPlugin([
      {from: './assets', to: 'assets'},
      {from: './index.html'}
    ])
  ])
}

module.exports = options;

And here is my server entry file:

import { app, router, store } from './src/app'

export default context => {
  // set router's location
  router.push(context.url)
  // call prefetch hooks on ponents matched by the route
  const s = Date.now()
  return Promise.all(router.getMatchedComponents().map(ponent => {
    if (ponent.prefetch) {
      return ponent.prefetch(store)
    }
  })).then(() => {
    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
  })
}

Here is my server.js:

'use strict'
const fs = require('fs')
const path = require('path')
const resolve = file => 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) => {
  console.log(".....ROUTE.......", req.url);
  console.log('renderer', renderer);
  if (!renderer) {
    return res.end('waiting for pilation... 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 => {
    if (firstChunk) {
      // embed initial store state
      if (context.initialState) {
        res.write(
          `<script>window.__INITIAL_STATE__=${
            serialize(context.initialState, { isJSON: true })
          }</script>`
        )
      }
      firstChunk = false
    }
    res.write(chunk)
  })

  renderStream.on('end', () => {
    res.end(template)
    console.log(`whole request: ${Date.now() - s}ms`)
  })

  renderStream.on('error', err => {
    throw err
  })
})

const port = process.env.PORT || 3000
app.listen(port, () => {
  console.log(`server started at http://localhost:${port}`)
})

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.

Please note that I'm using normal js files not .vue files with es6 and require the html templates using webpack require function.

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:

Error: render function or template not defined in ponent: 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 _binedTickCallback (internal/process/next_tick.js:74:11)

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).

Here is my webpack:

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 =  'monjs2';
  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,
      press: {
        warnings: false
      }
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: true
    }),
    new CopyWebpackPlugin([
      {from: './assets', to: 'assets'},
      {from: './index.html'}
    ])
  ])
}

module.exports = options;

And here is my server entry file:

import { app, router, store } from './src/app'

export default context => {
  // set router's location
  router.push(context.url)
  // call prefetch hooks on ponents matched by the route
  const s = Date.now()
  return Promise.all(router.getMatchedComponents().map(ponent => {
    if (ponent.prefetch) {
      return ponent.prefetch(store)
    }
  })).then(() => {
    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
  })
}

Here is my server.js:

'use strict'
const fs = require('fs')
const path = require('path')
const resolve = file => 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) => {
  console.log(".....ROUTE.......", req.url);
  console.log('renderer', renderer);
  if (!renderer) {
    return res.end('waiting for pilation... 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 => {
    if (firstChunk) {
      // embed initial store state
      if (context.initialState) {
        res.write(
          `<script>window.__INITIAL_STATE__=${
            serialize(context.initialState, { isJSON: true })
          }</script>`
        )
      }
      firstChunk = false
    }
    res.write(chunk)
  })

  renderStream.on('end', () => {
    res.end(template)
    console.log(`whole request: ${Date.now() - s}ms`)
  })

  renderStream.on('error', err => {
    throw err
  })
})

const port = process.env.PORT || 3000
app.listen(port, () => {
  console.log(`server started at http://localhost:${port}`)
})
Share Improve this question edited Jan 14, 2017 at 5:04 Saurabh 73.7k44 gold badges191 silver badges251 bronze badges asked Jan 13, 2017 at 22:22 Salah AssiSalah Assi 751 silver badge9 bronze badges 2
  • Do you try to remove this from webpack's config: ``` resolve: { alias: { 'vue$': 'vue/dist/vue' } }, ``` – Quoc-Anh Nguyen Commented Sep 22, 2017 at 14:46
  • nothing happened – Hùng Phong Commented Jun 5, 2018 at 14:17
Add a ment  | 

1 Answer 1

Reset to default 6

Does your index.html template has the placeholder <!--vue-ssr-outlet-->?

发布评论

评论列表(0)

  1. 暂无评论