Vue-cli3 webpack4 server rendering bug Failed to resolve async component

Project: the initial project of vue-cli3, including vue-router

< H2 > error < / H2 >

when doing ssr rendering, routing lazy loading is used in router.js, and then the code of the two entrances is packaged with webpack, but there is an error accessing the main page when starting the server port. The reason for the error in production environment access is:

[vue-router] Failed to resolve async component default: ReferenceError: document is not defined
[vue-router] uncaught error during route navigation:
ReferenceError: document is not defined
    at n.(anonymous function).n.(anonymous function).i.(anonymous function).u.push.n.(anonymous function).Promise.then.n.(anonymous function) (server-bundle.js:1:497)
    at new Promise (<anonymous>)
    at Function.module.exports.o.e (server-bundle.js:1:359)
    at b (server-bundle.js:1:4079)
    at /Users/zhangbo/Desktop/vue-ssr-example/node_modules/vue-router/dist/vue-router.common.js:1776:17
    at /Users/zhangbo/Desktop/vue-ssr-example/node_modules/vue-router/dist/vue-router.common.js:1803:66
    at Array.map (<anonymous>)
    at /Users/zhangbo/Desktop/vue-ssr-example/node_modules/vue-router/dist/vue-router.common.js:1803:38
    at Array.map (<anonymous>)
    at flatMapComponents (/Users/zhangbo/Desktop/vue-ssr-example/node_modules/vue-router/dist/vue-router.common.js:1802:26)
< H2 > reason < / H2 >

when the component is not lazily loaded, the rendering is normally accessible, but the above error will be reported after lazy loading. After inspection, it is found that the browser document api is called in server-bundle.js to create the link tag in header. The problem should be here, but there is no answer compared with other ssr demo webpack configurations. I hope there is a big boss to answer.

vue.config.js

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("webpack-merge")
const path = require("path")
const isServer = process.env.WEBPACK_TARGET === "server"

module.exports = {
  configureWebpack: () => {
    const Config = {
      entry: `./src/entry-${process.env.WEBPACK_TARGET}.js`,
      devtool: "source-map",
      target: isServer ? "node" : "web"
    }
    if (process.env.WEBPACK_TARGET === "server") {
      Config.output = { libraryTarget: "commonjs2", filename: "server-bundle.js" }
      Config.externals = nodeExternals({ whitelist: [/\.css$/] })
      Config.plugins = [new VueSSRServerPlugin()]
    } else {
      Config.plugins = [new VueSSRClientPlugin()]
    }
    console.log(Config)
    return Config
  },
  chainWebpack: config => {
    config.module
      .rule("vue")
      .use("vue-loader")
      .tap(options => {
        merge(options, {
          optimizeSSR: false
        })
      })

    config.plugin("html")
      .tap(args => {
        args[0].template = path.resolve(__dirname, "./src/index.temp.html")
        return args
      })
  }
}

router.js

import Vue from "vue"
import Router from "vue-router"

// import Home from "./views/Home.vue"
const Home = () => import("./views/Home.vue")

Vue.use(Router)

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

visit the home page after packaging, the server returns, but the page is blank, and an error document undefined

is reported.
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><title>{{title}}</title><link href=/css/chunk-67b7f2f3.42d2cf04.css rel=prefetch><link href=/js/about.1df3e7f5.js rel=prefetch><link href=/js/chunk-67b7f2f3.7f2bbd0a.js rel=prefetch><link href=/css/main.192eb16d.css rel=preload as=style><link href=/js/chunk-vendors.d536d49e.js rel=preload as=script><link href=/js/main.f21f17e6.js rel=preload as=script><link href=/css/main.192eb16d.css rel=stylesheet></head><body><script src=/js/chunk-vendors.d536d49e.js></script><script src=/js/main.f21f17e6.js></script></body></html>

Thank you very much.


in most cases: this problem occurs only when router (asynchronous loading) uses code splited.

reason: some plugins, is used and then some global variables such as document are generated in vue-ssr-server-bundle . These are defined in browser, and if they appear and execute on the server side, of course an error will be reported.

you can avoid such an error only by changing to Synchronize router, but you can't load routes with lazy loading (no feasible solution has been found at this time). But if you use global functions such as window and document in component, you can introduce jsdom to create a manual solution.

<script>
const {
    JSDOM
} = require('jsdom'); // document undefined
export default {
    name: 'List1View',
    computed: {
        items() {
            return this.$store.state['items']
        }
    },
    asyncData({ store }) {
        const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
            'url': 'http://localhost:3000'
        });
        dom.window.document.getElementById('test');//demo
        return store.dispatch('fetchItem', {
            id: 111
        })
    }
}
</script>
Menu