Using CSS-transitions I have a component that has animations for whenever the value changes.
When I run this with npm run dev
, the whole thing works just fine looking like this:
However, when using npm run build
and putting the code into production on an Nginx server, the animation does not work and the element is just removed, very quickly:
What causes this difference between dev and prod? Is there any better way of getting this animation effect?
BugRepro.vue, which I am using to reproduce this bug:
<template>
<div class="bugrepro">
<div class="empty-space"></div>
<Value :value="someNumber"></Value>
<button @click="change()">Change it!</button>
</div>
</template>
<script>
import Value from "./Value"
export default {
name: "BugRepro",
data() {
return {
someNumber: 20
};
},
components :{
Value
},
methods: {
change() {
this.someNumber += 1;
}
}
};
</script>
<style>
.empty-space {
margin-top: 100px;
}
</style>
Value.vue, which is holding the number and in charge of handling the animations when the value changes:
<template>
<div class="card-property">
<button class="btn btn-sm active fa" :class="`btn-info fa-tint`" style="cursor:default" title="Mana Cost">
{{ value }}
</button>
<transition-group name="diff-once" class="pos-absolute" tag="div" @after-enter="removeDiff()">
<button v-for="diff in diffs" :key="diff.id"
class="btn btn-sm active fa diff-once" :class="`btn-info fa-tint`" style="cursor:default">
{{diff.value}}
</button>
</transition-group>
</div>
</template>
<script>
export default {
name: "Value",
props: ["value"],
data() {
return {
diffs: [],
diffId: 0
};
},
methods: {
removeDiff() {
this.diffs.splice(0, 1);
}
},
watch: {
value: function(newValue, oldValue) {
console.log(typeof newValue);
let diff = newValue - oldValue;
this.diffs.push({value: diff, id: this.diffId});
this.diffId++;
}
}
}
</script>
<style>
.card-property {
position: relative;
}
.diff-once-leave-active {
transition: all 1s;
position: absolute;
}
.pos-absolute {
position: absolute;
top: 0;
left: 0;
}
.diff-once-leave {
opacity: 1;
}
.diff-once-leave-to {
opacity: 0;
transform: translateY(-30px);
}
</style>
I have considered the problem might be in my webpack.prod.conf, I have already tried disabling the OptimizeCSSPlugin
which did not help:
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const env = process.env.NODE_ENV === 'testing'
? require('../config/test.env')
: require('../config/prod.env')
const webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: true,
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: process.env.NODE_ENV === 'testing'
? 'index.html'
: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}),
// keep module.id stable when vendor modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks (module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig
As I do not know which files are relevant, you can also find all files on github
Easiest steps to reproduce:
npm install
and npm run dev
npm run build
Once I know which files are the most relevant, I will of course add them to this post.
The problem is that once you compile into production, all the CSS files from Bootstrap and your components get combined into one, and are run through some CSS optimisations. This causes your CSS styles to define transition at multiple points. There is your .diff-once-...
styles, and there are your .btn
styles. In DEV mode, .diff-once-...
is higher in the cascade, in production, it's .btn
. So the transition rule in button overrides all the rules you define later. You can solve this by either raising specificity on your animation classes like
.btn.diff-once-leave-to { ... }
.btn.diff-once-leave { ... }
Or (recommended) by adding scoped styles. This way all the animation styles get a higher specificity by default and don't interfere with any other styles in your app:
<style scoped>
.card-property {
position: relative;
}
.diff-once-leave-active {
transition: all 1s;
position: absolute;
}
.pos-absolute {
position: absolute;
top: 0;
left: 0;
}
.diff-once-leave {
opacity: 1;
}
.diff-once-leave-to {
opacity: 0;
transform: translateY(-30px);
}
</style>
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With