惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
MyScale Blog
MyScale Blog
U
Unit 42
M
MIT News - Artificial intelligence
小众软件
小众软件
P
Proofpoint News Feed
雷峰网
雷峰网
L
LangChain Blog
S
SegmentFault 最新的问题
腾讯CDC
F
Fortinet All Blogs
A
About on SuperTechFans
WordPress大学
WordPress大学
Vercel News
Vercel News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
G
Google Developers Blog
大猫的无限游戏
大猫的无限游戏
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
D
Docker
N
Netflix TechBlog - Medium
Apple Machine Learning Research
Apple Machine Learning Research
Recent Announcements
Recent Announcements
D
DataBreaches.Net
Stack Overflow Blog
Stack Overflow Blog

时间的朋友

Windows 命令行密码重置 Anaconda安装 typescript 注解解读1 Konvajs Shape加载自定义图片 sshpass 使用 why-is-node-running webgl笔记 SharedArrayBuffer is not defined blender 常用快捷键 | 时间的朋友 vue -- v3.4commit提交记录2 vue2 升级vue3报错问题整理 着色器 expressjs 源码 hyper-V arch linux 网络配置 element input数字格式化 three 拼接货架 WebAudio笔记 Windows nginx重启bat脚本 vue -- v3.4commit提交记录 URI malformed vue3 -- Class 对象在组件中使用范例 | 时间的朋友 ruby 安装和升级 element-plus 老版本cascader使用卡死问题 前端memo的实现 | 时间的朋友 Vue -- vue-class-component源码 | 时间的朋友 linux 优化脚本 typescript 装饰器 | 时间的朋友 microbundle 源码 | 时间的朋友 WSL2问题解决WslRegisterDistribution failed with error: 0x800701bc vue -- vue3利用createVNode函数,建立命令式调用组件 | 时间的朋友
vue3 内置Transition组件 | 时间的朋友
2023-09-15 · via 时间的朋友

Published: · LastMod: September 16, 2023 · 894 words

vue3 内置Transition组件 🔗

Transition 🔗

Transition在vue源码中是一个高阶函数,为了兼容不同平台,这里把dom相关的实现都留在runtime-dom包里面,核心代码都是在runtime-core里面

1
2
3
4
export const Transition: FunctionalComponent<TransitionProps> = (
  props,
  { slots }
) => h(BaseTransition, resolveTransitionProps(props), slots)

resolveTransitionProps 🔗

解析dom相关的props,并最终返回传递给BaseTransition的props

  • 递归对象,把相关的值赋值到新对象上
1
2
3
4
5
6
const baseProps: BaseTransitionProps<Element> = {}
  for (const key in rawProps) {
    if (!(key in DOMTransitionPropsValidators)) {
      ;(baseProps as any)[key] = (rawProps as any)[key]
    }
  }
  • 组装相关的class
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
  const {
    name = 'v',
    type,
    duration,
    enterFromClass = `${name}-enter-from`,
    enterActiveClass = `${name}-enter-active`,
    enterToClass = `${name}-enter-to`,
    appearFromClass = enterFromClass,
    appearActiveClass = enterActiveClass,
    appearToClass = enterToClass,
    leaveFromClass = `${name}-leave-from`,
    leaveActiveClass = `${name}-leave-active`,
    leaveToClass = `${name}-leave-to`
  } = rawProps
  • 实现相关生命周期的代码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
extend(baseProps, {
    onBeforeEnter(el) {
      // enter
    },
    onBeforeAppear(el) {
      // appear
    },
    onEnter: makeEnterHook(false),
    onAppear: makeEnterHook(true),
    onLeave(el: Element & { _isLeaving?: boolean }, done) {
      // leave
    },
    onEnterCancelled(el) {
      // entercancel
    },
    onAppearCancelled(el) {
      // appear cancel
    },
    onLeaveCancelled(el) {
      // leave cancel
    }
  } as BaseTransitionProps<Element>)

这里基类只需要调用,不需要管如何实现

makeEnterHook 🔗

makeEnterHook是一个高阶函数,这里对勾子函数做了进一步封装

在下一帧的时候操作元素上的class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
  const makeEnterHook = (isAppear: boolean) => {
    return (el: Element, done: () => void) => {
      const hook = isAppear ? onAppear : onEnter
      const resolve = () => finishEnter(el, isAppear, done)
      callHook(hook, [el, resolve])
      nextFrame(() => {
        removeTransitionClass(el, isAppear ? appearFromClass : enterFromClass)
        if (__COMPAT__ && legacyClassEnabled) {
          const legacyClass = isAppear
            ? legacyAppearFromClass
            : legacyEnterFromClass
          if (legacyClass) {
            removeTransitionClass(el, legacyClass)
          }
        }
        addTransitionClass(el, isAppear ? appearToClass : enterToClass)
        if (!hasExplicitCallback(hook)) {
          whenTransitionEnds(el, type, enterDuration, resolve)
        }
      })
    }
  }

whenTransitionEnds 🔗

这里触发回调函数,如果设置了变化时间,通过setTimeout进行调用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const id = (el._endId = ++endId)
const resolveIfNotStale = () => {
  if (id === el._endId) {
    resolve()
  }
}

if (explicitTimeout) {
  return setTimeout(resolveIfNotStale, explicitTimeout)
}

监听dom元素动画事件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18

  const endEvent = type + 'end'
  let ended = 0
  const end = () => {
    el.removeEventListener(endEvent, onEnd)
    resolveIfNotStale()
  }
  const onEnd = (e: Event) => {
    if (e.target === el && ++ended >= propCount) {
      end()
    }
  }
  setTimeout(() => {
    if (ended < propCount) {
      end()
    }
  }, timeout + 1)
  el.addEventListener(endEvent, onEnd)

BaseTransitionImpl 🔗

遍历出所有的子元素,并且把domtree 数组 平铺

1
2
      const children =
        slots.default && getTransitionRawChildren(slots.default(), true)

找到第一个非注释的节点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
      let child: VNode = children[0]
      if (children.length > 1) {
        let hasFound = false
        // locate first non-comment child
        for (const c of children) {
          if (c.type !== Comment) {
            child = c
            hasFound = true
            if (!__DEV__) break
          }
        }
      }

设置vnode的transition Hook,直接挂在到vnode到transition属性上

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
export function setTransitionHooks(vnode: VNode, hooks: TransitionHooks) {
  if (vnode.shapeFlag & ShapeFlags.COMPONENT && vnode.component) {
    setTransitionHooks(vnode.component.subTree, hooks)
  } else if (__FEATURE_SUSPENSE__ && vnode.shapeFlag & ShapeFlags.SUSPENSE) {
    vnode.ssContent!.transition = hooks.clone(vnode.ssContent!)
    vnode.ssFallback!.transition = hooks.clone(vnode.ssFallback!)
  } else {
    vnode.transition = hooks
  }
}