为什么带有 v-if 的元素内部的过渡没有进入过渡但离开有效?

Why is transition inside an element with v-if not entering the transition but leaving works?

标题几乎说明了一切。我在 Whosebug 上搜索了解决方案,但 他们提供了帮助。

使用经典模态组件的示例:

Vue.component('modal', {
  template: `
  <transition name="modal">
    <div class="modal-wrapper">
      <div class="modal-dialog">
        <slot></slot>
      </div>
    </div>
  </transition>
  `,
})

const app = new Vue({
  el: '#app',
  data: {
    showModal: false,
  },
})
/* transition */
.modal-enter-active,
.modal-leave-active {
  transition: opacity .5s;
}

.modal-enter,
.modal-leave-to {
  opacity: 0;
}

.modal-wrapper {
  position: absolute;
  left: 0; right: 0;
  top: 0; bottom: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: rgba(0, 0, 0, .25);
}

.modal-dialog {
  max-width: 90%;
  padding: 1em;
  background: white;
}
<script src="https://vuejs.org/js/vue.js"></script>
<div id="app">
  <p><button @click="showModal = true">Show modal</button></p>
  <modal v-if="showModal">
    <h3>Hello world</h3>
    <p>Amet quam alias amet incidunt voluptatum sapiente Mollitia</p>
    <p><button @click="showModal = false">Close</button></p>
  </modal>
</div>

(控制台也没有报错)

然而,使用v-show时一切正常。但是我不能在我的项目中真正使用它来代替 v-if。

Vue.component('modal', {
  template: `
  <transition name="modal">
    <div class="modal-wrapper">
      <div class="modal-dialog">
        <slot></slot>
      </div>
    </div>
  </transition>
  `,
})

const app = new Vue({
  el: '#app',
  data: {
    showModal: false,
  },
})
/* transition */
.modal-enter-active,
.modal-leave-active {
  transition: opacity .5s;
}

.modal-enter,
.modal-leave-to {
  opacity: 0;
}

.modal-wrapper {
  position: absolute;
  left: 0; right: 0;
  top: 0; bottom: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: rgba(0, 0, 0, .25);
}

.modal-dialog {
  max-width: 90%;
  padding: 1em;
  background: white;
}
<script src="https://vuejs.org/js/vue.js"></script>
<div id="app">
  <p><button @click="showModal = true">Show modal</button></p>
  <modal v-show="showModal">
    <h3>Hello world</h3>
    <p>Amet quam alias amet incidunt voluptatum sapiente Mollitia</p>
    <p><button @click="showModal = false">Close</button></p>
  </modal>
</div>

就是说,我必须在 使用 的任何地方用 <transition> 包裹 <modal> 并从模态本身移除过渡(这不会听起来不错)

<transition name="modal">
  <modal>
  ...
  </modal>
</transition>

为什么会这样以及如何使进入动画工作(使用 v-if<transition> 模态组件中?

我注意到 Vue 2.5(而不是 Vue 2.6)没有这样的问题。从那时起肯定发生了一些变化。

那是因为 v-if inserts/destroys 元素和 v-show 隐藏了它们(不从 DOM 中删除它们)。在您的示例中,如果 v-if 是设置 on/above 过渡元素,则 transition 不存在。如果您将 v-if 移动到转换下方,那么它将起作用。

Vue.component('modal', {
  props: ['showModal'],
  template: `
  <transition name="modal">
    <div class="modal-wrapper" v-if="showModal">
      <div class="modal-dialog">
        <slot></slot>
      </div>
    </div>
  </transition>
  `,
})

const app = new Vue({
  el: '#app',
  data: {
    showModal: false,
  },
})
/* transition */
.modal-enter-active,
.modal-leave-active {
  transition: opacity .5s;
}

.modal-enter,
.modal-leave-to {
  opacity: 0;
}

.modal-wrapper {
  position: absolute;
  left: 0; right: 0;
  top: 0; bottom: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: rgba(0, 0, 0, .25);
}

.modal-dialog {
  max-width: 90%;
  padding: 1em;
  background: white;
}
<script src="https://vuejs.org/js/vue.js"></script>
<div id="app">
  <p><button @click="showModal = true">Show modal</button></p>
  <modal :show-modal="showModal">
    <h3>Hello world</h3>
    <p>Amet quam alias amet incidunt voluptatum sapiente Mollitia</p>
    <p><button @click="showModal = false">Close</button></p>
  </modal>
</div>

您缺少 appear 属性。

默认情况下,第一次插入元素时,Vue 不会设置动画。根据文档:

If you also want to apply a transition on the initial render of a node, you can add the appear attribute:

<transition appear>
  <!-- ... -->
</transition>

By default, this will use the transitions specified for entering and leaving. If you’d like however, you can also specify custom CSS classes:

<transition
  appear
  appear-class="custom-appear-class"
  appear-to-class="custom-appear-to-class"
  appear-active-class="custom-appear-active-class"
>
<!-- ... -->
</transition>

因此只需添加 appear 即可解决问题。

这应该可以解决问题:

{
  template: `
  <transition name="modal">
    <div v-if="show">...</div>
  </transition>
  `,
  data() {return {
    show: false,
  }},
  mounted() {
    this.show = true
  },
}

我认为这种方法(我相信它确实如此)将动画延迟 1 个 Vue tick,并且大量嵌套此类动画元素会明显减慢速度。

Vue.component('modal', {
  template: `
  <transition name="modal">
    <div class="modal-wrapper" v-if="show">
      <div class="modal-dialog">
        <slot></slot>
      </div>
    </div>
  </transition>
  `,
  data() {return {
    show: false,
  }},
  mounted() {
    this.show = true
  }
})

const app = new Vue({
  el: '#app',
  data: {
    showModal: false,
  },
})
/* transition */
.modal-enter-active,
.modal-leave-active {
  transition: opacity .5s;
}

.modal-enter,
.modal-leave-to {
  opacity: 0;
}

.modal-wrapper {
  position: absolute;
  left: 0; right: 0;
  top: 0; bottom: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: rgba(0, 0, 0, .25);
}

.modal-dialog {
  max-width: 90%;
  padding: 1em;
  background: white;
}
<script src="https://vuejs.org/js/vue.js"></script>
<div id="app">
  <p><button @click="showModal = true">Show modal</button></p>
  <modal v-if="showModal">
    <h3>Hello world</h3>
    <p>Amet quam alias amet incidunt voluptatum sapiente Mollitia</p>
    <p><button @click="showModal = false">Close</button></p>
  </modal>
</div>