mirror of
https://github.com/vuejs/vue.git
synced 2024-11-21 20:28:54 +00:00
wip: ref tests passing
This commit is contained in:
parent
e1e5a75540
commit
ac85a4217e
@ -8,6 +8,7 @@ import {
|
||||
isArray,
|
||||
emptyObject,
|
||||
remove,
|
||||
hasChanged,
|
||||
isServerRendering,
|
||||
invokeWithErrorHandling
|
||||
} from 'core/util'
|
||||
@ -348,15 +349,6 @@ function doWatch(
|
||||
}
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#polyfill
|
||||
function hasChanged(x: unknown, y: unknown): boolean {
|
||||
if (x === y) {
|
||||
return x !== 0 || 1 / x === 1 / (y as number)
|
||||
} else {
|
||||
return x !== x && y !== y
|
||||
}
|
||||
}
|
||||
|
||||
function queuePostRenderEffect(fn: Function) {
|
||||
// TODO
|
||||
}
|
||||
|
@ -17,6 +17,23 @@ export {
|
||||
CustomRefFactory
|
||||
} from './reactivity/ref'
|
||||
|
||||
export {
|
||||
reactive,
|
||||
// readonly,
|
||||
isReactive,
|
||||
isReadonly,
|
||||
isShallow,
|
||||
// isProxy,
|
||||
// shallowReactive,
|
||||
// shallowReadonly,
|
||||
// markRaw,
|
||||
// toRaw,
|
||||
ReactiveFlags,
|
||||
// DeepReadonly,
|
||||
// ShallowReactive,
|
||||
UnwrapNestedRefs
|
||||
} from './reactivity/reactive'
|
||||
|
||||
export {
|
||||
watch,
|
||||
watchEffect,
|
||||
|
@ -1,12 +1,44 @@
|
||||
import { observe, Observer } from 'core/observer'
|
||||
import { Ref, UnwrapRefSimple } from './ref'
|
||||
|
||||
export const enum ReactiveFlags {
|
||||
SKIP = '__v_skip',
|
||||
IS_READONLY = '__v_isReadonly',
|
||||
IS_SHALLOW = '__v_isShallow',
|
||||
RAW = '__v_raw'
|
||||
}
|
||||
|
||||
export interface Target {
|
||||
__ob__?: Observer
|
||||
[ReactiveFlags.SKIP]?: boolean
|
||||
[ReactiveFlags.IS_READONLY]?: boolean
|
||||
[ReactiveFlags.IS_SHALLOW]?: boolean
|
||||
[ReactiveFlags.RAW]?: any
|
||||
}
|
||||
|
||||
export declare const ShallowReactiveMarker: unique symbol
|
||||
|
||||
export function reactive() {}
|
||||
// only unwrap nested ref
|
||||
export type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRefSimple<T>
|
||||
|
||||
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
|
||||
export function reactive(target: object) {
|
||||
// if trying to observe a readonly proxy, return the readonly version.
|
||||
if (!isReadonly(target)) {
|
||||
observe(target)
|
||||
}
|
||||
return target
|
||||
}
|
||||
|
||||
export function isReactive(value: unknown): boolean {
|
||||
return !!(value && (value as any).__ob__)
|
||||
return !!(value && (value as Target).__ob__)
|
||||
}
|
||||
|
||||
export function isShallow(value: unknown): boolean {
|
||||
// TODO
|
||||
return !!(value && (value as any).__ob__)
|
||||
return !!(value && (value as Target).__v_isShallow)
|
||||
}
|
||||
|
||||
export function isReadonly(value: unknown): boolean {
|
||||
// TODO
|
||||
return !!(value && (value as Target).__v_isReadonly)
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { defineReactive } from 'core/observer/index'
|
||||
import type { ShallowReactiveMarker } from './reactive'
|
||||
import { isReactive, ShallowReactiveMarker } from './reactive'
|
||||
import type { IfAny } from 'typescript/utils'
|
||||
import Dep from 'core/observer/dep'
|
||||
import { warn, isArray } from 'core/util'
|
||||
|
||||
declare const RefSymbol: unique symbol
|
||||
export declare const RawSymbol: unique symbol
|
||||
@ -13,6 +15,10 @@ export interface Ref<T = any> {
|
||||
* autocomplete, so we use a private Symbol instead.
|
||||
*/
|
||||
[RefSymbol]: true
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
dep: Dep
|
||||
}
|
||||
|
||||
export function isRef<T>(r: Ref<T> | unknown): r is Ref<T>
|
||||
@ -46,13 +52,13 @@ function createRef(rawValue: unknown, shallow: boolean) {
|
||||
if (isRef(rawValue)) {
|
||||
return rawValue
|
||||
}
|
||||
const ref = { __v_isRef: true }
|
||||
defineReactive(ref, 'value', rawValue, null, shallow)
|
||||
const ref: any = { __v_isRef: true, __v_isShallow: shallow }
|
||||
ref.dep = defineReactive(ref, 'value', rawValue, null, shallow)
|
||||
return ref
|
||||
}
|
||||
|
||||
export function triggerRef(ref: Ref) {
|
||||
// TODO triggerRefValue(ref, __DEV__ ? ref.value : void 0)
|
||||
ref.dep.notify()
|
||||
}
|
||||
|
||||
export function unref<T>(ref: T | Ref<T>): T {
|
||||
@ -67,22 +73,93 @@ export type CustomRefFactory<T> = (
|
||||
set: (value: T) => void
|
||||
}
|
||||
|
||||
export function customRef() {
|
||||
// TODO
|
||||
class CustomRefImpl<T> {
|
||||
public dep?: Dep = undefined
|
||||
|
||||
private readonly _get: ReturnType<CustomRefFactory<T>>['get']
|
||||
private readonly _set: ReturnType<CustomRefFactory<T>>['set']
|
||||
|
||||
public readonly __v_isRef = true
|
||||
|
||||
constructor(factory: CustomRefFactory<T>) {
|
||||
const dep = new Dep()
|
||||
const { get, set } = factory(
|
||||
() => dep.depend(),
|
||||
() => dep.notify()
|
||||
)
|
||||
this._get = get
|
||||
this._set = set
|
||||
}
|
||||
|
||||
get value() {
|
||||
return this._get()
|
||||
}
|
||||
|
||||
set value(newVal) {
|
||||
this._set(newVal)
|
||||
}
|
||||
}
|
||||
|
||||
export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
|
||||
return new CustomRefImpl(factory) as any
|
||||
}
|
||||
|
||||
export type ToRefs<T = any> = {
|
||||
[K in keyof T]: ToRef<T[K]>
|
||||
}
|
||||
|
||||
export function toRefs() {
|
||||
// TODO
|
||||
export function toRefs<T extends object>(object: T): ToRefs<T> {
|
||||
if (__DEV__ && !isReactive(object)) {
|
||||
warn(`toRefs() expects a reactive object but received a plain one.`)
|
||||
}
|
||||
const ret: any = isArray(object) ? new Array(object.length) : {}
|
||||
for (const key in object) {
|
||||
ret[key] = toRef(object, key)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
class ObjectRefImpl<T extends object, K extends keyof T> {
|
||||
public readonly __v_isRef = true
|
||||
|
||||
constructor(
|
||||
private readonly _object: T,
|
||||
private readonly _key: K,
|
||||
private readonly _defaultValue?: T[K]
|
||||
) {}
|
||||
|
||||
get value() {
|
||||
const val = this._object[this._key]
|
||||
return val === undefined ? (this._defaultValue as T[K]) : val
|
||||
}
|
||||
|
||||
set value(newVal) {
|
||||
this._object[this._key] = newVal
|
||||
}
|
||||
}
|
||||
|
||||
export type ToRef<T> = IfAny<T, Ref<T>, [T] extends [Ref] ? T : Ref<T>>
|
||||
|
||||
export function toRef() {
|
||||
// TODO
|
||||
export function toRef<T extends object, K extends keyof T>(
|
||||
object: T,
|
||||
key: K
|
||||
): ToRef<T[K]>
|
||||
|
||||
export function toRef<T extends object, K extends keyof T>(
|
||||
object: T,
|
||||
key: K,
|
||||
defaultValue: T[K]
|
||||
): ToRef<Exclude<T[K], undefined>>
|
||||
|
||||
export function toRef<T extends object, K extends keyof T>(
|
||||
object: T,
|
||||
key: K,
|
||||
defaultValue?: T[K]
|
||||
): ToRef<T[K]> {
|
||||
const val = object[key]
|
||||
return isRef(val)
|
||||
? val
|
||||
: (new ObjectRefImpl(object, key, defaultValue) as any)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,8 +12,10 @@ import {
|
||||
isPrimitive,
|
||||
isUndef,
|
||||
isValidArrayIndex,
|
||||
isServerRendering
|
||||
isServerRendering,
|
||||
hasChanged
|
||||
} from '../util/index'
|
||||
import { isRef } from '../../composition-api'
|
||||
|
||||
const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
|
||||
|
||||
@ -107,7 +109,7 @@ function copyAugment(target: Object, src: Object, keys: Array<string>) {
|
||||
* or the existing observer if the value already has one.
|
||||
*/
|
||||
export function observe(value: any, asRootData?: boolean): Observer | void {
|
||||
if (!isObject(value) || value instanceof VNode) {
|
||||
if (!isObject(value) || isRef(value) || value instanceof VNode) {
|
||||
return
|
||||
}
|
||||
let ob: Observer | void
|
||||
@ -167,22 +169,23 @@ export function defineReactive(
|
||||
}
|
||||
}
|
||||
}
|
||||
return value
|
||||
return isRef(value) ? value.value : value
|
||||
},
|
||||
set: function reactiveSetter(newVal) {
|
||||
const value = getter ? getter.call(obj) : val
|
||||
/* eslint-disable no-self-compare */
|
||||
if (newVal === value || (newVal !== newVal && value !== value)) {
|
||||
if (!hasChanged(value, newVal)) {
|
||||
return
|
||||
}
|
||||
/* eslint-enable no-self-compare */
|
||||
if (__DEV__ && customSetter) {
|
||||
customSetter()
|
||||
}
|
||||
// #7981: for accessor properties without setter
|
||||
if (getter && !setter) return
|
||||
if (setter) {
|
||||
setter.call(obj, newVal)
|
||||
} else if (getter) {
|
||||
// #7981: for accessor properties without setter
|
||||
return
|
||||
} else if (isRef(value) && !isRef(newVal)) {
|
||||
value.value = newVal
|
||||
} else {
|
||||
val = newVal
|
||||
}
|
||||
@ -190,6 +193,8 @@ export function defineReactive(
|
||||
dep.notify()
|
||||
}
|
||||
})
|
||||
|
||||
return dep
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -349,3 +349,12 @@ export function once(fn: Function): Function {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#polyfill
|
||||
export function hasChanged(x: unknown, y: unknown): boolean {
|
||||
if (x === y) {
|
||||
return x === 0 && 1 / x !== 1 / (y as number)
|
||||
} else {
|
||||
return x === x && y === y
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,16 @@
|
||||
import { ref, shallowRef, unref } from 'vca/reactivity/ref'
|
||||
import {
|
||||
ref,
|
||||
isRef,
|
||||
shallowRef,
|
||||
unref,
|
||||
triggerRef,
|
||||
toRef,
|
||||
toRefs,
|
||||
customRef,
|
||||
Ref
|
||||
} from 'vca/reactivity/ref'
|
||||
import { ReactiveEffect } from 'vca/reactivity/effect'
|
||||
import { isReactive } from 'vca/reactivity/reactive'
|
||||
import { isReactive, isShallow, reactive } from 'vca/reactivity/reactive'
|
||||
|
||||
const effect = (fn: () => any) => new ReactiveEffect(fn)
|
||||
|
||||
@ -54,59 +64,60 @@ describe('reactivity/ref', () => {
|
||||
expect(dummy).toBe(2)
|
||||
})
|
||||
|
||||
// it('should work like a normal property when nested in a reactive object', () => {
|
||||
// const a = ref(1)
|
||||
// const obj = reactive({
|
||||
// a,
|
||||
// b: {
|
||||
// c: a
|
||||
// }
|
||||
// })
|
||||
it('should work like a normal property when nested in a reactive object', () => {
|
||||
const a = ref(1)
|
||||
const obj = reactive({
|
||||
a,
|
||||
b: {
|
||||
c: a
|
||||
}
|
||||
})
|
||||
|
||||
// let dummy1: number
|
||||
// let dummy2: number
|
||||
let dummy1: number
|
||||
let dummy2: number
|
||||
|
||||
// effect(() => {
|
||||
// dummy1 = obj.a
|
||||
// dummy2 = obj.b.c
|
||||
// })
|
||||
effect(() => {
|
||||
dummy1 = obj.a
|
||||
dummy2 = obj.b.c
|
||||
})
|
||||
|
||||
// const assertDummiesEqualTo = (val: number) =>
|
||||
// [dummy1, dummy2].forEach(dummy => expect(dummy).toBe(val))
|
||||
const assertDummiesEqualTo = (val: number) =>
|
||||
[dummy1, dummy2].forEach(dummy => expect(dummy).toBe(val))
|
||||
|
||||
// assertDummiesEqualTo(1)
|
||||
// a.value++
|
||||
// assertDummiesEqualTo(2)
|
||||
// obj.a++
|
||||
// assertDummiesEqualTo(3)
|
||||
// obj.b.c++
|
||||
// assertDummiesEqualTo(4)
|
||||
// })
|
||||
assertDummiesEqualTo(1)
|
||||
a.value++
|
||||
assertDummiesEqualTo(2)
|
||||
obj.a++
|
||||
assertDummiesEqualTo(3)
|
||||
obj.b.c++
|
||||
assertDummiesEqualTo(4)
|
||||
})
|
||||
|
||||
// it('should unwrap nested ref in types', () => {
|
||||
// const a = ref(0)
|
||||
// const b = ref(a)
|
||||
it('should unwrap nested ref in types', () => {
|
||||
const a = ref(0)
|
||||
const b = ref(a)
|
||||
|
||||
// expect(typeof (b.value + 1)).toBe('number')
|
||||
// })
|
||||
expect(typeof (b.value + 1)).toBe('number')
|
||||
})
|
||||
|
||||
// it('should unwrap nested values in types', () => {
|
||||
// const a = {
|
||||
// b: ref(0)
|
||||
// }
|
||||
it('should unwrap nested values in types', () => {
|
||||
const a = {
|
||||
b: ref(0)
|
||||
}
|
||||
|
||||
// const c = ref(a)
|
||||
const c = ref(a)
|
||||
|
||||
// expect(typeof (c.value.b + 1)).toBe('number')
|
||||
// })
|
||||
expect(typeof (c.value.b + 1)).toBe('number')
|
||||
})
|
||||
|
||||
// it('should NOT unwrap ref types nested inside arrays', () => {
|
||||
// const arr = ref([1, ref(3)]).value
|
||||
// expect(isRef(arr[0])).toBe(false)
|
||||
// expect(isRef(arr[1])).toBe(true)
|
||||
// expect((arr[1] as Ref).value).toBe(3)
|
||||
// })
|
||||
it('should NOT unwrap ref types nested inside arrays', () => {
|
||||
const arr = ref([1, ref(3)]).value
|
||||
expect(isRef(arr[0])).toBe(false)
|
||||
expect(isRef(arr[1])).toBe(true)
|
||||
expect((arr[1] as Ref).value).toBe(3)
|
||||
})
|
||||
|
||||
// Vue 2 does not observe array properties
|
||||
// it('should unwrap ref types as props of arrays', () => {
|
||||
// const arr = [ref(0)]
|
||||
// const symbolKey = Symbol('')
|
||||
@ -120,69 +131,69 @@ describe('reactivity/ref', () => {
|
||||
// expect(arrRef[symbolKey as any]).toBe(2)
|
||||
// })
|
||||
|
||||
// it('should keep tuple types', () => {
|
||||
// const tuple: [number, string, { a: number }, () => number, Ref<number>] = [
|
||||
// 0,
|
||||
// '1',
|
||||
// { a: 1 },
|
||||
// () => 0,
|
||||
// ref(0)
|
||||
// ]
|
||||
// const tupleRef = ref(tuple)
|
||||
it('should keep tuple types', () => {
|
||||
const tuple: [number, string, { a: number }, () => number, Ref<number>] = [
|
||||
0,
|
||||
'1',
|
||||
{ a: 1 },
|
||||
() => 0,
|
||||
ref(0)
|
||||
]
|
||||
const tupleRef = ref(tuple)
|
||||
|
||||
// tupleRef.value[0]++
|
||||
// expect(tupleRef.value[0]).toBe(1)
|
||||
// tupleRef.value[1] += '1'
|
||||
// expect(tupleRef.value[1]).toBe('11')
|
||||
// tupleRef.value[2].a++
|
||||
// expect(tupleRef.value[2].a).toBe(2)
|
||||
// expect(tupleRef.value[3]()).toBe(0)
|
||||
// tupleRef.value[4].value++
|
||||
// expect(tupleRef.value[4].value).toBe(1)
|
||||
// })
|
||||
tupleRef.value[0]++
|
||||
expect(tupleRef.value[0]).toBe(1)
|
||||
tupleRef.value[1] += '1'
|
||||
expect(tupleRef.value[1]).toBe('11')
|
||||
tupleRef.value[2].a++
|
||||
expect(tupleRef.value[2].a).toBe(2)
|
||||
expect(tupleRef.value[3]()).toBe(0)
|
||||
tupleRef.value[4].value++
|
||||
expect(tupleRef.value[4].value).toBe(1)
|
||||
})
|
||||
|
||||
// it('should keep symbols', () => {
|
||||
// const customSymbol = Symbol()
|
||||
// const obj = {
|
||||
// [Symbol.asyncIterator]: ref(1),
|
||||
// [Symbol.hasInstance]: { a: ref('a') },
|
||||
// [Symbol.isConcatSpreadable]: { b: ref(true) },
|
||||
// [Symbol.iterator]: [ref(1)],
|
||||
// [Symbol.match]: new Set<Ref<number>>(),
|
||||
// [Symbol.matchAll]: new Map<number, Ref<string>>(),
|
||||
// [Symbol.replace]: { arr: [ref('a')] },
|
||||
// [Symbol.search]: { set: new Set<Ref<number>>() },
|
||||
// [Symbol.species]: { map: new Map<number, Ref<string>>() },
|
||||
// [Symbol.split]: new WeakSet<Ref<boolean>>(),
|
||||
// [Symbol.toPrimitive]: new WeakMap<Ref<boolean>, string>(),
|
||||
// [Symbol.toStringTag]: { weakSet: new WeakSet<Ref<boolean>>() },
|
||||
// [Symbol.unscopables]: { weakMap: new WeakMap<Ref<boolean>, string>() },
|
||||
// [customSymbol]: { arr: [ref(1)] }
|
||||
// }
|
||||
it('should keep symbols', () => {
|
||||
const customSymbol = Symbol()
|
||||
const obj = {
|
||||
[Symbol.asyncIterator]: ref(1),
|
||||
[Symbol.hasInstance]: { a: ref('a') },
|
||||
[Symbol.isConcatSpreadable]: { b: ref(true) },
|
||||
[Symbol.iterator]: [ref(1)],
|
||||
[Symbol.match]: new Set<Ref<number>>(),
|
||||
[Symbol.matchAll]: new Map<number, Ref<string>>(),
|
||||
[Symbol.replace]: { arr: [ref('a')] },
|
||||
[Symbol.search]: { set: new Set<Ref<number>>() },
|
||||
[Symbol.species]: { map: new Map<number, Ref<string>>() },
|
||||
[Symbol.split]: new WeakSet<Ref<boolean>>(),
|
||||
[Symbol.toPrimitive]: new WeakMap<Ref<boolean>, string>(),
|
||||
[Symbol.toStringTag]: { weakSet: new WeakSet<Ref<boolean>>() },
|
||||
[Symbol.unscopables]: { weakMap: new WeakMap<Ref<boolean>, string>() },
|
||||
[customSymbol]: { arr: [ref(1)] }
|
||||
}
|
||||
|
||||
// const objRef = ref(obj)
|
||||
const objRef = ref(obj)
|
||||
|
||||
// const keys: (keyof typeof obj)[] = [
|
||||
// Symbol.asyncIterator,
|
||||
// Symbol.hasInstance,
|
||||
// Symbol.isConcatSpreadable,
|
||||
// Symbol.iterator,
|
||||
// Symbol.match,
|
||||
// Symbol.matchAll,
|
||||
// Symbol.replace,
|
||||
// Symbol.search,
|
||||
// Symbol.species,
|
||||
// Symbol.split,
|
||||
// Symbol.toPrimitive,
|
||||
// Symbol.toStringTag,
|
||||
// Symbol.unscopables,
|
||||
// customSymbol
|
||||
// ]
|
||||
const keys: (keyof typeof obj)[] = [
|
||||
Symbol.asyncIterator,
|
||||
Symbol.hasInstance,
|
||||
Symbol.isConcatSpreadable,
|
||||
Symbol.iterator,
|
||||
Symbol.match,
|
||||
Symbol.matchAll,
|
||||
Symbol.replace,
|
||||
Symbol.search,
|
||||
Symbol.species,
|
||||
Symbol.split,
|
||||
Symbol.toPrimitive,
|
||||
Symbol.toStringTag,
|
||||
Symbol.unscopables,
|
||||
customSymbol
|
||||
]
|
||||
|
||||
// keys.forEach(key => {
|
||||
// expect(objRef.value[key]).toStrictEqual(obj[key])
|
||||
// })
|
||||
// })
|
||||
keys.forEach(key => {
|
||||
expect(objRef.value[key]).toStrictEqual(obj[key])
|
||||
})
|
||||
})
|
||||
|
||||
test('unref', () => {
|
||||
expect(unref(1)).toBe(1)
|
||||
@ -204,192 +215,192 @@ describe('reactivity/ref', () => {
|
||||
expect(dummy).toBe(2)
|
||||
})
|
||||
|
||||
// test('shallowRef force trigger', () => {
|
||||
// const sref = shallowRef({ a: 1 })
|
||||
// let dummy
|
||||
// effect(() => {
|
||||
// dummy = sref.value.a
|
||||
// })
|
||||
// expect(dummy).toBe(1)
|
||||
test('shallowRef force trigger', () => {
|
||||
const sref = shallowRef({ a: 1 })
|
||||
let dummy
|
||||
effect(() => {
|
||||
dummy = sref.value.a
|
||||
})
|
||||
expect(dummy).toBe(1)
|
||||
|
||||
// sref.value.a = 2
|
||||
// expect(dummy).toBe(1) // should not trigger yet
|
||||
sref.value.a = 2
|
||||
expect(dummy).toBe(1) // should not trigger yet
|
||||
|
||||
// // force trigger
|
||||
// triggerRef(sref)
|
||||
// expect(dummy).toBe(2)
|
||||
// })
|
||||
// force trigger
|
||||
triggerRef(sref)
|
||||
expect(dummy).toBe(2)
|
||||
})
|
||||
|
||||
// test('shallowRef isShallow', () => {
|
||||
// expect(isShallow(shallowRef({ a: 1 }))).toBe(true)
|
||||
// })
|
||||
test('shallowRef isShallow', () => {
|
||||
expect(isShallow(shallowRef({ a: 1 }))).toBe(true)
|
||||
})
|
||||
|
||||
// test('isRef', () => {
|
||||
// expect(isRef(ref(1))).toBe(true)
|
||||
// expect(isRef(computed(() => 1))).toBe(true)
|
||||
test('isRef', () => {
|
||||
expect(isRef(ref(1))).toBe(true)
|
||||
// TODO expect(isRef(computed(() => 1))).toBe(true)
|
||||
|
||||
// expect(isRef(0)).toBe(false)
|
||||
// expect(isRef(1)).toBe(false)
|
||||
// // an object that looks like a ref isn't necessarily a ref
|
||||
// expect(isRef({ value: 0 })).toBe(false)
|
||||
// })
|
||||
expect(isRef(0)).toBe(false)
|
||||
expect(isRef(1)).toBe(false)
|
||||
// an object that looks like a ref isn't necessarily a ref
|
||||
expect(isRef({ value: 0 })).toBe(false)
|
||||
})
|
||||
|
||||
// test('toRef', () => {
|
||||
// const a = reactive({
|
||||
// x: 1
|
||||
// })
|
||||
// const x = toRef(a, 'x')
|
||||
// expect(isRef(x)).toBe(true)
|
||||
// expect(x.value).toBe(1)
|
||||
test('toRef', () => {
|
||||
const a = reactive({
|
||||
x: 1
|
||||
})
|
||||
const x = toRef(a, 'x')
|
||||
expect(isRef(x)).toBe(true)
|
||||
expect(x.value).toBe(1)
|
||||
|
||||
// // source -> proxy
|
||||
// a.x = 2
|
||||
// expect(x.value).toBe(2)
|
||||
// source -> proxy
|
||||
a.x = 2
|
||||
expect(x.value).toBe(2)
|
||||
|
||||
// // proxy -> source
|
||||
// x.value = 3
|
||||
// expect(a.x).toBe(3)
|
||||
// proxy -> source
|
||||
x.value = 3
|
||||
expect(a.x).toBe(3)
|
||||
|
||||
// // reactivity
|
||||
// let dummyX
|
||||
// effect(() => {
|
||||
// dummyX = x.value
|
||||
// })
|
||||
// expect(dummyX).toBe(x.value)
|
||||
// reactivity
|
||||
let dummyX
|
||||
effect(() => {
|
||||
dummyX = x.value
|
||||
})
|
||||
expect(dummyX).toBe(x.value)
|
||||
|
||||
// // mutating source should trigger effect using the proxy refs
|
||||
// a.x = 4
|
||||
// expect(dummyX).toBe(4)
|
||||
// mutating source should trigger effect using the proxy refs
|
||||
a.x = 4
|
||||
expect(dummyX).toBe(4)
|
||||
|
||||
// // should keep ref
|
||||
// const r = { x: ref(1) }
|
||||
// expect(toRef(r, 'x')).toBe(r.x)
|
||||
// })
|
||||
// should keep ref
|
||||
const r = { x: ref(1) }
|
||||
expect(toRef(r, 'x')).toBe(r.x)
|
||||
})
|
||||
|
||||
// test('toRef default value', () => {
|
||||
// const a: { x: number | undefined } = { x: undefined }
|
||||
// const x = toRef(a, 'x', 1)
|
||||
// expect(x.value).toBe(1)
|
||||
test('toRef default value', () => {
|
||||
const a: { x: number | undefined } = { x: undefined }
|
||||
const x = toRef(a, 'x', 1)
|
||||
expect(x.value).toBe(1)
|
||||
|
||||
// a.x = 2
|
||||
// expect(x.value).toBe(2)
|
||||
a.x = 2
|
||||
expect(x.value).toBe(2)
|
||||
|
||||
// a.x = undefined
|
||||
// expect(x.value).toBe(1)
|
||||
// })
|
||||
a.x = undefined
|
||||
expect(x.value).toBe(1)
|
||||
})
|
||||
|
||||
// test('toRefs', () => {
|
||||
// const a = reactive({
|
||||
// x: 1,
|
||||
// y: 2
|
||||
// })
|
||||
test('toRefs', () => {
|
||||
const a = reactive({
|
||||
x: 1,
|
||||
y: 2
|
||||
})
|
||||
|
||||
// const { x, y } = toRefs(a)
|
||||
const { x, y } = toRefs(a)
|
||||
|
||||
// expect(isRef(x)).toBe(true)
|
||||
// expect(isRef(y)).toBe(true)
|
||||
// expect(x.value).toBe(1)
|
||||
// expect(y.value).toBe(2)
|
||||
expect(isRef(x)).toBe(true)
|
||||
expect(isRef(y)).toBe(true)
|
||||
expect(x.value).toBe(1)
|
||||
expect(y.value).toBe(2)
|
||||
|
||||
// // source -> proxy
|
||||
// a.x = 2
|
||||
// a.y = 3
|
||||
// expect(x.value).toBe(2)
|
||||
// expect(y.value).toBe(3)
|
||||
// source -> proxy
|
||||
a.x = 2
|
||||
a.y = 3
|
||||
expect(x.value).toBe(2)
|
||||
expect(y.value).toBe(3)
|
||||
|
||||
// // proxy -> source
|
||||
// x.value = 3
|
||||
// y.value = 4
|
||||
// expect(a.x).toBe(3)
|
||||
// expect(a.y).toBe(4)
|
||||
// proxy -> source
|
||||
x.value = 3
|
||||
y.value = 4
|
||||
expect(a.x).toBe(3)
|
||||
expect(a.y).toBe(4)
|
||||
|
||||
// // reactivity
|
||||
// let dummyX, dummyY
|
||||
// effect(() => {
|
||||
// dummyX = x.value
|
||||
// dummyY = y.value
|
||||
// })
|
||||
// expect(dummyX).toBe(x.value)
|
||||
// expect(dummyY).toBe(y.value)
|
||||
// reactivity
|
||||
let dummyX, dummyY
|
||||
effect(() => {
|
||||
dummyX = x.value
|
||||
dummyY = y.value
|
||||
})
|
||||
expect(dummyX).toBe(x.value)
|
||||
expect(dummyY).toBe(y.value)
|
||||
|
||||
// // mutating source should trigger effect using the proxy refs
|
||||
// a.x = 4
|
||||
// a.y = 5
|
||||
// expect(dummyX).toBe(4)
|
||||
// expect(dummyY).toBe(5)
|
||||
// })
|
||||
// mutating source should trigger effect using the proxy refs
|
||||
a.x = 4
|
||||
a.y = 5
|
||||
expect(dummyX).toBe(4)
|
||||
expect(dummyY).toBe(5)
|
||||
})
|
||||
|
||||
// test('toRefs should warn on plain object', () => {
|
||||
// toRefs({})
|
||||
// expect(`toRefs() expects a reactive object`).toHaveBeenWarned()
|
||||
// })
|
||||
test('toRefs should warn on plain object', () => {
|
||||
toRefs({})
|
||||
expect(`toRefs() expects a reactive object`).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
// test('toRefs should warn on plain array', () => {
|
||||
// toRefs([])
|
||||
// expect(`toRefs() expects a reactive object`).toHaveBeenWarned()
|
||||
// })
|
||||
test('toRefs should warn on plain array', () => {
|
||||
toRefs([])
|
||||
expect(`toRefs() expects a reactive object`).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
// test('toRefs reactive array', () => {
|
||||
// const arr = reactive(['a', 'b', 'c'])
|
||||
// const refs = toRefs(arr)
|
||||
test('toRefs reactive array', () => {
|
||||
const arr = reactive(['a', 'b', 'c'])
|
||||
const refs = toRefs(arr)
|
||||
|
||||
// expect(Array.isArray(refs)).toBe(true)
|
||||
expect(Array.isArray(refs)).toBe(true)
|
||||
|
||||
// refs[0].value = '1'
|
||||
// expect(arr[0]).toBe('1')
|
||||
refs[0].value = '1'
|
||||
expect(arr[0]).toBe('1')
|
||||
|
||||
// arr[1] = '2'
|
||||
// expect(refs[1].value).toBe('2')
|
||||
// })
|
||||
arr[1] = '2'
|
||||
expect(refs[1].value).toBe('2')
|
||||
})
|
||||
|
||||
// test('customRef', () => {
|
||||
// let value = 1
|
||||
// let _trigger: () => void
|
||||
test('customRef', () => {
|
||||
let value = 1
|
||||
let _trigger: () => void
|
||||
|
||||
// const custom = customRef((track, trigger) => ({
|
||||
// get() {
|
||||
// track()
|
||||
// return value
|
||||
// },
|
||||
// set(newValue: number) {
|
||||
// value = newValue
|
||||
// _trigger = trigger
|
||||
// }
|
||||
// }))
|
||||
const custom = customRef((track, trigger) => ({
|
||||
get() {
|
||||
track()
|
||||
return value
|
||||
},
|
||||
set(newValue: number) {
|
||||
value = newValue
|
||||
_trigger = trigger
|
||||
}
|
||||
}))
|
||||
|
||||
// expect(isRef(custom)).toBe(true)
|
||||
expect(isRef(custom)).toBe(true)
|
||||
|
||||
// let dummy
|
||||
// effect(() => {
|
||||
// dummy = custom.value
|
||||
// })
|
||||
// expect(dummy).toBe(1)
|
||||
let dummy
|
||||
effect(() => {
|
||||
dummy = custom.value
|
||||
})
|
||||
expect(dummy).toBe(1)
|
||||
|
||||
// custom.value = 2
|
||||
// // should not trigger yet
|
||||
// expect(dummy).toBe(1)
|
||||
custom.value = 2
|
||||
// should not trigger yet
|
||||
expect(dummy).toBe(1)
|
||||
|
||||
// _trigger!()
|
||||
// expect(dummy).toBe(2)
|
||||
// })
|
||||
_trigger!()
|
||||
expect(dummy).toBe(2)
|
||||
})
|
||||
|
||||
// test('should not trigger when setting value to same proxy', () => {
|
||||
// const obj = reactive({ count: 0 })
|
||||
test('should not trigger when setting value to same proxy', () => {
|
||||
const obj = reactive({ count: 0 })
|
||||
|
||||
// const a = ref(obj)
|
||||
// const spy1 = jest.fn(() => a.value)
|
||||
const a = ref(obj)
|
||||
const spy1 = vi.fn(() => a.value)
|
||||
|
||||
// effect(spy1)
|
||||
effect(spy1)
|
||||
|
||||
// a.value = obj
|
||||
// expect(spy1).toBeCalledTimes(1)
|
||||
a.value = obj
|
||||
expect(spy1).toBeCalledTimes(1)
|
||||
|
||||
// const b = shallowRef(obj)
|
||||
// const spy2 = jest.fn(() => b.value)
|
||||
const b = shallowRef(obj)
|
||||
const spy2 = vi.fn(() => b.value)
|
||||
|
||||
// effect(spy2)
|
||||
effect(spy2)
|
||||
|
||||
// b.value = obj
|
||||
// expect(spy2).toBeCalledTimes(1)
|
||||
// })
|
||||
b.value = obj
|
||||
expect(spy2).toBeCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user