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