删除 Vue JS 中的元素时在 dev 中工作但在 prod 中不工作时的动画

Posted

技术标签:

【中文标题】删除 Vue JS 中的元素时在 dev 中工作但在 prod 中不工作时的动画【英文标题】:Animation when removing element in Vue JS working in dev but not in prod 【发布时间】:2018-11-22 07:19:39 【问题描述】:

使用 CSS-transitions,我有一个组件,该组件在值发生变化时具有动画。

当我使用npm run dev 运行此程序时,整个过程运行良好,如下所示:

但是,当使用 npm run build 并将代码投入 nginx 服务器上的生产时,动画不起作用并且元素很快被移除:

是什么导致了 dev 和 prod 之间的这种差异?有没有更好的方法来获得这个动画效果?

BugRepro.vue,我用它来重现这个错误:

<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,它保存数字并负责在值变化时处理动画:

<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>

我认为问题可能出在我的 webpack.prod.conf 中,我已经尝试禁用 OptimizeCSSPlugin,但没有帮助:

'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

由于我不知道哪些文件是相关的,you can also find all files on github

最简单的重现步骤:

检查源代码,运行npm installnpm run dev 转到http://localhost:42637 并单击按钮 - 动画正常工作 停止开发服务器并运行npm run build 在某些 HTTP 服务器上托管文件,例如 Nginx 转到托管文件的 HTTP 服务器并单击按钮 - 动画不工作

一旦我知道哪些文件最相关,我当然会将它们添加到这篇文章中。

【问题讨论】:

【参考方案1】:

问题在于,一旦您编译到生产环境中,来自 Bootstrap 的所有 CSS 文件和您的组件都会合并为一个,并通过一些 CSS 优化运行。这会导致您的 CSS 样式在多个点定义过渡。有你的.diff-once-... 样式,还有你的.btn 样式。在 DEV 模式下,.diff-once-... 在级联中更高,在生产中,它是.btn。因此按钮中的转换规则会覆盖您稍后定义的所有规则。您可以通过提高动画类的特异性来解决这个问题,例如

.btn.diff-once-leave-to  ... 
.btn.diff-once-leave  ... 

或者(推荐)通过添加作用域样式。这样,所有动画样式默认情况下都会获得更高的特异性,并且不会干扰您应用中的任何其他样式:

<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>

希望这会有所帮助!

【讨论】:

+1 很棒的收获。然后我又经历了CSS Specificty。重要的东西值得一读再读。

以上是关于删除 Vue JS 中的元素时在 dev 中工作但在 prod 中不工作时的动画的主要内容,如果未能解决你的问题,请参考以下文章

NuxtJS:在开发中工作但在生产中工作的路由(netlify)

请求在CURL中工作但不在Ajax中工作

查询在 PHPMyAdmin 中工作但在 PHP 中不工作

ApnsPHP:在开发中工作但不在生产中的推送通知

Android Geolocation在模拟器中工作但不在手机中工作

Jquery 对象 innerHTML 在 Firefox 中工作但在 IE 中不工作?