1.模板中transition标签会依据内建的transition组件选项创立组件实例。组件渲染成页面时调用render函数,获取transition标签内的标签对应的节点(插槽),并将transition组件标签上的数据合并到该节点VNode上。

如果先人(包含以后transition标签)都是根标签,且并存在它是transition标签内的标签(插槽)时,间接跳过以后的transition。
mode值为out-in(以后元素先进行过渡,实现之后新元素过渡进入),当transition标签内组件切换时,间接返回空文本节点,来替换旧组件节点,旧节点移除时实现leave动画后,强制更新transition组件,新组件开始插入,并开始enter动画。
mode值为in-out(新元素先进行过渡,实现之后以后元素过渡来到),当transition标签内组件切换时,为旧组件节点增加delayLeave钩子(钩子用来开始节点的leave动画),当新节点实现enter动画后,再执行旧节点的performLeave,开始旧节点的leave动画,最初从document中移除旧节点。
// 内建的transition组件选项var 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]};/* transition组件选项 */var Transition = {  name: 'transition',  props: transitionProps,  abstract: true,  render: function render (h) {// 获取Vnode节点,h:createElement    var this$1 = this;    var children = this.$slots.default;    if (!children) {      return    }    // filter out text nodes (possible whitespaces)    children = children.filter(isNotTextNode);// 过滤文本节点    /* istanbul ignore if */    if (!children.length) {      return    }    // warn multiple elements    if (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      );    }    var mode = this.mode;    // warn invalid mode    if (process.env.NODE_ENV !== 'production' &&      mode && mode !== 'in-out' && mode !== 'out-in'    ) {      warn(        'invalid <transition> mode: ' + mode,        this.$parent      );    }    var rawChild = children[0];    // if this is a component root node and the component's    // parent container node also has transition, skip.    // 如果先人都是根标签,且正好是transition组件标签内的标签(插槽)时,间接跳过以后的transition    if (hasParentTransition(this.$vnode)) {      return rawChild    }    // apply transition data to child    // use getRealChild() to ignore abstract components e.g. keep-alive    var child = 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.    var id = "__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.key;    var data = (child.data || (child.data = {})).transition = extractTransitionData(this);// 将transition标签上的数据增加到子标签节点data.transition上    var oldRawChild = this._vnode;    var oldChild = getRealChild(oldRawChild);    // mark v-show    // so that the transition module can hand over the control to the directive    if (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!      var oldData = oldChild.data.transition = extend({}, data);      // handle transition mode      if (mode === 'out-in') {// 以后元素先进行过渡,实现之后新元素过渡进入        // return placeholder node and queue update when leave finishes        this._leaving = true;        mergeVNodeHook(oldData, 'afterLeave', function () {// 增加afterLeave回调函数,旧节点动画实现后,再更新组件,从新渲染          this$1._leaving = false;          this$1.$forceUpdate();        });        return placeholder(h, rawChild)// 移除旧节点,替换为空的文本节点      } else if (mode === 'in-out') {// 新元素先进行过渡,实现之后以后元素过渡来到        if (isAsyncPlaceholder(child)) {          return oldRawChild        }        var delayedLeave;        var performLeave = function () { delayedLeave(); };        mergeVNodeHook(data, 'afterEnter', performLeave);// 在enter动画实现之后开始调用performLeave,开始leave动画        mergeVNodeHook(data, 'enterCancelled', performLeave);// 在enter动画过程中勾销后开始调用performLeave,开始leave动画        mergeVNodeHook(oldData, 'delayLeave', function (leave) { delayedLeave = leave; });// 更改旧节点的leave动画的执行机会,提早从document移除的工夫      }    }    return rawChild  }};

2.transition的create、activete、reomve办法将会增加到节点钩子函数汇合中,将在节点patch过程中的不同周期调用,在创立dom后、插入到document之前调用_enter办法,在dom从document移除之前调用leave办法。

var transition = inBrowser ? {  create: _enter,// 创立dom后调用  activate: _enter,// 创立组件后,且是transition组件根标签节点时  remove: function remove$$1 (vnode, rm) {// 从文档中移除前调用    /* istanbul ignore else */    if (vnode.data.show !== true) {      leave(vnode, rm);    } else {      rm();// 从文档中移除    }  }} : {};

3.在enter办法中,创立dom、插入到document之前,增加enter和enterActive类名、调用对应的钩子。插入到文档后移除enter类名,增加enterTo类名。动画实现后,移除enterTo和enterActive类名。

function enter (vnode, toggleDisplay) {  var el = vnode.elm;  ...  var css = data.css;// 是否通过css实现过渡  var type = data.type;// 判断是transition还是animation  var enterClass = data.enterClass;  var enterToClass = data.enterToClass;  var enterActiveClass = data.enterActiveClass;  var appearClass = data.appearClass;  var appearToClass = data.appearToClass;  var appearActiveClass = data.appearActiveClass;  var beforeEnter = data.beforeEnter;// 钩子函数  var enter = data.enter;// 钩子函数  var afterEnter = data.afterEnter;// 钩子函数  var enterCancelled = data.enterCancelled;// 钩子函数  var beforeAppear = data.beforeAppear;// 钩子函数  var appear = data.appear;// 钩子函数(节点在初始渲染的过渡)  var afterAppear = data.afterAppear;// 钩子函数  var appearCancelled = data.appearCancelled;// 钩子函数  var duration = data.duration;// 进入的持续时间  // activeInstance will always be the <transition> component managing this  // transition. One edge case to check is when the <transition> is placed  // as the root node of a child component. In that case we need to check  // <transition>'s parent for appear check.  var context = activeInstance;  var transitionNode = activeInstance.$vnode;  while (transitionNode && transitionNode.parent) {// 向上查找不是组件根标签的先人节点    context = transitionNode.context;    transitionNode = transitionNode.parent;  }  var isAppear = !context._isMounted || !vnode.isRootInsert;  if (isAppear && !appear && appear !== '') {    return  }// 类名  var startClass = isAppear && appearClass    ? appearClass    : enterClass;  var activeClass = isAppear && appearActiveClass    ? appearActiveClass    : enterActiveClass;  var toClass = isAppear && appearToClass    ? appearToClass    : enterToClass;// 钩子函数  var beforeEnterHook = isAppear    ? (beforeAppear || beforeEnter)    : beforeEnter;  var enterHook = isAppear    ? (typeof appear === 'function' ? appear : enter)    : enter;  var afterEnterHook = isAppear    ? (afterAppear || afterEnter)    : afterEnter;  var enterCancelledHook = isAppear    ? (appearCancelled || enterCancelled)    : enterCancelled;  var explicitEnterDuration = toNumber(    isObject(duration)      ? duration.enter      : duration  );  if (process.env.NODE_ENV !== 'production' && explicitEnterDuration != null) {    checkDuration(explicitEnterDuration, 'enter', vnode);  }  var expectsCSS = css !== false && !isIE9;  var userWantsControl = getHookArgumentsLength(enterHook);  var cb = el._enterCb = once(function () {// 动画完结后的回调,第一次调用后生效    if (expectsCSS) {      removeTransitionClass(el, toClass);// 移除toClass类名      removeTransitionClass(el, activeClass);// 移除active类名    }    if (cb.cancelled) {      if (expectsCSS) {        removeTransitionClass(el, startClass);      }      enterCancelledHook && enterCancelledHook(el);// 调用enter过程勾销的钩子    } else {      afterEnterHook && afterEnterHook(el);// 调用afterEnter钩子    }    el._enterCb = null;  });  if (!vnode.data.show) {    // remove pending leave element on enter by injecting an insert hook    mergeVNodeHook(vnode, 'insert', function () {      var parent = el.parentNode;      var pendingNode = parent && parent._pending && parent._pending[vnode.key];      if (pendingNode &&        pendingNode.tag === vnode.tag &&        pendingNode.elm._leaveCb      ) {        pendingNode.elm._leaveCb();      }      enterHook && enterHook(el, cb);    });  }  // start enter transition  beforeEnterHook && beforeEnterHook(el);// 在插入文档之前调用beforeEnter钩子  if (expectsCSS) {    addTransitionClass(el, startClass);// 增加enter类名,在插入到文档后移除    addTransitionClass(el, activeClass);// 增加active类名在    nextFrame(function () {// 下一帧      removeTransitionClass(el, startClass);// 移除enter类名      if (!cb.cancelled) {// 当进入leave状态,cb将会被勾销        addTransitionClass(el, toClass);// 插入到文档后增加enterTo类名        if (!userWantsControl) {          if (isValidDuration(explicitEnterDuration)) {            setTimeout(cb, explicitEnterDuration);          } else {            whenTransitionEnds(el, type, cb);// 动画完结后调用cb          }        }      }    });  }  ...}

4.leave办法中,dom从document移除之前,增加leave和leaveActive类名,以及对应的钩子。在下一帧移除leave类名,增加leaveTo类名。动画实现后移除leaveTo和leaveActive类名,而后dom从document中移除。

function leave (vnode, rm) {  var el = vnode.elm;  // call enter callback now  if (isDef(el._enterCb)) {// 终止enter状态,针对afterEnterHook钩子还没调用的状况,将会触发enterCancelledHook钩子函数    el._enterCb.cancelled = true;    el._enterCb();  }  var data = resolveTransition(vnode.data.transition);  if (isUndef(data) || el.nodeType !== 1) {    return rm()  }  /* istanbul ignore if */  if (isDef(el._leaveCb)) {    return  }  var css = data.css;  var type = data.type;  var leaveClass = data.leaveClass;  var leaveToClass = data.leaveToClass;  var leaveActiveClass = data.leaveActiveClass;  var beforeLeave = data.beforeLeave;  var leave = data.leave;  var afterLeave = data.afterLeave;  var leaveCancelled = data.leaveCancelled;  var delayLeave = data.delayLeave;// mode为in-out时存在  var duration = data.duration;  var expectsCSS = css !== false && !isIE9;  var userWantsControl = getHookArgumentsLength(leave);  var explicitLeaveDuration = toNumber(    isObject(duration)      ? duration.leave      : duration  );  if (process.env.NODE_ENV !== 'production' && isDef(explicitLeaveDuration)) {    checkDuration(explicitLeaveDuration, 'leave', vnode);  }  var cb = el._leaveCb = once(function () {    if (el.parentNode && el.parentNode._pending) {      el.parentNode._pending[vnode.key] = null;    }    if (expectsCSS) {      removeTransitionClass(el, leaveToClass);      removeTransitionClass(el, leaveActiveClass);    }    if (cb.cancelled) {      if (expectsCSS) {        removeTransitionClass(el, leaveClass);      }      leaveCancelled && leaveCancelled(el);    } else {      rm();// 移除DOM元素,会在本次宏工作执行完开始从新渲染      afterLeave && afterLeave(el);    }    el._leaveCb = null;      });  if (delayLeave) {    delayLeave(performLeave);// 提早执行performLeave(mode为in-out时,在新节点enter动画实现时执行performLeave,开始leave动画)  } else {    performLeave();  }  function performLeave () {    // the delayed leave may have already been cancelled    if (cb.cancelled) {// 被勾销了      return    }    // record leaving element    if (!vnode.data.show && el.parentNode) {      (el.parentNode._pending || (el.parentNode._pending = {}))[(vnode.key)] = vnode;    }    beforeLeave && beforeLeave(el);// 调用beforeLeave钩子函数    if (expectsCSS) {      addTransitionClass(el, leaveClass);// 增加leave类名      addTransitionClass(el, leaveActiveClass)// 增加leaveActive类名      nextFrame(function () {//        removeTransitionClass(el, leaveClass);// 在下一帧移除leave类名        if (!cb.cancelled) {          addTransitionClass(el, leaveToClass);// 在下一帧增加leaveToClass类名          if (!userWantsControl) {            if (isValidDuration(explicitLeaveDuration)) {// 动画实现后或者设定的持续时间实现后移除leaveTo和leaveActive类名,最初dom从document移除              setTimeout(cb, explicitLeaveDuration);// 在用户设定的持续时间后调用回调函数            } else {              whenTransitionEnds(el, type, cb);// 动画完结调用回调函数            }          }        }      });    }    leave && leave(el, cb);    if (!expectsCSS && !userWantsControl) {      cb();    }  }}

5.vue通过window.getComputedStyle获取css设置的动画工夫,element.style 读取的只是元素的内联款式,即写在元素的 style 属性上的款式;而 getComputedStyle 读取的款式是最终款式,包含了内联款式、嵌入款式和内部款式。