VueJS 仅 JS 转换挂钩需要 setTimeout 才能使 CSS 转换正常工作

VueJS JS-only transition hook requires setTimeout for CSS transition to work

我正在为 VueJS 上的 <transition-group> 元素使用纯 JS 钩子,我对 enter 钩子的实际工作方式感到非常困惑。根据文档,我知道我必须 call done() to avoid events being called synchronously:

When using JavaScript-only transitions, the done callbacks are required for the enter and leave hooks. Otherwise, the hooks will be called synchronously and the transition will finish immediately.

然而,即使我使用它,它似乎也阻止了 CSS 转换在 进入转换 中发生。我找到的唯一解决方案是使用 window.setTimeout 设置样式,我认为这是一种代码味道。这是没有超时的代码和有超时的代码之间的快速视觉比较(有超时的是预期的效果):

损坏的输入过渡(左填充和不透明度没有过渡):

所需的进入转换:

在下面的示例中,我使用 <transition-group> 显示一个列表,并希望使用 JS-hooks 以便我可以在单个列表项上创建交错填充。它似乎可以工作,但在 enter 过渡中,填充 属性 上的 CSS 过渡不起作用。

new Vue({
  el: '#app',
  data: {
    items: [
      'Lorem',
      'Ipsum',
      'Dolor',
      'Sit',
      'Amet'
    ],
    toggle: false
  },
  computed: {
    filteredItems: function() {
      if (!this.toggle)
        return [];

      return this.items;
    }
  },
  methods: {
    toggleItems: function() {
      this.toggle = !this.toggle;
    },
    beforeEnter: function(el) {
      el.style.paddingLeft = '0px';
      el.style.opacity = '0';
    },
    enter: function(el, done) {
      el.style.paddingLeft = `${10 * +el.dataset.index}px`;
      el.style.opacity = '1';
      done();
    },
    beforeLeave: function(el) {
      el.style.paddingLeft = '0px';
      el.style.opacity = '0';
    }
  }
})
ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

ul li {
  transition: all 500ms ease-in-out;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">

  <button @click="toggleItems">
    Toggle items
  </button>
  
  <transition-group
    tag="ul"
    @before-enter="beforeEnter"
    @enter="enter"
    @before-leave="beforeLeave">
    
    <li
      v-for="(item, i) in filteredItems"
      v-bind:key="i"
      v-bind:data-index="i">
      {{ item }}
    </li>
  </transition-group>
</div>

如果将 enter 方法中的所有逻辑包装在任意超时内,那么它会起作用:

enter: function(el, done) {
  window.setTimeout(() => {
    el.style.paddingLeft = `${10 * +el.dataset.index}px`;
    el.style.opacity = '1';
    done();
  }, 100);
},

这就是我有点困惑的地方:enter 挂钩不会先等待 beforeEnter 完成吗?工作片段如下

@enter 挂钩更改为 @after-enter 应该可以解决这个问题

我不知道为什么 @enter 挂钩对此不起作用,因为查看文档它 应该 但这至少应该摆脱超时而不是黑客

new Vue({
  el: '#app',
  data: {
    items: [
      'Lorem',
      'Ipsum',
      'Dolor',
      'Sit',
      'Amet'
    ],
    toggle: false
  },
  computed: {
    filteredItems: function() {
      if (!this.toggle)
        return [];

      return this.items;
    }
  },
  methods: {
    toggleItems: function() {
      this.toggle = !this.toggle;
    },
    beforeEnter: function(el) {
      el.style.paddingLeft = '0px';
      el.style.opacity = '0';
    },
    afterEnter: function(el) {
      el.style.paddingLeft = `${10 * +el.dataset.index}px`;
      el.style.opacity = '1';
    },
    beforeLeave: function(el) {
      el.style.paddingLeft = '0px';
      el.style.opacity = '0';
    }
  }
})
ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

ul li {
  transition: all 500ms ease-in-out;
}

li.v-enter-active {
  transition: none
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">

  <button @click="toggleItems">
    Toggle items
  </button>

  <transition-group 
    tag="ul" 
    @before-enter="beforeEnter"
    @after-enter="afterEnter" 
    @before-leave="beforeLeave">
    <li v-for="(item, i) in filteredItems" v-bind:key="i" v-bind:data-index="i">
      {{ item }}
    </li>
  </transition-group>
</div>

附带说明一下,如果您使用的是 SCSS 或 SASS,则可以使用 SCSS 而不是 JavaScript