| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- import {
- reactive,
- readonly,
- toRaw,
- ReactiveFlags,
- Target,
- readonlyMap,
- reactiveMap,
- shallowReactiveMap,
- shallowReadonlyMap
- } from './reactive'
- import { TrackOpTypes, TriggerOpTypes } from './operations'
- import {
- track,
- trigger,
- ITERATE_KEY,
- pauseTracking,
- resetTracking
- } from './effect'
- import {
- isObject,
- hasOwn,
- isSymbol,
- hasChanged,
- isArray,
- isIntegerKey,
- extend,
- makeMap
- } from '@vue/shared'
- import { isRef } from './ref'
- const isNonTrackableKeys = /*#__PURE__*/ makeMap(`__proto__,__v_isRef,__isVue`)
- const builtInSymbols = new Set(
- Object.getOwnPropertyNames(Symbol)
- .map(key => (Symbol as any)[key])
- .filter(isSymbol)
- )
- const get = /*#__PURE__*/ createGetter()
- const shallowGet = /*#__PURE__*/ createGetter(false, true)
- const readonlyGet = /*#__PURE__*/ createGetter(true)
- const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)
- const arrayInstrumentations = /*#__PURE__*/ createArrayInstrumentations()
- function createArrayInstrumentations() {
- const instrumentations: Record<string, Function> = {}
- // instrument identity-sensitive Array methods to account for possible reactive
- // values
- ;(['includes', 'indexOf', 'lastIndexOf'] as const).forEach(key => {
- const method = Array.prototype[key] as any
- instrumentations[key] = function(this: unknown[], ...args: unknown[]) {
- const arr = toRaw(this)
- for (let i = 0, l = this.length; i < l; i++) {
- track(arr, TrackOpTypes.GET, i + '')
- }
- // we run the method using the original args first (which may be reactive)
- const res = method.apply(arr, args)
- if (res === -1 || res === false) {
- // if that didn't work, run it again using raw values.
- return method.apply(arr, args.map(toRaw))
- } else {
- return res
- }
- }
- })
- // instrument length-altering mutation methods to avoid length being tracked
- // which leads to infinite loops in some cases (#2137)
- ;(['push', 'pop', 'shift', 'unshift', 'splice'] as const).forEach(key => {
- const method = Array.prototype[key] as any
- instrumentations[key] = function(this: unknown[], ...args: unknown[]) {
- pauseTracking()
- const res = method.apply(this, args)
- resetTracking()
- return res
- }
- })
- return instrumentations
- }
- function createGetter(isReadonly = false, shallow = false) {
- return function get(target: Target, key: string | symbol, receiver: object) {
- if (key === ReactiveFlags.IS_REACTIVE) {
- return !isReadonly
- } else if (key === ReactiveFlags.IS_READONLY) {
- return isReadonly
- } else if (
- key === ReactiveFlags.RAW &&
- receiver ===
- (isReadonly
- ? shallow
- ? shallowReadonlyMap
- : readonlyMap
- : shallow
- ? shallowReactiveMap
- : reactiveMap
- ).get(target)
- ) {
- return target
- }
- const targetIsArray = isArray(target)
- if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
- return Reflect.get(arrayInstrumentations, key, receiver)
- }
- const res = Reflect.get(target, key, receiver)
- if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
- return res
- }
- if (!isReadonly) {
- track(target, TrackOpTypes.GET, key)
- }
- if (shallow) {
- return res
- }
- if (isRef(res)) {
- // ref unwrapping - does not apply for Array + integer key.
- const shouldUnwrap = !targetIsArray || !isIntegerKey(key)
- return shouldUnwrap ? res.value : res
- }
- if (isObject(res)) {
- // Convert returned value into a proxy as well. we do the isObject check
- // here to avoid invalid value warning. Also need to lazy access readonly
- // and reactive here to avoid circular dependency.
- return isReadonly ? readonly(res) : reactive(res)
- }
- return res
- }
- }
- const set = /*#__PURE__*/ createSetter()
- const shallowSet = /*#__PURE__*/ createSetter(true)
- function createSetter(shallow = false) {
- return function set(
- target: object,
- key: string | symbol,
- value: unknown,
- receiver: object
- ): boolean {
- let oldValue = (target as any)[key]
- if (!shallow) {
- value = toRaw(value)
- oldValue = toRaw(oldValue)
- if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
- oldValue.value = value
- return true
- }
- } else {
- // in shallow mode, objects are set as-is regardless of reactive or not
- }
- const hadKey =
- isArray(target) && isIntegerKey(key)
- ? Number(key) < target.length
- : hasOwn(target, key)
- const result = Reflect.set(target, key, value, receiver)
- // don't trigger if target is something up in the prototype chain of original
- if (target === toRaw(receiver)) {
- if (!hadKey) {
- trigger(target, TriggerOpTypes.ADD, key, value)
- } else if (hasChanged(value, oldValue)) {
- trigger(target, TriggerOpTypes.SET, key, value, oldValue)
- }
- }
- return result
- }
- }
- function deleteProperty(target: object, key: string | symbol): boolean {
- const hadKey = hasOwn(target, key)
- const oldValue = (target as any)[key]
- const result = Reflect.deleteProperty(target, key)
- if (result && hadKey) {
- trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
- }
- return result
- }
- function has(target: object, key: string | symbol): boolean {
- const result = Reflect.has(target, key)
- if (!isSymbol(key) || !builtInSymbols.has(key)) {
- track(target, TrackOpTypes.HAS, key)
- }
- return result
- }
- function ownKeys(target: object): (string | symbol)[] {
- track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY)
- return Reflect.ownKeys(target)
- }
- export const mutableHandlers: ProxyHandler<object> = {
- get,
- set,
- deleteProperty,
- has,
- ownKeys
- }
- export const readonlyHandlers: ProxyHandler<object> = {
- get: readonlyGet,
- set(target, key) {
- if (__DEV__) {
- console.warn(
- `Set operation on key "${String(key)}" failed: target is readonly.`,
- target
- )
- }
- return true
- },
- deleteProperty(target, key) {
- if (__DEV__) {
- console.warn(
- `Delete operation on key "${String(key)}" failed: target is readonly.`,
- target
- )
- }
- return true
- }
- }
- export const shallowReactiveHandlers = /*#__PURE__*/ extend(
- {},
- mutableHandlers,
- {
- get: shallowGet,
- set: shallowSet
- }
- )
- // Props handlers are special in the sense that it should not unwrap top-level
- // refs (in order to allow refs to be explicitly passed down), but should
- // retain the reactivity of the normal readonly object.
- export const shallowReadonlyHandlers = /*#__PURE__*/ extend(
- {},
- readonlyHandlers,
- {
- get: shallowReadonlyGet
- }
- )
|