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}`);
});
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,谢谢!
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
https://github.com/webpack-contrib/mini-css-extract-plugin/issues/48#issuecomment-375288454
https://github.com/webpack-contrib/mini-css-extract-plugin/issues/90
https://github.com/vuejs/vue-ssr-docs/issues/196
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. :)
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
})
]
}
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 asdocument.getElementsByTagNameSo 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/localswill 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 asdocument.getElementsByTagNameSo 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/localswill 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?
@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
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 }) ] }
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);`
Most helpful comment
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.getElementsByTagNameSo what I did to solve it is drop the css file at server render.
note
css-loader/localswill not inject the css file at server, thus solve the problem.Hope that will help you.
Reference
https://github.com/webpack-contrib/mini-css-extract-plugin/issues/48#issuecomment-375288454
820
https://github.com/webpack-contrib/mini-css-extract-plugin/issues/90
https://github.com/vuejs/vue-ssr-docs/issues/196