It happens when add <style></style>
in .vue
file.
[Vue warn]: Error in beforeCreate hook: "ReferenceError: document is not defined"
I mostly wrote code based on tutorial site. https://github.com/vuejs/vue-hackernews-2.0/
src/App.vue
<template>
<div class="red">Hello from App.vue</div>
</template>
<script>
export default { name: "App" }
</script>
<style lang="scss" scoped> <-- Without style works well...
.red { color: red; }
</style>
src/app.js
import Vue from 'vue'
import App from './App.vue'
export function createApp() {
let app = new Vue({
render: h => h(App)
})
}
return { app }
}
src/entry-server.js
import { createApp } from './app'
export default context => {
return new Promise((resolve, reject) => {
const { app } = createApp()
resolve(app)
})
}
src/entry-client.js
import { createApp } from './app'
const { app } = createApp()
app.$mount('#app')
webpack.config.js
const path = require('path')
const webpack = require('webpack')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
const env = process.env.NODE_ENV || 'development'
const isProd = env === 'production'
const baseConfig = {
mode: env,
devtool: isProd
? false
: 'source-map',
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist/',
filename: '[name].js'
},
module: {
noParse: /es6-promise\.js$/,
rules: [
{
test: /\.css$/,
use: [
'vue-style-loader',
'style-loader',
'css-loader',
],
},
{
test: /\.scss$/,
use: [
'vue-style-loader',
'style-loader',
'css-loader',
'sass-loader'
],
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false
}
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
presets: ['@babel/preset-env'],
}
},
{
test: /\.(png|jpg|gif|svg|jpeg)$/,
loader: 'url-loader',
options: {
limit: 10000,
name: '[name].[ext]?[hash]'
}
},
],
},
performance: {
hints: false
},
plugins: isProd
? [
new VueLoaderPlugin()
]
: [
new VueLoaderPlugin(),
new FriendlyErrorsPlugin()
]
}
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')
const { merge } = require('webpack-merge')
const VueSSRClientConfig = merge(baseConfig, {
entry: {
app: './src/entry-client.js'
},
resolve: {
alias: {
'create-api': './create-api-client.js',
},
extensions: ['.js', '.vue']
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.VUE_ENV': '"client"'
}),
new VueSSRClientPlugin()
]
})
const nodeExternals = require("webpack-node-externals")
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
const VueSSRServerConfig = merge(baseConfig, {
target: 'node',
entry: './src/entry-server.js',
output: {
filename: 'server-bundle.js',
path: path.resolve(__dirname, 'dist'),
libraryTarget: 'commonjs2'
},
resolve: {
alias: {
'create-api': './create-api-server.js',
},
extensions: ['.js', '.vue']
},
externals: nodeExternals({
allowlist: /[\.css|\.scss]$/
}),
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.VUE_ENV': '"server"'
}),
new VueSSRServerPlugin()
]
})
module.exports = [VueSSRServerConfig, VueSSRClientConfig]
setup-dev-server.js
const fs = require('fs')
const path = require('path')
const MFS = require('memory-fs')
const webpack = require('webpack')
const serverConfig = require('./webpack.config')[0]
const clientConfig = require('./webpack.config')[1]
const readFile = (fs, file) => {
try {
return fs.readFileSync(path.join(clientConfig.output.path, file), 'utf-8')
} catch (e) {}
}
module.exports = function setupDevServer(app, cb) {
let bundle
let clientManifest
let ready
const readyPromise = new Promise(r => { ready = r })
const update = () => {
if (bundle && clientManifest) {
ready()
cb(bundle, { clientManifest })
}
}
// modify client config to work with hot middleware
clientConfig.entry.app = ['webpack-hot-middleware/client', clientConfig.entry.app]
clientConfig.output.filename = '[name].js'
clientConfig.plugins.push(
new webpack.HotModuleReplacementPlugin(),
)
// dev middleware
const clientCompiler = webpack(clientConfig)
const devMiddleware = require('webpack-dev-middleware')(clientCompiler, {
publicPath: clientConfig.output.publicPath,
noInfo: true
})
app.use(devMiddleware)
clientCompiler.hooks.done.tap('done', stats => {
stats = stats.toJson()
stats.errors.forEach(err => console.error(err))
stats.warnings.forEach(err => console.warn(err))
if (stats.errors.length) return
clientManifest = JSON.parse(readFile(
devMiddleware.fileSystem,
'vue-ssr-client-manifest.json'
))
update()
})
// hot middleware
app.use(require('webpack-hot-middleware')(clientCompiler, { heartbeat: 1000 }))
// watch and update server renderer
const serverCompiler = webpack(serverConfig)
const mfs = new MFS()
serverCompiler.outputFileSystem = mfs
serverCompiler.watch({}, (err, stats) => {
if (err) throw err
stats = stats.toJson()
if (stats.errors.length) return
bundle = JSON.parse(readFile(mfs, 'vue-ssr-server-bundle.json'))
update()
})
return readyPromise
}
server.js
const path = require('path')
const express = require('express')
const app = express()
const resolve = file => path.resolve(__dirname, file)
const isProd = process.env.NODE_ENV === 'production'
const { createBundleRenderer } = require('vue-server-renderer')
function createRenderer(bundle, options) {
return createBundleRenderer(bundle, Object.assign(options, {
basedir: resolve('./dist'),
runInNewContext: !isProd,
}))
}
let renderer
let readyPromise
if (isProd) {
const bundle = require('./dist/vue-ssr-server-bundle.json')
const clientManifest = require('./dist/vue-ssr-client-manifest.json')
renderer = createRenderer(bundle, { clientManifest })
} else {
readyPromise = require('./setup-dev-server')(
app,
(bundle, options) => {
renderer = createRenderer(bundle, options)
}
)
}
function render(req, res) {
const context = req.body || {}
const { requestId } = req.body || {}
renderer.renderToString(context, (err, html) => {
// return json
res.json({ requestId, html })
})
}
app.get('/', isProd ? render : (req, res) => {
readyPromise.then(() => render(req, res))
})
app.listen(9991)
Pretty sure that this is to do with your webpack coniguration. I think it's because style loader is trying to inject your styles into the DOM (which obviously is not present on the server side). Hence the reference error. I'm not 100% sure, but try only using vue-style-loader. There's no need to put it in a chain with style-loader as they are pretty much doing the same thing.
Also run your build command on the project and take a look into the server-bundle. That will show you who's trying to access the DOM.
EDIT:
As a general approach to what you're trying to do, you should also include sass/css in one single rule, like this:
{
test: /\.(sa|sc|c)ss$/,
use: ['vue-style-loader', 'css-loader', 'sass-loader']
},