vue: SSR fails when split code

Version

2.5.16

Reproduction link

https://github.com/vincent-yao27/vue-ssr-bug

Steps to reproduce

  1. Create a project with vue-cli 3.
  2. Set up SSR.
  3. Use import('path/to/component.vue') to split code.
  4. Build server bundle.
  5. Start a server to render the pages.
// vue.config.js 

const SSRServerPlugin = require('vue-server-renderer/server-plugin')
const nodeExternals = require('webpack-node-externals')

module.exports = {
	configureWebpack: {
		entry: './src/entry-server',
		target: 'node',
		devtool: 'source-map',
		output: {
			libraryTarget: 'commonjs2'
		},
		externals: nodeExternals({ whitelist: /\.css$/ }),
		optimization: {
			splitChunks: false
		},
		plugins: [new SSRServerPlugin()],
	}
}


// router.js

import Vue from 'vue'
import Router from 'vue-router'
import About from './views/About.vue'

Vue.use(Router)

export function createRouter() {
  return new Router({
    mode: 'history',
    routes: [
      {
        path: '/',
        name: 'home',
        component: () => import('./views/Home.vue')
      },
      {
        path: '/about',
        name: 'about',
        component: About
      }
    ]
  })
}


// main.js

import Vue from 'vue'
import App from './App.vue'
import { createRouter } from './router'

Vue.config.productionTip = false

export function createApp() {
  const router = createRouter()

  const app = new Vue({
    router,
    render: h => h(App)
  })

  return { app, router }
}


// entry-server.js

import { createApp } from './main'

export default (context) => new Promise((resolve, reject) => {
	const { app, router } = createApp()

	router.push(context.url)

	router.onReady(() => {
		const matchedComponents = router.getMatchedComponents()
		if (!matchedComponents.length) {
			return reject({ code: 404 })
		}
		resolve(app)
	}, reject)
})


// server.js

const express = require('express')
const { createBundleRenderer } = require('vue-server-renderer')

const app = express();
const bundle = require('./dist/vue-ssr-server-bundle.json')

const renderer = createBundleRenderer(bundle, {
	runInNewContext: false,
})

app.get('*', (req, res) => {
	const context = { url: req.url }
	renderer.renderToString(context, (err, html) => {
		if (err) console.error(err)
		res.end(html)
	})
})

app.listen(3000)

What is expected?

Render path /, /about successfully.

What is actually happening?

  1. Get ReferenceError: document is not defined when path / is rendered.
  2. Render path /about successfully.
  3. After deleting configureWebpack.optimization.splitChunks in vue.config.js, it shows an Error: Server-side bundle should have one single entry file. Avoid using CommonsChunkPlugin in the server config during building.

system: OS X 10.11.6 node: 8.11.3 vue-server-renderer: 2.5.16 webpack: 4.15.1

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 20 (3 by maintainers)

Most upvoted comments

参考我的vue.config.js 配置文件

SSR真是坑,足足被坑了几个小时才解决 一直在报 Error: Server-side bundle should have one single entry file. Avoid using CommonsChunkPlugin in the server config.

解决方法: 注意我的第46行配置为: splitChunks: TARGET_NODE ? false : undefined node环境下必须将splitChunks才不报错

完整vue.config.js文件


//webpack 服务端、客户端插件
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')

// 优化相关
const nodeExternals = require('webpack-node-externals')
// 合并相关
const merge = require('lodash.merge')

// 环境变量:决定入口是客户端还是服务端
const TARGET_NODE = process.env.WEBPACK_TARGET === "node"
const target = TARGET_NODE? "server": "client"

module.exports = {

    css: {
        extract: false
    },

    // 根据服务器或者客户端,编译到不同文件夹
    outputDir: './dist/'+target,

    configureWebpack: ()=>({
        // 将entry指向应用程序的 server/client 文件
        entry: `./src/entry-${target}.js`,
        // 对 bundle renderer 提供 source map支持
        devtool: 'source-map',
        // 这运行 webpack 以 Node 适用方式处理动态导入(dynamic import)
        // 并且还会在编译 Vue 组件时告知 'vue-loader' 输出面向服务器代码(server-oriented code)
        target: TARGET_NODE? 'node' : 'web',
        node: TARGET_NODE? undefined : false,
        output: {
            // 此处告知 server bundle 使用Node风格导出模块
            libraryTarget: TARGET_NODE ? "commonjs2" : undefined
        },
        //外置化应用程序依赖模块。可以使服务器构建速度更快,并生成较小的bundle文件
        externals: TARGET_NODE
            ? nodeExternals({
                // 不要外置化webpack  需要处理的依赖模块
                // 可以在这里添加更多的文件类型。例如,未处理 *.vue 原始文件
                // 你还应该将修改 `global` (例如 polyfill)的依赖模块列入白名单
                whitelist: [/\.css$/]
            })
            : undefined,
        optimization: {
            splitChunks: TARGET_NODE ? false : undefined 
        },
        // 这是将服务器的整个输出构建为单个JSON文件的插件
        // 服务端默认文件名为 `vue-ssr-server-bundle.json`
        plugins: [TARGET_NODE? new VueSSRServerPlugin(): new VueSSRClientPlugin()]
    }),

    chainWebpack: config => {
        config.module
            .rule('vue')
            .use('vue-loader')
            .tap(options => {
                merge(options, {
                    optimizeSSR: false
                })
            })
    }
}

chainWebpack: config => { if (process.env.NODE_ENV === 'production') { config.optimization.delete('splitChunks') } }

This is a simple option

I switched to https://github.com/Akryum/vue-cli-plugin-ssr code splitting is working well now for me

@yekver or @vincent-yao27 did you ever overcome this issue? We’re on vue cli 3.0.3 and haven’t seen how to solve this.

I have the same error while using https://github.com/Akryum/vue-cli-plugin-ssr. Also I’ve realized that the problem is that the content of mini-extract-css-plugin is inside vue-ssr-server-bundle.json

@posva @zickat please try webpack 4 and mini-extract-css-plugin

export default [
  {
    path: '/',
    component: () => import(/* webpackChunkName: "index" */ '../views/index.vue')
  },
  {
    path: '/login',
    component: () => import(/* webpackChunkName: "login" */ '../views/login.vue')
  }
]

when you write some css in each of .vue file (index or login but not App.vue)

you will find this problem “document is not defined” and see the content of vue-ssr-server-bundle.json has some document