Расскажите о vue — переходе в деталях

Vue.js
Расскажите о vue — переходе в деталях

Эта статья является третьей статьей в серии vue.vueвстроенные компонентыtransition. Предыдущие ссылки выглядят следующим образом

Прежде чем мы начнем, давайте взглянем на официальныйtransitionОпределение

  1. Автоматически определяет, использует ли целевой элемент переходы или анимацию CSS, и если да, то добавляет/удаляет классы перехода CSS в соответствующее время.
  2. Если компонент перехода установленХук-функция JavaScript, эти функции ловушек будут вызываться в нужное время.
  3. Если переходы/анимации CSS не обнаружены и не установлены хуки JavaScript, вставка и/или удаление DOM выполняется немедленно в следующем кадре.

1. Абстрактные компоненты

существуетvueв, дляoptionsЕсть свойство под названиемabstract, типboolean, который показывает, является ли компонент абстрактным компонентом. но искалoptions, но нашел только такой

static options: Object;

Но по факту,vueвсе еще правabstractсвойства обрабатываются, вlifecycle.jsизinitLifecycle()Есть такая логика в методе

let parent = options.parent
if (parent && !options.abstract) {
  while (parent.$options.abstract && parent.$parent) {
    parent = parent.$parent
  }
  parent.$children.push(vm)
}
vm.$parent = parent

Из этой логики мы видим, чтоvueОн будет подниматься слой за слоем, чтобы узнать, есть ли он у родительского узла.abstractатрибут, и после того, как он будет найден, он будет непосредственноvmупал на родительский узел$children, его собственные отношения родитель-потомок напрямую игнорируются

затем вcreate-component.jsизcreateComponent()Это обрабатывается в методе следующим образом

if (isTrue(Ctor.options.abstract)) {
  // abstract components do not keep anything
  // other than props & listeners & slot

  // work around flow
  const slot = data.slot
  data = {}
  if (slot) {
    data.slot = slot
  }
}

2. Приведите пример

Прежде чем анализировать, посмотрите официальнуюtransitionпример

<template>
  <div id="example">
    <button @click="show = !show">
      Toggle render
    </button>
    <transition name="slide-fade">
      <p v-if="show">hello</p>
    </transition>
  </div>
</template>

<script>
export default {
  data () {
    return {
      show: true
    }
  }
}
</script>

<style lang="scss">
.slide-fade-enter-active {
  transition: all .3s ease;
}
.slide-fade-leave-active {
  transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.slide-fade-enter, .slide-fade-leave-to {
  transform: translateX(10px);
  opacity: 0;
}
</style>

При нажатии кнопки для переключения состояния отображенияtransitionОбернутый элемент будет иметь переход css. Далее мы проанализируем, как он производит этот эффект.

Три, реализация перехода

Из приведенного выше использования мы должны быть в состоянии понять, что для<transition>,vueОн предоставляет нам полный набор хуков css и js.Сами хуки были определены для меня и привязаны к экземпляру.Остальное нужно переписать функцию хуков css или js самим. На самом деле это иvueсебяhooksРеализация очень похожа и поможет нам делать разные вещи в разных хуках.

Это мышление программирования очень похоже на мышление в функциональном программировании, или так оно и есть.

Функциональное программирование связано с отображением данных, и лямбда в функциональном программировании может рассматриваться как отношение между двумя типами, типом ввода и типом вывода. Вы даете ему значение типа ввода, и вы получаете значение типа вывода.

Собственно, это и отражено в этом куске, и разные результаты вывода можно получить, переписывая в разной степени в разных хуках.Конечно, вывод здесь - это собственно эффект перехода, и этот метод не совсем тот输入 => 输出способ? Далее речь пойдет о конкретной реализации этого блока.

<transtion>Реализация компонентаexportИз объекта, отсюда мы можем видеть, что он будет предварительно установленpropsсвязываются сtransition, а затем нам нужно только ввести в него точки, чтобы получить соответствующий вывод стиля Нам не нужно думать о конкретном процессе.

export default {
  name: 'transition',
  props: transitionProps,
  abstract: true,
  render (h: Function) {
    // 具体 render 等会再看
  }
}

который поддерживаетpropsКак следует, вы можетеenterа такжеleaveстиль для произвольных перезаписей

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]
}

Далее посмотримrenderСодержимое внутри — это то, как делать то, что у нас есть заранее输入 => 输出преобразованный

  • childrenЛогика обработки: сначала получить из слота по умолчанию<transition>обернутые дочерние узлы, которые затем обрабатываютсяfilterФильтровать, отфильтровывать текстовые узлы и пробелы. Если нет дочернего узла, то сразуreturn, если есть несколько дочерних узлов, сообщается об ошибке, потому что<transition>Компонент может иметь только один дочерний узел
let children: any = this.$slots.default
if (!children) {
  return
}
children = children.filter(isNotTextNode)
if (!children.length) {
  return
}
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
  )
}
  • modelЛогика обработки: суждениеmodeЭтоin-outа такжеout-inДва режима, если нет, сообщать об ошибке напрямую
const mode: string = this.mode
if (
  process.env.NODE_ENV !== 'production' &&
  mode && mode !== 'in-out' && mode !== 'out-in'
) {
  warn(
    'invalid <transition> mode: ' + mode,
    this.$parent
  )
}
  • rawChild & childЛогика обработки:rawChildдля<transition>первый компонент пакетаvnodeдочерний узел, затем определите, является ли его родительский контейнер такжеtransition, если да, вернитесь напрямуюrawChild; затем выполнитьgetRealChild()Метод получает реальный дочерний узел компонента и возвращает значение, если он не существует.rawChild
const rawChild: VNode = children[0]
if (hasParentTransition(this.$vnode)) {
  return rawChild
}
const child: ?VNode = getRealChild(rawChild)
if (!child) {
  return rawChild
}

  • hasParentTransition(): Поднимитесь на один слой вверх, чтобы узнать, есть ли у родительского узлаtransitionАтрибуты
function hasParentTransition (vnode: VNode): ?boolean {
  while ((vnode = vnode.parent)) {
    if (vnode.data.transition) {
      return true
    }
  }
}

  • getRealChild(): рекурсивно получить первый неабстрактный дочерний узел и вернуть
function getRealChild (vnode: ?VNode): ?VNode {
  const compOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
  if (compOptions && compOptions.Ctor.options.abstract) {
    return getRealChild(getFirstComponentChild(compOptions.children))
  } else {
    return vnode
  }
}

  • Далее правильноchild.keyа такжеdataобработанный. сначала черезidи ряд условных парchild.keyвыполнить операцию присваивания, затем использоватьextractTransitionDataПолучите необходимые переходы из экземпляра компонентаdataданные
const id: string = `__transition-${this._uid}-` // 使用 this._uid 进行拼接
child.key = child.key == null // 若 child.key 为 null
  ? child.isComment // child 为注释节点
    ? id + 'comment'
    : id + child.tag
  : isPrimitive(child.key) // 若 child.key 为 原始值
    ? (String(child.key).indexOf(id) === 0 ? child.key : id + child.key)
    : child.key
// 获取过渡需要的数据
const data: Object = (child.data || (child.data = {})).transition = extractTransitionData(this)

if (child.data.directives && child.data.directives.some(isVShowDirective)) {
  child.data.show = true
}

  • extractTransitionData(): Это целое является входным значениемpropsи внутриeventsПроцесс преобразования , параметр методаthis, текущий компонент. Сначала пройдите текущий компонентoptions.propsDataи назначитьdata, затем перебирает события родительского компонента и присваивает ихdata.
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
}

Согласно текущей логике, пример, который я привел выше, полученныйchild.dataследующим образом

{
  transition: {
    name: 'slide-fade'
  }
}

  • Наконец, старый и новыйchildСравните и выполните некоторые функции ловушекhook mergeждать
const oldRawChild: VNode = this._vnode
const oldChild: VNode = getRealChild(oldRawChild)
if (
  oldChild &&
  oldChild.data &&
  !isSameChild(child, oldChild) &&
  !isAsyncPlaceholder(oldChild) &&
  !(oldChild.componentInstance && oldChild.componentInstance._vnode.isComment)
) {
  // 将 oldData 更为为当前的 data
  const oldData: Object = oldChild.data.transition = extend({}, data)
  // 当 transition mode 为 out-in 的情况,返回一个 vnode 占位,执行完 watch 进行更新
  if (mode === 'out-in') {
    this._leaving = true
    mergeVNodeHook(oldData, 'afterLeave', () => {
      this._leaving = false
      this.$forceUpdate()
    })
    return placeholder(h, rawChild)
  // 当 transition mode 为 in-out 的情况
  } else if (mode === 'in-out') {
    if (isAsyncPlaceholder(child)) {
      return oldRawChild
    }
    let delayedLeave
    const performLeave = () => { delayedLeave() }
    mergeVNodeHook(data, 'afterEnter', performLeave)
    mergeVNodeHook(data, 'enterCancelled', performLeave)
    mergeVNodeHook(oldData, 'delayLeave', leave => { delayedLeave = leave })
  }
}

Проверьте это здесьmergeVNodeHook(),placeholder()а также$forceUpdate()Логика, упомянутая здесьmergeVNodeHookлогика: будетhookфункция объединена вdef.data.hook[hookKey], создать новыйinvoker

// mergeVNodeHook
export function mergeVNodeHook (def: Object, hookKey: string, hook: Function) {
  if (def instanceof VNode) {
    def = def.data.hook || (def.data.hook = {})
  }
  let invoker
  // 获取已有 hook 赋值给 oldHook
  const oldHook = def[hookKey]
  function wrappedHook () {
    hook.apply(this, arguments)
    // 删除合并的钩子确保其只被调用一次,这样能防止内存泄漏
    remove(invoker.fns, wrappedHook)
  }
	// 如果 oldHook 不存在,则直接创建一个 invoker
  if (isUndef(oldHook)) {
    invoker = createFnInvoker([wrappedHook])
  } else {
    // oldHook 已经存在,则将 invoker 赋值为 oldHook
    if (isDef(oldHook.fns) && isTrue(oldHook.merged)) {
      invoker = oldHook
      invoker.fns.push(wrappedHook)
    } else {
      // 现有的普通钩子
      invoker = createFnInvoker([oldHook, wrappedHook])
    }
  }

  invoker.merged = true
  def[hookKey] = invoker
}

// placeholder
function placeholder (h: Function, rawChild: VNode): ?VNode {
  if (/\d-keep-alive$/.test(rawChild.tag)) {
    return h('keep-alive', {
      props: rawChild.componentOptions.propsData
    })
  }
}

// $forceUpdate
Vue.prototype.$forceUpdate = function () {
  const vm: Component = this
  if (vm._watcher) {
    vm._watcher.update()
  }
}

Четыре, войти и выйти

Введение<transition>Внедрение компонента, мы узнали, что его вrenderсцена для输入值 propsи некоторые js-хуки для输出процесс обработки.

Или мы можем понять это так,<transition>существуетrenderЭтап получит данные об узле в разныхmodeСоответствующая функция ловушки привязана к соответствующей функции ловушки, и необходимо использовать каждую ловушку.dataданные, а также возвращаетrawChild vnodeузел.

Однако до сих пор он не делал никакого дизайна в анимации. Итак, далее мы поговорим об этом подробноtransitionсуществуетenterтак же какleaveКак работает эта штука输入 => 输出из.

первый вsrc/platforms/web/modules/transition.js, есть такой кусок кода

function _enter (_: any, vnode: VNodeWithData) {
  if (vnode.data.show !== true) {
    enter(vnode)
  }
}

export default inBrowser ? {
  create: _enter,
  activate: _enter,
  remove (vnode: VNode, rm: Function) {
    if (vnode.data.show !== true) {
      leave(vnode, rm)
    } else {
      rm()
    }
  }
} : {}

Из приведенного выше кода видно, что при обработке анимации он устанавливает два тайминга, а именно:

  1. существуетcreateа такжеactivateвыполнить, когдаenter()
  2. removeвыполнить, когдаleave()

1. введите

Прежде чем анализировать, давайте посмотрим наenter()контекст дизайна

export function enter (vnode: VNodeWithData, toggleDisplay: ?() => void) {
  const el: any = vnode.elm
  // 如果 el 中存在 _leaveCb,则立即执行 _leaveCb()
  if (isDef(el._leaveCb)) {
    el._leaveCb.cancelled = true
    el._leaveCb()
  }
  // 一系列处理,这里忽略,后面会具体分析
}

  • Сначала давайте посмотрим наenterЧто делать с переходными данными: они будутvnode.data.transitionвходящий вresolveTransition()Когда параметр, и проанализируйте его, чтобы получитьdataданные
const data = resolveTransition(vnode.data.transition)
// 如果 data 不存在,则直接返回
if (isUndef(data)) {
  return
}
// 如果 el 中存在 _enterCb 或者 el 不是元素节点,则直接返回
if (isDef(el._enterCb) || el.nodeType !== 1) {
  return
}

const {
  css,
  type,
  enterClass,
  enterToClass,
  enterActiveClass,
  appearClass,
  appearToClass,
  appearActiveClass,
  beforeEnter,
  enter,
  afterEnter,
  enterCancelled,
  beforeAppear,
  appear,
  afterAppear,
  appearCancelled,
  duration
} = data

Тогда мы смотрим наresolveTransition(): метод имеет один параметрvnode.data.transition, он проходит черезautoCssTransition()метод обработкиnameсвойств и распространяется наvnode.data.transitionвверх и назад

export function resolveTransition (def?: string | Object): ?Object {
  if (!def) {
    return
  }
  if (typeof def === 'object') {
    const res = {}
    if (def.css !== false) {
      extend(res, autoCssTransition(def.name || 'v'))
    }
    extend(res, def)
    return res
  } else if (typeof def === 'string') {
    return autoCssTransition(def)
  }
}

вautoCssTransition()Конкретная логика такова: получить параметрыnameпосле возвращения сnameСвязанныйcss class

const autoCssTransition: (name: string) => Object = cached(name => {
  return {
    enterClass: `${name}-enter`,
    enterToClass: `${name}-enter-to`,
    enterActiveClass: `${name}-enter-active`,
    leaveClass: `${name}-leave`,
    leaveToClass: `${name}-leave-to`,
    leaveActiveClass: `${name}-leave-active`
  }
})

  • Сразу, да<transition>Это граничный случай обработки корневого узла дочернего компонента, нам нужно обработать его родительский компонентappear check
let context = activeInstance
let transitionNode = activeInstance.$vnode
// 往上查找出 <transition> 是子组件的根节点的边界情况,进行赋值
while (transitionNode && transitionNode.parent) {
  context = transitionNode.context
  transitionNode = transitionNode.parent
}
// 上下文实例没有 mounted 或者 vnode 不是根节点插入的
const isAppear = !context._isMounted || !vnode.isRootInsert
if (isAppear && !appear && appear !== '') {
  return
}

  • для переходаclass, функция ловушки для обработки
// 过渡 class 处理
const startClass = isAppear && appearClass
  ? appearClass
  : enterClass
const activeClass = isAppear && appearActiveClass
  ? appearActiveClass
  : enterActiveClass
const toClass = isAppear && appearToClass
  ? appearToClass
  : enterToClass
// 钩子函数处理
const beforeEnterHook = isAppear
  ? (beforeAppear || beforeEnter)
  : beforeEnter
const enterHook = isAppear
  ? (typeof appear === 'function' ? appear : enter)
  : enter
const afterEnterHook = isAppear
  ? (afterAppear || afterEnter)
  : afterEnter
const enterCancelledHook = isAppear
  ? (appearCancelled || enterCancelled)
  : enterCancelled

  • получить другую конфигурацию
const explicitEnterDuration: any = toNumber(
  isObject(duration)
    ? duration.enter
    : duration
) // 获取 enter 动画执行时间

if (process.env.NODE_ENV !== 'production' && explicitEnterDuration != null) {
  checkDuration(explicitEnterDuration, 'enter', vnode)
}
// 过渡动画是否受 css 影响
const expectsCSS = css !== false && !isIE9
// 用户是否想介入控制 css 动画
const userWantsControl = getHookArgumentsLength(enterHook)

  • правильноinsertХук-функция для слияния
if (!vnode.data.show) {
  // remove pending leave element on enter by injecting an insert hook
  mergeVNodeHook(vnode, 'insert', () => {
    const parent = el.parentNode
    const pendingNode = parent && parent._pending && parent._pending[vnode.key]
    if (pendingNode &&
      pendingNode.tag === vnode.tag &&
      pendingNode.elm._leaveCb
    ) {
      pendingNode.elm._leaveCb()
    }
    enterHook && enterHook(el, cb)
  })
}

  • Время выполнения функции хука анимации перехода: сначала выполнитьbeforeEnterHookкрючок и поместите узел DOMelСдайте, а потом рассудите, хотите ли вы управлять анимацией через css, если даtrueзатем выполнитеaddTransitionClass()Метод добавляет к узлуstartClassа такжеactiveClass.
  • затем выполнитьnextFrame()Введите следующий кадр, следующий кадр в основном для удаления предыдущего кадра и добавления хороших.class;Сразу определяем, отменен ли переход, если нет, добавляемtoClassКласс перехода; позже, если пользователь не пройдетenterHookФункция хука для управления анимацией, если пользователь указываетdurationвремя, выполнитьsetTimeoutпровестиdurationзадержка, иначе выполнитьwhenTransitionEndsПринимать решениеcbвремя исполнения
beforeEnterHook && beforeEnterHook(el)
if (expectsCSS) {
  addTransitionClass(el, startClass)
  addTransitionClass(el, activeClass)
  nextFrame(() => {
    removeTransitionClass(el, startClass)
    if (!cb.cancelled) {
      addTransitionClass(el, toClass)
      if (!userWantsControl) {
        if (isValidDuration(explicitEnterDuration)) {
          setTimeout(cb, explicitEnterDuration)
        } else {
          whenTransitionEnds(el, type, cb)
        }
      }
    }
  })
}

  • Окончательное исполнение определяется заранееcbФункция: если на анимацию влияетcssконтроль, удалениеtoClassа такжеactiveClass; Сразу определить, отменен ли переход, если да, то удалить его напрямуюstartClassи выполнитьenterCancelledHook, иначе продолжить выполнениеafterEnterHook
// enter 执行后不同动机对应不同的 cb 回调处理
const cb = el._enterCb = once(() => {
  if (expectsCSS) {
    removeTransitionClass(el, toClass)
    removeTransitionClass(el, activeClass)
  }
  if (cb.cancelled) {
    if (expectsCSS) {
      removeTransitionClass(el, startClass)
    }
    enterCancelledHook && enterCancelledHook(el)
  } else {
    afterEnterHook && afterEnterHook(el)
  }
  el._enterCb = null
})

Код задействованной выше функции выглядит следующим образом:

  1. nextFrame: простая версияrequestAnimationFrame, параметрfn, то есть метод, который нужно выполнить в следующем кадре
  2. addTransitionClass: для текущего элементаelувеличить указанныйclass
  3. removeTransitionClass: удалить текущий элементelУказанныйclass
  4. whenTransitionEnds:пройти черезgetTransitionInfoполучатьtransitionнекоторую информацию, напримерtype,timeout,propCount, и дляelпривязка элементаonEnd. Затем продолжайте выполнение следующего кадра, обновитеendedзначение, пока анимация не закончится. будуelэлементальonEndудалить и выполнитьcbфункция
// nextFrame
const raf = inBrowser
  ? window.requestAnimationFrame
    ? window.requestAnimationFrame.bind(window)
    : setTimeout
  : fn => fn()

export function nextFrame (fn: Function) {
  raf(() => {
    raf(fn)
  })
}

// addTransitionClass
export function addTransitionClass (el: any, cls: string) {
  const transitionClasses = el._transitionClasses || (el._transitionClasses = [])
  if (transitionClasses.indexOf(cls) < 0) {
    transitionClasses.push(cls)
    addClass(el, cls)
  }
}

// removeTransitionClass
export function removeTransitionClass (el: any, cls: string) {
  if (el._transitionClasses) {
    remove(el._transitionClasses, cls)
  }
  removeClass(el, cls)
}

// whenTransitionEnds
export function whenTransitionEnds (
  el: Element,
  expectedType: ?string,
  cb: Function
) {
  const { type, timeout, propCount } = getTransitionInfo(el, expectedType)
  if (!type) return cb()
  const event: string = type === TRANSITION ? transitionEndEvent : animationEndEvent
  let ended = 0
  const end = () => {
    el.removeEventListener(event, onEnd)
    cb()
  }
  const onEnd = e => {
    if (e.target === el) {
      if (++ended >= propCount) {
        end()
      }
    }
  }
  setTimeout(() => {
    if (ended < propCount) {
      end()
    }
  }, timeout + 1)
  el.addEventListener(event, onEnd)
}

2. уйти

Мы закончилиenterэтот кусок输入 => 输出обработка, которая в основном происходит после вставки компонента. Далее идет соответствующийleaveсцена输入 => 输出в основном это происходит до того, как компонент будет уничтожен.leaveа такжеenterОбработка этапов очень похожа, поэтому я не буду их повторять здесь, вы можете прочитать их сами. Я немного расскажу о них нижеdelayLeaveДля отложенного исполненияleaveКак создаются анимации перехода

export function leave (vnode: VNodeWithData, rm: Function) {
  const el: any = vnode.elm

  // call enter callback now
  if (isDef(el._enterCb)) {
    el._enterCb.cancelled = true
    el._enterCb()
  }

  const data = resolveTransition(vnode.data.transition)
  if (isUndef(data) || el.nodeType !== 1) {
    return rm()
  }

  if (isDef(el._leaveCb)) {
    return
  }

  const {
    css,
    type,
    leaveClass,
    leaveToClass,
    leaveActiveClass,
    beforeLeave,
    leave,
    afterLeave,
    leaveCancelled,
    delayLeave,
    duration
  } = data

  const expectsCSS = css !== false && !isIE9
  const userWantsControl = getHookArgumentsLength(leave)

  const explicitLeaveDuration: any = toNumber(
    isObject(duration)
      ? duration.leave
      : duration
  )

  if (process.env.NODE_ENV !== 'production' && isDef(explicitLeaveDuration)) {
    checkDuration(explicitLeaveDuration, 'leave', vnode)
  }

  const cb = el._leaveCb = once(() => {
    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()
      afterLeave && afterLeave(el)
    }
    el._leaveCb = null
  })

  if (delayLeave) {
    delayLeave(performLeave)
  } 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: any)] = vnode
    }
    beforeLeave && beforeLeave(el)
    if (expectsCSS) {
      addTransitionClass(el, leaveClass)
      addTransitionClass(el, leaveActiveClass)
      nextFrame(() => {
        removeTransitionClass(el, leaveClass)
        if (!cb.cancelled) {
          addTransitionClass(el, leaveToClass)
          if (!userWantsControl) {
            if (isValidDuration(explicitLeaveDuration)) {
              setTimeout(cb, explicitLeaveDuration)
            } else {
              whenTransitionEnds(el, type, cb)
            }
          }
        }
      })
    }
    leave && leave(el, cb)
    if (!expectsCSS && !userWantsControl) {
      cb()
    }
  }
}

delayLeaveчерезresolveTransition(vnode.data.transition)Получить функцию, если она существует, выполнитьdelayLeave, иначе выполнить напрямуюperformLeave

if (delayLeave) {
  delayLeave(performLeave)
} else {
  performLeave()
}

может видетьdelayLeaveэто функция, которая сама по себе ничего не делает, единственное, что нужно сделать, этоperformLeaveОн предоставляется как параметр обратного вызова, который пользователь может вызвать самостоятельно.

В соответствии с этой идеей мы просто преобразуем приведенный выше официальный пример следующим образом

<template>
  <div id="example">
    <button @click="show = !show">
      Toggle render
    </button>
    <transition name="slide-fade" @delay-leave="handleDelay">
      <p v-if="show">hello</p>
    </transition>
  </div>
</template>

<script>
export default {
  data () {
    return {
      show: true
    }
  },
  methods: {
    handleDelay (done) {
      setTimeout(() => {
        done()
      }, 2000)
    }
  }
}
</script>

<style lang="scss">
.slide-fade-enter-active {
  transition: all .3s ease;
}
.slide-fade-leave-active {
  transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.slide-fade-enter, .slide-fade-leave-to {
  transform: translateX(10px);
  opacity: 0;
}
</style>

Конечным результатом является то, что мойleaveЭффект перехода будет выполнен через 2S

Суммировать

статью сюда, для встроенного<transition>Представлен компонентный анализ. От начала до конца статьи я вставил输入 => 输出понятие в.<transition>компонент илиvueИменно таким и является сам дизайн, он делается для нас внутри输入 => 输出обработки, так что нам не нужно заботиться о его внутренностях, просто сосредоточьтесь на нашей собственной бизнес-логике.

Итак, на данный момент кажется, что<transition>Используй его, ты нужен себеcssошибиться первым

В конце концов, я построил фронтальную группу связи: 731175396.

Приглашаем всех сестринских газет (забудьте о китайской бумаге~) присоединиться, давайте хвастаться, говорить о жизни и технологиях.