diff --git a/packages/reactivity/__tests__/reactive.spec.ts b/packages/reactivity/__tests__/reactive.spec.ts index 62913e670..9db682f89 100644 --- a/packages/reactivity/__tests__/reactive.spec.ts +++ b/packages/reactivity/__tests__/reactive.spec.ts @@ -44,6 +44,27 @@ describe('reactivity/reactive', () => { expect(clone[0]).toBe(observed[0]) }) + test('Array identity methods should work with raw values', () => { + const raw = {} + const arr = reactive([{}, {}]) + arr.push(raw) + expect(arr.indexOf(raw)).toBe(2) + expect(arr.indexOf(raw, 3)).toBe(-1) + expect(arr.includes(raw)).toBe(true) + expect(arr.includes(raw, 3)).toBe(false) + expect(arr.lastIndexOf(raw)).toBe(2) + expect(arr.lastIndexOf(raw, 1)).toBe(-1) + + // should work also for the observed version + const observed = arr[2] + expect(arr.indexOf(observed)).toBe(2) + expect(arr.indexOf(observed, 3)).toBe(-1) + expect(arr.includes(observed)).toBe(true) + expect(arr.includes(observed, 3)).toBe(false) + expect(arr.lastIndexOf(observed)).toBe(2) + expect(arr.lastIndexOf(observed, 1)).toBe(-1) + }) + test('nested reactives', () => { const original = { nested: { diff --git a/packages/reactivity/src/baseHandlers.ts b/packages/reactivity/src/baseHandlers.ts index 307697055..cee658898 100644 --- a/packages/reactivity/src/baseHandlers.ts +++ b/packages/reactivity/src/baseHandlers.ts @@ -2,7 +2,7 @@ import { reactive, readonly, toRaw } from './reactive' import { TrackOpTypes, TriggerOpTypes } from './operations' import { track, trigger, ITERATE_KEY } from './effect' import { LOCKED } from './lock' -import { isObject, hasOwn, isSymbol, hasChanged } from '@vue/shared' +import { isObject, hasOwn, isSymbol, hasChanged, isArray } from '@vue/shared' import { isRef } from './ref' const builtInSymbols = new Set( @@ -15,8 +15,21 @@ const get = /*#__PURE__*/ createGetter() const readonlyGet = /*#__PURE__*/ createGetter(true) const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true) +const arrayIdentityInstrumentations: Record = {} +;['includes', 'indexOf', 'lastIndexOf'].forEach(key => { + arrayIdentityInstrumentations[key] = function( + value: unknown, + ...args: any[] + ): any { + return toRaw(this)[key](toRaw(value), ...args) + } +}) + function createGetter(isReadonly = false, shallow = false) { return function get(target: object, key: string | symbol, receiver: object) { + if (isArray(target) && hasOwn(arrayIdentityInstrumentations, key)) { + return Reflect.get(arrayIdentityInstrumentations, key, receiver) + } const res = Reflect.get(target, key, receiver) if (isSymbol(key) && builtInSymbols.has(key)) { return res