Client-side rendered template adds additional Carriage Return characters to DOM
See original GitHub issueVersion
vue: 2.5.21 vue-server-renderer: 2.5.21 vue-template-compiler: 2.5.21 vue-loader: 15.4.2 webpack: 4.27.1 @babel/core: 7.2.0 @babel/plugin-transform-runtime: 7.2.0 @babel/preset-env: 7.2.0
(I’m not sure, but I think this problem was non-existent when I was using vue-loader@13)
Reproduction link
(Needs build step)
Steps to reproduce
- Write source code with markup elements that have new lines in it
- Build it & run it (or hot-reload in development environment)
- (More info in the image below)
What is expected?
Line feeds, carriage returns and whitespaces should be removed from the compiled template. Like here: https://vuejs.org/v2/guide/render-function.html#Template-Compilation
Hydration doesn’t add additional carriage return (%0D) characters to rendered content.
Visually rendered content doesn’t have any unnecessary spaces.
What is actually happening?
Line feeds, carriage returns and whitespaces remain in compiled code and on top of that render differently after SSR hydration.
Hydration adds additional carriage return (%0D) characters to rendered content.
Visually rendered content has unnecessary spaces after SSR hydration (but not before).
This behavior can be observed here: https://workaline.com/collection/vue (it looks fine before hydration kicks in)
Image explaining it a little bit more:
Configs:
Note: I’ve tried changing vue-loader compilerOptions.preserveWhitespace
to different values - but that didn’t changed anything.
webpack.base.config.js
/*------------------------------------*\
Imports
\*------------------------------------*/
const NODE_ENV = process.env.NODE_ENV || 'development';
const path = require('path');
const postCSSPresetEnv = require('postcss-preset-env');
/*------------------------------------*\
Options
\*------------------------------------*/
const browsersList = [
'> 1%',
'ie >= 10',
'ie_mob >= 10',
'ff >= 40',
'chrome >= 40',
'safari >= 7',
'ios >= 7',
'android >= 4.4',
]
/*------------------------------------*\
Base
\*------------------------------------*/
var baseConfig = {
mode: NODE_ENV,
devtool: 'cheap-module-eval-source-map',
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false
}
}
},
{
test: /\.js$/,
include : path.join(__dirname, '/src'),
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true,
compact: true,
presets: [
[
'@babel/preset-env', {
targets: {
browsers: browsersList,
}
},
],
],
},
},
],
},
{
test: /\.scss$/,
exclude: /node_modules/,
use: [
{
loader: 'vue-style-loader',
},
{
loader: 'css-loader',
},
{
loader: 'sass-loader',
options: {
outputStyle: 'compressed',
},
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
postCSSPresetEnv({
browsers: browsersList,
}),
]
},
},
],
},
{
test: /\.css$/,
use: [
{
loader: 'vue-style-loader',
},
{
loader: 'css-loader',
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
postCSSPresetEnv({
browsers: browsersList,
}),
]
},
},
],
},
{
test: /\.(png|jpe?g|gif|eot|svg|otf|ttf|woff|woff2)(\?\S*)?$/,
use: [
{
loader: 'url-loader',
query: {
limit: 100000000,
},
},
],
},
],
},
};
/*------------------------------------*\
Export
\*------------------------------------*/
module.exports = baseConfig;
webpack.client.config.js
/*------------------------------------*\
Imports
\*------------------------------------*/
const path = require('path');
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const WebpackCleanupPlugin = require('webpack-cleanup-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const NODE_ENV = process.env.NODE_ENV || 'development';
/*------------------------------------*\
Import Base Config
\*------------------------------------*/
var baseConfig = require('./webpack.base.config.js');
/*------------------------------------*\
Client
\*------------------------------------*/
var clientConfig = webpackMerge(baseConfig, {
entry: {
app: path.join(__dirname, '/src/entry.client.js'),
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"' + NODE_ENV + '"',
VUE_ENV: '"client"',
}
}),
new VueLoaderPlugin(),
new VueSSRClientPlugin(),
],
});
/*------------------------------------*\
Development
\*------------------------------------*/
if (NODE_ENV === 'development') {
clientConfig.output = {
path: path.join(__dirname, '/dist'),
publicPath: '/dist/',
filename: '[name].[hash].js',
};
}
/*------------------------------------*\
Production
\*------------------------------------*/
if (NODE_ENV === 'production') {
clientConfig.devtool = '';
clientConfig.output = {
path: path.join(__dirname, '/dist'),
publicPath: '/dist/',
filename: '[name].[hash].js',
}
clientConfig.plugins = (clientConfig.plugins || []).concat([
new WebpackCleanupPlugin({
preview: false,
}),
new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: false,
}),
]);
}
/*------------------------------------*\
Export
\*------------------------------------*/
module.exports = clientConfig;
webpack.server.config.js
/*------------------------------------*\
Imports
\*------------------------------------*/
const path = require('path');
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const packageJSON = require('../../package.json');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin');
const NODE_ENV = process.env.NODE_ENV || 'development';
/*------------------------------------*\
Import Base Config
\*------------------------------------*/
var baseConfig = require('./webpack.base.config.js');
/*------------------------------------*\
Server
\*------------------------------------*/
var serverConfig = webpackMerge(baseConfig, {
target: 'node',
externals: Object.keys(packageJSON.dependencies),
entry: path.join(__dirname, '/src/entry.server.js'),
devtool: 'source-map',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'server.bundle.js',
libraryTarget: 'commonjs2',
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"' + NODE_ENV + '"',
VUE_ENV: '"server"',
}
}),
new VueLoaderPlugin(),
new VueSSRServerPlugin(),
],
});
/*------------------------------------*\
Export
\*------------------------------------*/
module.exports = serverConfig;
Issue Analytics
- State:
- Created 5 years ago
- Comments:8 (4 by maintainers)
Top GitHub Comments
a boiled down repository that can showcase the problem with
npm start
or similar is fine, yes 😃Hi @posva thanks for taking time to check this.
I played with it a little bit more today, and it seems that either one of these two things fixes this problem:
output.libraryTarget: 'commonjs2'
only in the server config:https://github.com/vuejs/vue-hackernews-2.0/blob/master/build/webpack.server.config.js#L13
Which I similarly did myself:
https://github.com/DominikSerafin/repro-9207/blob/master/application/config/webpack.server.config.js#L28
But… today while trying to debug this, I also added this to my client config, then this issue also has been fixed (even with saved/commited CRLF endings).
I’m not very familiar with what exactly webpack setting
output.libraryTarget
does though, so I’m not sure if that’s actually problem with Vue, Vue-Loader or something else.