Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vue ssr document is not defined #2380

Closed
anneincoding opened this issue Sep 7, 2018 · 8 comments
Closed

vue ssr document is not defined #2380

anneincoding opened this issue Sep 7, 2018 · 8 comments

Comments

@anneincoding
Copy link

Hi everyone, I am trying to do server side render by 'vue-server-renderer', I can do

npm run dev

to develop, but when I try

npm run build
npm run start

I always got errors like this

ReferenceError: document is not defined
    at n.(anonymous function).n.(anonymous function).(anonymous function).u.push.n.(anonymous function).Promise.then.n.(anonymous function) (server-bundle.js:1:422)
    at new Promise (<anonymous>)
    at Function.module.exports.o.e (server-bundle.js:1:346)
    at component (server-bundle.js:1:3272)
    at <my address>/node_modules/vue-router/dist/vue-router.common.js:1776:17
    at <my address>/node_modules/vue-router/dist/vue-router.common.js:1803:66
    at Array.map (<anonymous>)
    at <my address>/node_modules/vue-router/dist/vue-router.common.js:1803:38
    at Array.map (<anonymous>)
    at flatMapComponents (<my address>/node_modules/vue-router/dist/vue-router.common.js:1802:26)
error during render : /
ReferenceError: document is not defined
    at n.(anonymous function).n.(anonymous function).(anonymous function).u.push.n.(anonymous function).Promise.then.n.(anonymous function) (webpack/bootstrap:55:0)
    at new Promise (<anonymous>)
    at Function.module.exports.o.e (webpack/bootstrap:50:0)
    at component (src/router/index.js:12:43)
    at <my address>/node_modules/vue-router/dist/vue-router.common.js:1776:17
    at <my address>/node_modules/vue-router/dist/vue-router.common.js:1803:66
    at Array.map (<anonymous>)
    at <my address>/node_modules/vue-router/dist/vue-router.common.js:1803:38
    at Array.map (<anonymous>)
    at flatMapComponents (<my address>/node_modules/vue-router/dist/vue-router.common.js:1802:26)

my server.js looks like this

const fs = require('fs');
const path = require('path');
const express = require('express');
const favicon = require('serve-favicon');
const compression = require('compression');
// const microcache = require('route-cache');
const serialize = require('serialize-javascript');
const LRU = require('lru-cache');
const { createBundleRenderer } = require('vue-server-renderer');
const resolve = file => path.resolve(__dirname, file);

const isProd = process.env.NODE_ENV === 'production';
// const useMicroCache = process.env.MICRO_CACHE !== 'false';

// const serverInfo = `express/${require('express/package.json').version}` +
//     `vue-server-renderer/${require('vue-server-renderer/package.json').version}`;

const app = express();

const proxy = require('http-proxy-middleware');

app.use('/index', proxy({'target': 'https://xiaochengxu.huxiu.com', 'changeOrigin': true}));

function createRenderer (bundle, options) {
  // https://github.com/vuejs/vue/blob/dev/packages/vue-server-renderer/README.md#why-use-bundlerenderer
  return createBundleRenderer(bundle, Object.assign(options, {
    // for component caching
    'cache': LRU({
      'max': 1000,
      'maxAge': 1000 * 60 * 15
    }),
    // this is only needed when vue-server-renderer is npm-linked
    'basedir': resolve('./dist'),
    // recommended for performance
    'runInNewContext': false
  }));
}

let renderer;
let readyPromise;
const templatePath = resolve('./src/index.template.html');

if (isProd) {
  // In production: create server renderer using template and built server bundle.
  // The server bundle is generated by vue-ssr-webpack-plugin.
  const template = fs.readFileSync(templatePath, 'utf-8');
  // const template = fs.readFileSync('./dist/index.html', 'utf-8');
  const serverBundle = require('./dist/vue-ssr-server-bundle.json');
  // The client manifests are optional, but it allows the renderer
  // to automatically infer preload/prefetch links and directly add <script>
  // tags for any async chunks used during render, avoiding waterfall requests.
  const clientManifest = require('./dist/vue-ssr-client-manifest.json');

  renderer = createRenderer(serverBundle, {
    template,
    clientManifest
  });
} else {
  // In development: setup the dev server with watch and hot-reload,
  // and create a new renderer on bundle / index template update.
  readyPromise = require('./build/dev-server')(
    app,
    templatePath,
    (bundle, options) => {
      renderer = createRenderer(bundle, options);
    }
  );
}


const serve = (path, cache) => express.static(resolve(path), {
  'maxAge': cache && isProd ? 60 * 60 * 24 * 30 : 0
});
// 加载和设置static

app.use(compression({ 'threshold': 0 }));
app.use(favicon('./public/favicon.ico'));
app.use('/dist', serve('./dist', true));
app.use('/public', serve('./public', true));
// app.use('/manifest.json', serve('./manifest.json', true));
app.use('/service-worker.js', serve('./dist/service-worker.js'));
// app.use(microcache.cacheSeconds(1, req => useMicroCache && req.originalUrl));

function render(req, res) {
  // res.setHeader('Context-Type', 'text/html');
  // res.setHeader('Server', serverInfo);
  const s = Date.now();
  const context = {
    'title': '虎嗅网', // default title
    'url': req.url
  };

  const handleError = err => {
    if (err.url) {
      res.redirect(err.url);
    } else if (err.code === 404) {
      res.status(404).send('404 | Page Not Found');
    } else {
      // Render Error Page or Redirect
      res.status(500).send('500 | Internal Server Error');
      console.error(`error during render : ${req.url}`);
      console.error(err.stack);
    }
  };
  let html = '';
  const renderStream = renderer.renderToStream(context);

  renderStream.once('data', data => {
    html += data.toString();
  });
  renderStream.on('data', chunk => {
    res.write(chunk);
  });
  renderStream.on('end', () => {
    if (context.initialState) {
      res.write(
        `<script>window.__INSTAL_STATE__=${
          serialize(context.initialState, {'isJSON':true})
        }</script>`
      );
    }
    res.end(html.tail);
    console.log(`whole request: ${Date.now() - s}ms`);
  });
  renderStream.on('error', handleError);
}

app.get('*', isProd ? render : (req, res) => {
  readyPromise.then(() => render(req, res));
});

const port = process.env.PORT || 9028;

app.listen(port, () => {
  console.log(`server started at  http://localhost:${port}`);
});
@vue-bot
Copy link

vue-bot commented Sep 7, 2018

Hello, your issue has been closed because it does not conform to our issue requirements. In order to ensure every issue provides the necessary information for us to investigate, we require the use of the Issue Helper when creating new issues. Thank you!


你好,你的 issue 不符合我们所要求的格式,因此已被自动关闭。为了确保每个 issue 都提供必需的相关信息,请务必使用我们的 Issue 向导 来创建新 issue,谢谢!

@vue-bot vue-bot closed this as completed Sep 7, 2018
@simon300000
Copy link

simon300000 commented Dec 2, 2018

I also had the same problem, I found it might be caused by mini-css-extract-plugin.
mini-css-extract-plugin will extract css file and inject, which use function such as document.getElementsByTagName

So what I did to solve it is drop the css file at server render.

const merge = require('webpack-merge')
const nodeExternals = require('webpack-node-externals')
const baseConfig = require('./webpack.base.config.js')
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')

module.exports = merge(baseConfig, {
  entry: './src/entry-server.js',
  target: 'node',
  devtool: 'source-map',
  output: {
    libraryTarget: 'commonjs2'
  },
  externals: nodeExternals({
    whitelist: /\.css$/
  }),
  plugins: [
    new VueSSRServerPlugin()
  ],
  module: {
    rules: [{
      test: /\.css$/,
      use: [
        'css-loader/locals'
      ]
    }, {
      test: /\.scss$/,
      use: [
        'css-loader/locals',
        'sass-loader'
      ]
    }]
  }
})

note css-loader/locals will not inject the css file at server, thus solve the problem.

Hope that will help you.

Reference
webpack-contrib/mini-css-extract-plugin#48 (comment)
#820

webpack-contrib/mini-css-extract-plugin#90
vuejs/vue2-ssr-docs#196

@aeharding
Copy link

For me, I got a very similar error (with vue-router and the anonymous functions, running with dev but not build and whatnot).

The fix was I was refreshing my babel config and took out "dynamic-import-node" from the plugins section. I needed to add it back... and it started working again. :)

@Murphycx94
Copy link

use extract-css-chunks-webpack-plugin instead mini-css-extract-plugin

webpack.base.config.js

const ExtractCssChunksPlugin = require('extract-css-chunks-webpack-plugin')
{
  ...
  {
        test: /\.css$/,
        use: [
          {
            loader: ExtractCssChunksPlugin.loader,
            options: {
              hot: !isProd,
              reloadAll: !isProd
            }
          },
          'postcss-loader',
          'css-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          {
            loader: ExtractCssChunksPlugin.loader,
            options: {
              hot: !isProd,
              reloadAll: !isProd
            }
          },
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      }
  ...
  plugins: [
   ...
    new ExtractCssChunksPlugin({
      filename: isProd ? 'css/[name].[contenthash:8].css' : '[name].css',
      chunkFilename: isProd ? 'css/[name].[contenthash:8].chunk.css' : '[name].chunk.css'
    })
  ]
}

webpack.server.config.js

{
  ...
  plugins: [
    new webpack.optimize.LimitChunkCountPlugin({
      maxChunks: 1
    })
  ]
}

extract-css-chunks-webpack-plugin

@lycHub
Copy link

lycHub commented Jan 5, 2020

I also had the same problem, I found it might be caused by mini-css-extract-plugin.
mini-css-extract-plugin will extract css file and inject, which use function such as document.getElementsByTagName

So what I did to solve it is drop the css file at server render.

const merge = require('webpack-merge')
const nodeExternals = require('webpack-node-externals')
const baseConfig = require('./webpack.base.config.js')
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')

module.exports = merge(baseConfig, {
  entry: './src/entry-server.js',
  target: 'node',
  devtool: 'source-map',
  output: {
    libraryTarget: 'commonjs2'
  },
  externals: nodeExternals({
    whitelist: /\.css$/
  }),
  plugins: [
    new VueSSRServerPlugin()
  ],
  module: {
    rules: [{
      test: /\.css$/,
      use: [
        'css-loader/locals'
      ]
    }, {
      test: /\.scss$/,
      use: [
        'css-loader/locals',
        'sass-loader'
      ]
    }]
  }
})

note css-loader/locals will not inject the css file at server, thus solve the problem.

Hope that will help you.

Reference
webpack-contrib/mini-css-extract-plugin#48 (comment)
#820

webpack-contrib/mini-css-extract-plugin#90
vuejs/vue-ssr-docs#196

I also had the same problem, I found it might be caused by mini-css-extract-plugin.
mini-css-extract-plugin will extract css file and inject, which use function such as document.getElementsByTagName

So what I did to solve it is drop the css file at server render.

const merge = require('webpack-merge')
const nodeExternals = require('webpack-node-externals')
const baseConfig = require('./webpack.base.config.js')
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')

module.exports = merge(baseConfig, {
  entry: './src/entry-server.js',
  target: 'node',
  devtool: 'source-map',
  output: {
    libraryTarget: 'commonjs2'
  },
  externals: nodeExternals({
    whitelist: /\.css$/
  }),
  plugins: [
    new VueSSRServerPlugin()
  ],
  module: {
    rules: [{
      test: /\.css$/,
      use: [
        'css-loader/locals'
      ]
    }, {
      test: /\.scss$/,
      use: [
        'css-loader/locals',
        'sass-loader'
      ]
    }]
  }
})

note css-loader/locals will not inject the css file at server, thus solve the problem.

Hope that will help you.

Reference
webpack-contrib/mini-css-extract-plugin#48 (comment)
#820

webpack-contrib/mini-css-extract-plugin#90
vuejs/vue-ssr-docs#196

Hello, in the version of vue-cli3+, how to use css-loader/locals?
I tried for a long time but failed. Do you have any examples?

@simon300000
Copy link

simon300000 commented Jan 5, 2020

@lycHub Hello, I build SSR with webpack, I do not have any examples with vue-cli3

PS: I do have one, but it is "hybrid", SSR is still handled by webpack
https://github.com/simon300000/musedash.moe

@mjmnagy
Copy link

mjmnagy commented Aug 25, 2020

use extract-css-chunks-webpack-plugin instead mini-css-extract-plugin

webpack.base.config.js

const ExtractCssChunksPlugin = require('extract-css-chunks-webpack-plugin')
{
  ...
  {
        test: /\.css$/,
        use: [
          {
            loader: ExtractCssChunksPlugin.loader,
            options: {
              hot: !isProd,
              reloadAll: !isProd
            }
          },
          'postcss-loader',
          'css-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          {
            loader: ExtractCssChunksPlugin.loader,
            options: {
              hot: !isProd,
              reloadAll: !isProd
            }
          },
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      }
  ...
  plugins: [
   ...
    new ExtractCssChunksPlugin({
      filename: isProd ? 'css/[name].[contenthash:8].css' : '[name].css',
      chunkFilename: isProd ? 'css/[name].[contenthash:8].chunk.css' : '[name].chunk.css'
    })
  ]
}

webpack.server.config.js

{
  ...
  plugins: [
    new webpack.optimize.LimitChunkCountPlugin({
      maxChunks: 1
    })
  ]
}

extract-css-chunks-webpack-plugin

Hi,

I am running into the same problem. However, i attempted to add your solution into my vue.config.js file as such

const path = require('path')
const ExtractCssChunksPlugin = require('extract-css-chunks-webpack-plugin')

module.exports = {
  outputDir: 'dist',
  lintOnSave: false,
  css: {
    extract: true //false
  },
  chainWebpack: config => {
    config.resolve.alias.set('@', path.resolve(__dirname))
  },
  configureWebpack: {
    optimization: {
      splitChunks: {
        cacheGroups: {
          styles: {
            name: 'styles',
            test: /\.css$/,
            chunks: 'all',
            enforce: true
          }
        }
      }
    },
    plugins: [
      new ExtractCssChunksPlugin({
        filename: '[name].css'
      })
    ],
    module: {
      rules: [
        {
          test: /\.css$/,
          use: [
            {
              loader: ExtractCssChunksPlugin.loader
            },
            'postcss-loader',
            'css-loader'
          ]
        }
      ]
    }
  }
}

i get the following error

(1:1) Unknown word

> 1 | // Imports
    | ^
  2 | var ___CSS_LOADER_API_IMPORT___ = require("../../../node_modules/css-loader/dist/runtime/api.js");
  3 | exports = ___CSS_LOADER_API_IMPORT___(false);`

@codeDebugTest
Copy link

@lycHub Hello, I build SSR with webpack, I do not have any examples with vue-cli3

PS: I do have one, but it is "hybrid", SSR is still handled by webpack https://github.com/simon300000/musedash.moe

I create a vue-ssr-demo project with vue-cli, and I also encountered the same problem. My solution is replace the mini-css-extract-plugin with a new loader

        // css-loader mini-css-extract-plugin(extract-css-loader),will generate browser sentence such as document.getElementsByTagName xxxxx。
        // this will result in error (document not defined), running on server side。
        // so delete mini-css-extract-plugin and replace with css-context-loader。
        const langs = ['css', 'less']
        const types = ['vue-modules', 'vue', 'normal-modules', 'normal']

        langs.forEach(lang => {
          types.forEach(type => {
            const rule = config.module.rule(lang).oneOf(type)
            rule.uses.delete('extract-css-loader')
            rule.use('css-context-loader').loader(CssContextLoader).before('css-loader')
          })
        })

the new loader you can reference vue-cli-plugin-ssr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants