文件路径:node_modules\vue\src\platforms\web\runtime\components\transition.js
/* @flow */// Provides transition support for a single element/component.// supports transition mode (out-in / in-out)import { warn } from 'core/util/index'import { camelize, extend, isPrimitive } from 'shared/util'import {mergeVNodeHook,isAsyncPlaceholder,getFirstComponentChild} from 'core/vdom/helpers/index'export const transitionProps = {name: String,appear: Boolean,css: Boolean,mode: String,type: String,enterClass: String,leaveClass: String,enterToClass: String,leaveToClass: String,enterActiveClass: String,leaveActiveClass: String,appearClass: String,appearActiveClass: String,appearToClass: String,duration: [Number, String, Object]}// in case the child is also an abstract component, e.g. <keep-alive>// we want to recursively retrieve the real component to be renderedfunction getRealChild (vnode: ?VNode): ?VNode {const compOptions: ?VNodeComponentOptions = vnode && vnode.componentOptionsif (compOptions && compOptions.Ctor.options.abstract) {return getRealChild(getFirstComponentChild(compOptions.children))} else {return vnode}}export function extractTransitionData (comp: Component): Object {const data = {}const options: ComponentOptions = comp.$options// propsfor (const key in options.propsData) {data[key] = comp[key]}// events.// extract listeners and pass them directly to the transition methodsconst listeners: ?Object = options._parentListenersfor (const key in listeners) {data[camelize(key)] = listeners[key]}return data}function placeholder (h: Function, rawChild: VNode): ?VNode {if (/\d-keep-alive$/.test(rawChild.tag)) {return h('keep-alive', {props: rawChild.componentOptions.propsData})}}function hasParentTransition (vnode: VNode): ?boolean {while ((vnode = vnode.parent)) {if (vnode.data.transition) {return true}}}function isSameChild (child: VNode, oldChild: VNode): boolean {return oldChild.key === child.key && oldChild.tag === child.tag}const isNotTextNode = (c: VNode) => c.tag || isAsyncPlaceholder(c)const isVShowDirective = d => d.name === 'show'export default {name: 'transition',props: transitionProps,abstract: true,render (h: Function) {let children: any = this.$slots.defaultif (!children) {return}// filter out text nodes (possible whitespaces)children = children.filter(isNotTextNode)/* istanbul ignore if */if (!children.length) {return}// warn multiple elementsif (process.env.NODE_ENV !== 'production' && children.length > 1) {warn('<transition> can only be used on a single element. Use ' +'<transition-group> for lists.',this.$parent)}const mode: string = this.mode// warn invalid modeif (process.env.NODE_ENV !== 'production' &&mode && mode !== 'in-out' && mode !== 'out-in') {warn('invalid <transition> mode: ' + mode,this.$parent)}const rawChild: VNode = children[0]// if this is a component root node and the component's// parent container node also has transition, skip.if (hasParentTransition(this.$vnode)) {return rawChild}// apply transition data to child// use getRealChild() to ignore abstract components e.g. keep-aliveconst child: ?VNode = getRealChild(rawChild)/* istanbul ignore if */if (!child) {return rawChild}if (this._leaving) {return placeholder(h, rawChild)}// ensure a key that is unique to the vnode type and to this transition// component instance. This key will be used to remove pending leaving nodes// during entering.const id: string = `__transition-${this._uid}-`child.key = child.key == null? child.isComment? id + 'comment': id + child.tag: isPrimitive(child.key)? (String(child.key).indexOf(id) === 0 ? child.key : id + child.key): child.keyconst data: Object = (child.data || (child.data = {})).transition = extractTransitionData(this)const oldRawChild: VNode = this._vnodeconst oldChild: VNode = getRealChild(oldRawChild)// mark v-show// so that the transition module can hand over the control to the directiveif (child.data.directives && child.data.directives.some(isVShowDirective)) {child.data.show = true}if (oldChild &&oldChild.data &&!isSameChild(child, oldChild) &&!isAsyncPlaceholder(oldChild) &&// #6687 component root is a comment node!(oldChild.componentInstance && oldChild.componentInstance._vnode.isComment)) {// replace old child transition data with fresh one// important for dynamic transitions!const oldData: Object = oldChild.data.transition = extend({}, data)// handle transition modeif (mode === 'out-in') {// return placeholder node and queue update when leave finishesthis._leaving = truemergeVNodeHook(oldData, 'afterLeave', () => {this._leaving = falsethis.$forceUpdate()})return placeholder(h, rawChild)} else if (mode === 'in-out') {if (isAsyncPlaceholder(child)) {return oldRawChild}let delayedLeaveconst performLeave = () => { delayedLeave() }mergeVNodeHook(data, 'afterEnter', performLeave)mergeVNodeHook(data, 'enterCancelled', performLeave)mergeVNodeHook(oldData, 'delayLeave', leave => { delayedLeave = leave })}}return rawChild}}
