Transition 组件与自定义指令间的“纠葛”
2024-09-09 09:18 阅读(318)

persisted,这个 prop 没有在 Vue 的官方文档中出现,不过我们可以通过源码知道:

图中代码源自 Vue.js 3.2.45 版本


当 persisted 设置为 true 时,表示该过渡不会实际插入或移除 DOM 元素。相反,他只会切换元素的显示和隐藏状态(display: none)。这意味着元素始终存在于 DOM 中,但其可见性会根据状态进行切换。(原文:If true, indicates this is a transition that doesn't actually insert/remove the element, but toggles the show / hidden status instead.)

同时,当 persisted 为 true 的情况下,过渡钩子会被注入,但是会被渲染器跳过。这意味着渲染器中不会执行 Transition 组件相关钩子。(原文:The transition hooks are injected, but will be skipped by the renderer.)

自定义指令可以通过调用注入的 Transition 相关钩子控制过渡效果。(原文:Instead, a custom directive can control the transition by calling the injected hooks (e.g. v-show).)

注意 @before-enter 和 @enter 两个 JavaScript 钩子的执行时机,在第一次加载页面时,这两个钩子是不会执行的。除非传入 appear prop 为 true。

由上图源码可知,在未完成挂载的情况下,appear 为 true 才会执行 onBeforeAppear 或 onBeforeEnter 钩子、onAppear 或 onEnter 钩子。


这里提到了 v-show 指令,编译器中会判断 Transition 的子元素是否使用 v-show 指令,如果使用  v-show 指令,则会注入 persisted: true 到 Transition 的 prop 中:

注释中提到了 v-show 指令,我们再看看 v-show 的源码:

一开始看到注释中的这句话时,并不理解其中的意思:


// Instead, a custom directive can control the transition by calling the injected hooks (e.g. v-show).


直到看到 v-show 的源码时,瞬间明白了,自定义指令也可以像 v-show 一样,通过调用注入的 Transition 相关钩子控制过渡效果。如下面的自定义指令的例子:


<script src="../../../dist/vue.global.js"></script>

<style>
.btn-container {
  display: inline-block;
  position: relative;
  height: 1em;
}
</style>

<div id="demo">
  <div class="btn-container">
    <button @click="show = !show">Toggle</button>
    <transition
      persisted
      @before-enter="onBeforeEnter"
      @enter="onEnter"
      @leave="onLeave"
    >
      <div v-my-transition="show">This is a custom transition example.</div>
    </transition>    
  </div>
</div>

<script>
const { createApp, ref } = Vue

createApp({
  setup() {
    const show = ref(true)
    const onBeforeEnter = (el) => {
      el.style.opacity = 0; // 初始透明度为0
    }
    const onEnter = (el, done) => {
      el.offsetHeight; // 强制重绘
      el.style.transition = 'opacity 0.5s'; // 设置过渡效果
      el.style.opacity = 1; // 进入时透明度变为1
      done(); // 完成过渡
    }
    const onLeave = (el, done) => {
      el.style.transition = 'opacity 0.5s'; // 设置过渡效果
      el.style.opacity = 0; // 离开时透明度变为0
      setTimeout(done, 500); // 等待过渡完成后调用done
    }
    return {
      show,
      onBeforeEnter,
      onEnter,
      onLeave
    }
  },
  directives: {
    'my-transition': {
      beforeMount(el, { value }, { transition }) {
        if (transition && value) {
          transition.beforeEnter(el)
        }
      },
      mounted(el, { value }, { transition }) {
        if (transition && value) {
          transition.enter(el)
        }
      },
      updated(el, { value, oldValue }, { transition }) {
        if (!value === !oldValue) return
        if (transition) {
          if (value) {
            transition.beforeEnter(el)
            transition.enter(el)
          } else {
            transition.leave(el, () => {
              console.log('leave done')
            })
          }
        }
      }
    }
  }
}).mount('#demo')
</script>

上面的代码自定义了一个指令 my-transition ,该自定义指令通过调用注入的 transition 钩子,实现了动画过渡效果。

上面的代码传入了 persisted 为 true 的 prop ,好处是传入了 persisted 为 true 的 prop 后,渲染器会跳过 transition 钩子,避免了重复执行 transition 钩子。

总结

persisted,这个 prop 没有在 Vue 的官方文档中出现,有关 persisted 的含义与 Transition 组件结合自定义组件的用法也是通过阅读源码学习到的。

这也是阅读源码的好处之一吧,可以学习到官方文档外的相关用法,拓展自己的知识。

至于为啥 Vue 官方文档 为啥没有提到 persisted ,也没有说到自定义指令可以结合 Transition 组件一起使用自定义过渡效果,估计是考虑到这种用法在日常开发中用得不多吧。

但是对于前端开发工程师来说,通过阅读源码学习一些拓展的知识,对自身没有坏处。


作者:云浪

链接:https://juejin.cn

返回顶部