element-plus源码学习后,我了解到多种多样的写法

文章封面来自于深圳湾桥,很漂亮!

本文是阅读源码之后,学习到一些新写法,平常业务开发也可以用起来。在我看来,阅读源码,不但能知道该框架的底层原理,出现bug时,可以快速排查和修复,更重要的是,阅读源码就像向优秀的人学习,掌握我们不曾了解的新知识点,看看别人是如何编写出漂亮的,可复用的代码。

操作符!.

onMounted(() => {
  // 组合在一起,!. 就是“强制执行方法,然后再访问它的返回值”。
  const items = breadcrumb.value!.querySelectorAll(`.${ns.e('item')}`)
  if (items.length) {
    items[items.length - 1].setAttribute('aria-current', 'page')
  }
})

element-plus: 只有控制到页面上行为的函数就存在于vue文件中,其他逻辑处理函数,都在ts文件中

取值和监听值变化,不使用watch

const checkedValue = computed<CascaderValue>({
  get() {
    return cloneDeep(props.modelValue) as CascaderValue
  },
  set(val) {
    emit(UPDATE_MODEL_EVENT, val)
    emit(CHANGE_EVENT, val)
    if (props.validateEvent) {
      formItem?.validate('change').catch((err) => debugWarn(err))
    }
  },
})

方法调用后面加!

// 透传
  const carouselContext = inject(carouselContextKey)!

在 JavaScript 中,方法调用后面加上感叹号 ! 通常是用来强制执行(Force execution)这个方法。以数组作为例子:- 使用数组方法时不加感叹号,它只会返回方法的结果,但不会改变数组自身:

let arr = [1, 2, 3];
let newArr = arr.map(x => x * 2);

// newArr [2, 4, 6]
// arr 还是 [1, 2, 3], 数组本身没有变化

而加上感叹号后,会修改数组自身:

let arr = [1, 2, 3];
arr.map!(x => x * 2);

// arr 变为 [2, 4, 6]
// 数组自身被修改了

这是因为,数组的 map 等方法本身是不会修改原数组的,而加上感叹号后则会强制修改数组自身。

其他数组方法同理,比如 filter、forEach、sort 等,不加感叹号只返回结果,加感叹号则修改原数组。

总的来说,感叹号 ! 被用于强制执行某个不会产生副作用的方法,产生副作用(side effect),修改调用者自身。

这一点和 Haskell 中的特写运算符(Bang pattern)! 类似,也用于产生 computations with effects。

使用感叹号需要小心,可能会影响程序的可预测性和数据不变性,但对于需要改变源数据的场景,使用感叹号可以使代码更加简洁。

透传使用symbol

provide(
      CASCADER_PANEL_INJECTION_KEY, // 透传下去,使用Symbol作为唯一值
      reactive({
        config,
        expandingNode,
        checkedNodes,
        isHoverMenu,
        initialLoaded,
        renderLabelFn,
        lazyLoad,
        expandNode,
        handleCheckChange,
      })
    )


export const CASCADER_PANEL_INJECTION_KEY: InjectionKey<ElCascaderPanelContext> =
  Symbol()



强制执行,会影响最终值

element-plus源码学习后,我了解到多种多样的写法_第1张图片

获取组件实例,调用组件自定义事件

const { emit } = getCurrentInstance()!

const model = computed({
    get() {
      return isGroup.value
        ? checkboxGroup?.modelValue?.value
        : props.modelValue ?? selfModel.value
    },

    set(val: unknown) {
      if (isGroup.value && isArray(val)) {
        isLimitExceeded.value =
          checkboxGroup?.max?.value !== undefined &&
          val.length > checkboxGroup?.max.value
        isLimitExceeded.value === false && checkboxGroup?.changeEvent?.(val)
      } else {
        emit(UPDATE_MODEL_EVENT, val)
        selfModel.value = val
      }
    },
  })

  1. getCurrentInstance() 函数可以在 setup() 中获取到当前组件实例。

  2. 但是直接调用 getCurrentInstance() 会返回一个可选型(maybe)的实例,可能为 null。

  3. 为了确保获取到实例,使用 ! 非空断言运算符,强制转换为非空实例。

  4. 所以 getCurrentInstance()! 确保了返回的实例不会是 null。

  5. 然后使用 ES6 解构赋值语法,从实例中取出 emit 方法。

  6. emit 方法用于在组件内部触发自定义事件。

  7. 这种方式避免了代码中出现隐式的 this,使代码更清晰可读。

  8. 是 Vue 3 Composition API 中常用的一种实例访问模式,用于在 setup() 中获取实例属性和方法。

unref与ref

element-plus源码学习后,我了解到多种多样的写法_第2张图片

unref 和 ref 都可以用来获取响应式对象的值,但是有以下几点关键区别:

  1. unref 是一个函数,ref 是创建ref对象的方法。unref直接返回值,ref会创建一个响应式的引用对象。

  2. unref 获取值,ref 创建值的引用。unref目的是获取值,ref是创建一个值的响应式引用。

  3. unref 参数可以是基础值或响应式对象,ref只接受基础值创建引用。unref的参数可以是基础类型的值,也可以是ref或reactive对象,它会返回对象的值或对象本身。ref只接受基础类型的值来创建一个响应式的引用对象。

  4. unref 使用场景是在组件逻辑中获取值,ref创建应用于模板的响应式引用。unref常用于组件逻辑中,需要获取响应式对象的原始值时使用。ref更多地在模板中使用,创建一个可以响应式跟踪的引用。

unref直接返回的值,不需要用.value访问

rAF & cAF

requestAnimationFrame 和 cancelAnimationFrame 是浏览器用来实现高性能动画的 API。它可以把每一帧的代码编排到浏览器的一次重绘中,避免频繁的重绘导致性能问题。

这两个函数在频繁重绘调用,可以减轻浏览器负担

比如:定时器

const isClient = typeof window !== "undefined";

export const rAF = (fn: () => void) =>
  isClient
    ? window.requestAnimationFrame(fn)
    : (setTimeout(fn, 16) as unknown as number)

export const cAF = (handle: number) =>
  isClient ? window.cancelAnimationFrame(handle) : clearTimeout(handle)

(event.target as HTMLElement).closest

const handleMouseDown = (event: MouseEvent) => {
  const target = (event.target as HTMLElement).closest('td')
  if (!target) return
  focusWithClick = true
}

const handleMouseUp = (event: MouseEvent) => {
  const target = (event.target as HTMLElement).closest('td')
  if (!target) return
  focusWithClick = false
}

closest() 方法会沿着 DOM 树向上寻找匹配的选择器的第一个祖先元素,在这里是查找最近的 元素。所以整个代码的作用就是:- 获取点击事件的目标元素

  • 将其转化为 HTMLElement 类型
  • 在其祖先元素中查找最近的 td元素
  • 并返回这个 td 元素这样可以方便地通过点击事件获取到对应的 td 元素进行后续操作。

你可能感兴趣的:(源码学习,学习,javascript,开发语言)