useArrayDifference - VueUse源码解读

151 阅读3分钟

作用

得到两个数组的差异(响应式),意味着一个数组变了,结果也会响应式变化

例子

普通用法

import { useArrayDifference } from '@vueuse/core'

const list1 = ref([0, 1, 2, 3, 4, 5])
const list2 = ref([4, 5, 6])
const result = useArrayDifference(list1, list2)
// result.value: [0, 1, 2, 3]
list2.value = [0, 1, 2]
// result.value: [3, 4, 5]

自定义对比函数

import { useArrayDifference } from '@vueuse/core'

const list1 = ref([{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }])
const list2 = ref([{ id: 4 }, { id: 5 }, { id: 6 }])

const result = useArrayDifference(list1, list2, (value, othVal) => value.id === othVal.id)
// result.value: [{ id: 1 }, { id: 2 }, { id: 3 }]

自定义key

const list1 = ref([{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }])
const list2 = ref([{ id: 3 }, { id: 4 }, { id: 5 }])

const result = useArrayDifference(list1, list2, 'id')

// result.value: [{ id: 1 }, { id: 2 }]

源码解读

首先使用了函数重载的方式,使得这个函数的参数的多样性,第三个参数可不写,可以是个 字符串表示 key,也可以是一个 对比函数;

核心在于使用了 computed 得到的是结算属性,这样子 listvalues 变了,result 也会更新,其次是这里使用的是 findIndex 而不是 使用的 !find(y => compareFn(x, y)),避免了 非真值(0, false, null)的干扰

computed(() => toValue(list).filter(x => toValue(values).findIndex(y => compareFn(x, y)) === -1))
import type { ComputedRef } from 'vue-demi'
import { computed } from 'vue-demi'
import type { MaybeRefOrGetter } from '../utils'
import { toValue } from '../toValue'

// 默认对比函数,value 全等于 othVal
function defaultComparator<T>(value: T, othVal: T) {
  return value === othVal
}

export function useArrayDifference<T>(list: MaybeRefOrGetter<T[]>, values: MaybeRefOrGetter<T[]>, key?: keyof T): ComputedRef<T[]>
export function useArrayDifference<T>(list: MaybeRefOrGetter<T[]>, values: MaybeRefOrGetter<T[]>, compareFn?: (value: T, othVal: T) => boolean): ComputedRef<T[]>

/**
 * Reactive get array difference of two array
 * @see https://vueuse.org/useArrayDifference
 * @returns - the difference of two array
 * @param args
 */
export function useArrayDifference<T>(...args: any[]): ComputedRef<T[]> {
  const list: MaybeRefOrGetter<T[]> = args[0]
  const values: MaybeRefOrGetter<T[]> = args[1]
  let compareFn = args[2] ?? defaultComparator

  if (typeof compareFn === 'string') {
    const key = compareFn as keyof T
    compareFn = (value: T, othVal: T) => value[key] === othVal[key]
  }

  return computed(() => toValue(list).filter(x => toValue(values).findIndex(y => compareFn(x, y)) === -1))
}

进一步思考

源码,只会得到在数组1中而不在数组2中的数据,但是差异的意思应该是可以筛选出,数组1和数组2不相同的元素才对;

本人优化后的代码如下: 添加了一个options,可选参数,支持合并差异。

pr: github.com/vueuse/vueu…

import type { ComputedRef } from 'vue-demi'
import { computed } from 'vue-demi'
import type { MaybeRefOrGetter } from '../utils'
import { toValue } from '../toValue'

export interface UseArrayDifferenceOptions {
  /**
   * Whether to merge differences
   * @default false
   */
  mergeDiff?: boolean
}

function defaultComparator<T>(value: T, othVal: T) {
  return value === othVal
}

export function useArrayDifference<T>(list: MaybeRefOrGetter<T[]>, values: MaybeRefOrGetter<T[]>, key?: keyof T): ComputedRef<T[]>
export function useArrayDifference<T>(list: MaybeRefOrGetter<T[]>, values: MaybeRefOrGetter<T[]>, key?: keyof T, options?: UseArrayDifferenceOptions): ComputedRef<T[]>
export function useArrayDifference<T>(list: MaybeRefOrGetter<T[]>, values: MaybeRefOrGetter<T[]>, compareFn?: (value: T, othVal: T) => boolean): ComputedRef<T[]>
export function useArrayDifference<T>(list: MaybeRefOrGetter<T[]>, values: MaybeRefOrGetter<T[]>, compareFn?: (value: T, othVal: T) => boolean, options?: UseArrayDifferenceOptions): ComputedRef<T[]>

/**
 * Reactive get array difference of two array
 * @see https://vueuse.org/useArrayDifference
 * @returns - the difference of two array
 * @param args
 */
export function useArrayDifference<T>(...args: any[]): ComputedRef<T[]> {
  const list: MaybeRefOrGetter<T[]> = args[0]
  const values: MaybeRefOrGetter<T[]> = args[1]

  let compareFn = args[2] ?? defaultComparator
  const { mergeDiff = false } = args[3] ?? {}

  if (typeof compareFn === 'string') {
    const key = compareFn as keyof T
    compareFn = (value: T, othVal: T) => value[key] === othVal[key]
  }

  const diff1 = computed(() => toValue(list).filter(x => toValue(values).findIndex(y => compareFn(x, y)) === -1))
  const diff2 = computed(() => toValue(values).filter(x => toValue(list).findIndex(y => compareFn(x, y)) === -1))

  const result = computed(() => mergeDiff ? [...toValue(diff1), ...toValue(diff2)] : toValue(diff1))

  return result
}

其他知识【选读】

函数重载 在 TypeScript 中,函数重载允许你为同一个函数定义多个签名。这样可以在不同的上下文中使用相同的函数名,但参数和返回类型可以不同。