vue-transition原理分析(一)

5,467 阅读2分钟

对于transition这个单词,大家应该不陌生,在css3中transition属性被用于描述css属性的过渡效果。在vue中,使用了同名的transition组件,其目的也显而易见了,是为了提供易于使用的组件级别的过渡效果。

一、transition组件使用示例

​ 先看一下transition的简单使用示例

<template>
  <div id="app">
    <button v-on:click="show = !show">Toggle</button>
    <transition name="fade">
      <p v-if="show">hello</p>
    </transition>
  </div>
</template>
<script>
export default {
  name: "App",
  data() {
    return {
      show: true
    };
  }
};
</script>
<style>
#app {
  text-align: center;
  margin-top: 60px;
}
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to {
  opacity: 0;
}
</style>

​ 效果展示:

​ 可以看到在点击Toggle后,hello的出现和消失具有动画效果。

​ 我们来看一下DOM

​ DOM中不存在transition组件对应的元素节点,这一点在vue的官方文档上有说明:<transition>只会把过渡效果应用到其包裹的内容上,而不会额外渲染 DOM 元素,也不会出现在可被检查的组件层级中

二、transition组件源码

​ 需要说明的是transition的源码并不在src/core目录下,而是在src/platforms目录下,这说明web和weex平台对于transition的实现并不一致,这里我们只关注web平台下的实现。

// vue源码\src\platforms\web\runtime\components\transition.js
// 为了便于理解,只保留了部分代码

export default {
  name: 'transition',
  props: transitionProps,
  abstract: true,

  render (h: Function) {
    let children: any = this.$slots.default
    if (!children) {
      return
    }

    // filter out text nodes (possible whitespaces)
    children = children.filter(isNotTextNode)
    if (!children.length) {
      return
    }

    const rawChild: VNode = children[0]

    // apply transition data to child
    // use getRealChild() to ignore abstract components e.g. keep-alive
    const child: ?VNode = getRealChild(rawChild)
    if (!child) {
      return rawChild
    }
    // 核心代码
    const data: Object = (child.data || (child.data = {})).transition = extractTransitionData(this)

    return rawChild
  }
}

注意:上面的代码中,只保留了目前我们需要的核心源码。

​ 在代码中abstract:true表示transition是一个抽象组件,并且其render函数中返回的是child节点,因此transition组件本身并不会在DOM上有任何渲染。

​ 在上面代码中核心是第30行,其给child节点的data上添加了一个transition属性,我们看一下extractTransitionData的代码:

// vue源码\src\platforms\web\runtime\components\transition.js

export function extractTransitionData (comp: Component): Object {
  const data = {}
  const options: ComponentOptions = comp.$options
  // props
  for (const key in options.propsData) {
    data[key] = comp[key]
  }
  // events.
  // extract listeners and pass them directly to the transition methods
  const listeners: ?Object = options._parentListeners
  for (const key in listeners) {
    data[camelize(key)] = listeners[key]
  }
  return data
}

​ extractTransitionData的代码很简单,就是将绑定在transition组件上的属性和事件提取出来,组合成一个数据对象。

三、总结

​ 总结来说,transition是vue内置的一个抽象组件,在transition组件渲染时会将绑定在transition自身的属性和事件组合成一个对象,并将该对象绑定在transition子组件的data.transition属性上。

​ 那么可以猜测的是:子组件渲染时会根据其data.transition属性添加对应的过渡class及触发相应事件,从而达到示例中的效果。那么这个过程具体是如何进行的,我们下一篇文章再探讨吧。