🚀🚀🚀Vue 3.5新特性 · 源码解析

892 阅读4分钟

这里是【程序员三千】,一个喜欢捣鼓各种编程工具新鲜玩法的啊婆主。全网同名(已入驻:🎵抖音、视频号、B站、🍠小红书)

前言

Vue 3.5出来也有一段时间了,网上有很多博主都介绍过3.5的新特性,今天我们就不再去介绍新功能点了,直接带大家去干vue3.5的源码解析。毕竟大家在面试的的时候,肯定不会被问到:你能说下vue 3.5中,更新了哪些新特性吗?(大笑表情),当然如果你之前完全没了解过3.5新特性的话,可以去看github上官方提供的更新列表我这里也整理了一份中文版的详细解读,大家感兴趣的话,自行获取。

image.png

useTemplateRef函数用法

今天我们先带大家去重新认识下其中的一个新特性:useTemplateRef函数。 首先我们先要去了解下useTemplateRef的用法。

我们知道,在vue3中,ref通常是用来声明响应式数据的,当ref不光作为响应式声明,还被用作DOM实例的时候,那么可能会造成代码视觉上的混淆

code.png

所以 Vue3.5 推出了 useTemplateRef 来进行 组件实例获取,从而跟 响应式变量 区分开。

code2.png

useTemplateRef源码解析

那这个api在vue3源码中,是怎么实现的呢?其实本质上,只不过是在ref上进行了一次封装。

我们打开vue3.5的源码,可以在这个packages/runtime-core/src/helpers/useTemplateRef.ts目录下,找到useTemplateRef的源码实现。

image.png


    import { type ShallowRef, readonly, shallowRef } from '@vue/reactivity'
    import { getCurrentInstance } from '../component'
    import { warn } from '../warning'
    import { EMPTY_OBJ } from '@vue/shared'

    export const knownTemplateRefs: WeakSet<ShallowRef> = new WeakSet()

    export function useTemplateRef<T = unknown, Keys extends string = string>(
      key: Keys,
    ): Readonly<ShallowRef<T | null>> {
      const i = getCurrentInstance()
      const r = shallowRef(null)
      if (i) {
        const refs = i.refs === EMPTY_OBJ ? (i.refs = {}) : i.refs
        let desc: PropertyDescriptor | undefined
        if (
          __DEV__ &&
          (desc = Object.getOwnPropertyDescriptor(refs, key)) &&
          !desc.configurable
        ) {
          warn(`useTemplateRef('${key}') already exists.`)
        } else {
          Object.defineProperty(refs, key, {
            enumerable: true,
            get: () => r.value,
            set: val => (r.value = val),
          })
        }
      } else if (__DEV__) {
        warn(
          `useTemplateRef() is called when there is no active component ` +
            `instance to be associated with.`,
        )
      }
      const ret = __DEV__ ? readonly(r) : r
      if (__DEV__) {
        knownTemplateRefs.add(ret)
      }
      return ret
    }
   

当然如果直接去看的话,还是有点复杂的,那我们可以把这个代码给大家简化一下,剔除掉对应的边缘逻辑,回归它的本质实现

import { type ShallowRef, readonly, shallowRef } from '@vue/reactivity'
import { getCurrentInstance } from '../component'
import { warn } from '../warning'
import { EMPTY_OBJ } from '@vue/shared'


export function useTemplateRef(
    key
    ) {
  const i = getCurrentInstance()
  const r = shallowRef(null)
  if (i) {
    const refs = i.refs === EMPTY_OBJ ? (i.refs = {}) : i.refs

    Object.defineProperty(refs, key, {
        enumerable: true,
        get: () => r.value,
        set: val => (r.value = val),
      })
  }
  return r
}

然后我们来看一下,大家重点关注这三个地方:

  • 第一个就是他的入参:key
    useTemplateRef(
        key
        )
  • 第二个就是内部定义的两个变量
     const i = getCurrentInstance()
     const r = shallowRef(null)
  • 最后就是他的返回值r
     return r

然后我们对照着useTemplateRef的使用demo,结合的看一下源码,这个key其实就是这里传进来的el

code2.png

const el = useTemplateRef('el');

然后这个i是通过getCurrentInstance这个方法进行获取的。根据这个方法名,我们可以判断出,这个i其实就是当前的一个上下文的实例,

  const i = getCurrentInstance()

得到这个i之后,接下来会进行一个判断,如果这个i存在,会从这个i里面去取一个refs,下面这段代码是对refs值的一个判空操作。最后获得的这个refs其实就是,上下文中所有ref的实例集合。

  if (i) {
    const refs = i.refs === EMPTY_OBJ ? (i.refs = {}) : i.refs
  }
}

拿到所有ref的实例之后,通过Object.defineProperty监听指定对象 refs 的指定属性 key 里面的get与set行为。有了这块代码呢,也就意味着当前我们会去监听refs里面的这个key,也就是我们一开始传递进来的el。当我们为他去触发set行为的时候,我们会拿到这个val,也就是传进来的这个refs,大家可以理解成我们这个当前绑定的dom元素。当我们拿到这个val的时候,我们让r.value等于这个val


export function useTemplateRef(
    key
    ) {
  const i = getCurrentInstance()
  const r = shallowRef(null)
  if (i) {
    const refs = i.refs === EMPTY_OBJ ? (i.refs = {}) : i.refs

    Object.defineProperty(refs, key, {
        enumerable: true,
        get: () => r.value,
        set: val => (r.value = val),
      })
  }
  return r
}


那这个r又是什么呢?

我们可以看到r是通过shallowRef这个方法获取到的。如果大家之前读过ref相关源码的话,一定不会陌生,其实shallowRef这个方法本质上是调用了createRef,当我们去声明响应式数据时的ref,也是通过的这个方法实现的,所有这个r其实就是一个ref的响应式对象。

image.png

结语

所以到这里之后,大家应该都明白了吧?

其实通过useTemplateRef去获取dom对象与通过ref获取去dom对象,本质上是一样的。只是3.5里对useTemplateRef进行了一次封装。

以上就是今天与大家分享的全部内容,你的支持是我更新的最大动力,我们下期见!

打工人肝 文章/视频 不易,期待你一键三连的鼓励 !!!

送你一份三千自己发现的一份20T编程学习合集,其中包括前端、后端、AI课程、编程工具、视频剪辑、面试题库等视频教程。获取方式👉 公众号搜索【程序员三千】