UNPKG

@vitarx/responsive

Version:
233 lines (232 loc) 7.33 kB
var _a, _b; import { isObject } from '@vitarx/utils'; import { Depend } from '../../../depend/index.js'; import { DEEP_SIGNAL_SYMBOL, isMarkNotSignal, isRefSignal, isSignal, REF_SIGNAL_SYMBOL, SIGNAL_RAW_VALUE_SYMBOL, SIGNAL_SYMBOL, SignalManager } from '../../core/index.js'; import { reactive } from '../reactive/index.js'; /** * # `Ref`值代理对象,用于代理一个值,使其成为响应式变量。 * * @template T - 任意类型 * @template Deep - 是否深度代理 * @remarks * 该对象的`value`属性是响应式的,当其值发生变化时,会触发监听器的回调函数。 * * @example * ```ts * const count = new Ref(0) // 或使用助手函数 ref(0) * count.value++ // count.value 的值变为1 * ``` */ export class Ref { /** * 创建Ref值信号对象 * * @param {T} value - 需要被包装为响应式的值 * @param {SignalOptions} [options] - 响应式配置选项 * @param {boolean} [options.deep=true] - 是否深度代理嵌套对象 * @param {CompareFunction} [options.compare=Object.is] - 值比较函数,用于决定是否触发更新 */ constructor(value, options) { /** * 标识为响应式信号对象 * @readonly */ Object.defineProperty(this, _a, { enumerable: true, configurable: true, writable: true, value: true }); /** * 标识为值引用类型的响应式信号 * @readonly */ Object.defineProperty(this, _b, { enumerable: true, configurable: true, writable: true, value: true }); /** * 响应式配置选项 * @private */ Object.defineProperty(this, "_options", { enumerable: true, configurable: true, writable: true, value: void 0 }); /** * 标识当前值是否需要被代理为响应式对象 * @private */ Object.defineProperty(this, "_shouldProxyValue", { enumerable: true, configurable: true, writable: true, value: false }); /** * 存储被代理后的子响应式对象 * @private */ Object.defineProperty(this, "_reactiveValue", { enumerable: true, configurable: true, writable: true, value: void 0 }); /** * 存储原始值的内部属性 * * @private */ Object.defineProperty(this, "_value", { enumerable: true, configurable: true, writable: true, value: void 0 }); if (isRefSignal(value)) throw new Error('Cannot set value of Ref to Ref'); this._options = { compare: options?.compare ?? Object.is, deep: options?.deep ?? true }; this._value = value; this.evaluateProxyNeeded(); } /** * 获取响应式值 * * 根据配置和值类型,可能返回原始值或响应式代理对象: * - 如果已存在响应式代理对象,则直接返回 * - 如果需要代理且尚未创建代理,则创建新的响应式代理 * - 否则返回原始值并追踪依赖 * * @returns {RefValue<T, Deep>} 响应式值或原始值 */ get value() { if (this._reactiveValue) { return this._reactiveValue; } else if (this._shouldProxyValue) { this._reactiveValue = reactive(this._value, this._options); SignalManager.bindParent(this._reactiveValue, this, 'value'); this._shouldProxyValue = false; return this._reactiveValue; } Depend.track(this, 'value'); return this._value; } /** * 获取原始值目标值 * * @private * @returns {T} 原始值 */ get [(_a = SIGNAL_SYMBOL, _b = REF_SIGNAL_SYMBOL, SIGNAL_RAW_VALUE_SYMBOL)]() { Depend.track(this, 'value'); return this._value; } /** * 设置新值并触发更新 * * 当新值与旧值不同时: * - 清理旧的响应式代理(如果存在) * - 更新内部值 * - 重新评估是否需要代理 * - 通知观察者和父级信号 * * @param {T} newValue - 要设置的新值 * @throws {Error} 如果尝试设置值为Ref对象,则抛出异常 */ set value(newValue) { if (isRefSignal(newValue)) throw new Error('Cannot set value of Ref to Ref'); if (this._options.compare(this._value, newValue)) return; // 清理旧的响应式代理 if (this._reactiveValue) { SignalManager.unbindParent(this._reactiveValue, this, 'value'); this._reactiveValue = undefined; } this._value = newValue; this.evaluateProxyNeeded(); SignalManager.notifySubscribers(this, 'value'); } /** * 是否深度代理标识 * * @returns {Deep} 当前实例的深度代理配置 */ get [DEEP_SIGNAL_SYMBOL]() { return this._options.deep; } /** * 定义当对象需要转换成原始值时的行为 * * 根据不同的转换提示返回适当的值: * - 'number': 返回值本身,尝试进行数值转换 * - 'string': 调用toString方法 * - 'default': 返回值本身 * * @param {string} hint - 转换提示类型 * @returns {any} 根据提示类型转换后的原始值 */ [Symbol.toPrimitive](hint) { switch (hint) { case 'number': return this.value; case 'string': return this.toString(); case 'default': return this.value; } } /** * 将引用的目标值转换为字符串 * * 如果目标值有`toString`方法,则会返回目标值的字符串形式, * 否则返回格式化的类型描述。 * * @returns {string} 字符串表示 * @override */ toString() { Depend.track(this, 'value'); if (this._value?.toString) { return this._value.toString(); } else { return `[Object Ref<${typeof this._value}>]`; } } /** * 重新评估当前值是否需要代理 * * 根据以下条件决定是否需要将value转换为响应式对象: * - deep配置为true(启用深度响应) * - 当前值是对象类型 * - 当前值未被标记为非响应式对象 * * @private */ evaluateProxyNeeded() { this._shouldProxyValue = this._options.deep && isObject(this._value) && !isMarkNotSignal(this._value) && !isSignal(!this._value); } /** * 手动触发value值的更新事件 * * 即使值没有发生变化,也会强制触发更新通知。 * 这在一些特殊场景下很有用,比如更新了浅层Ref的深层值且希望触发监听器。 */ forceUpdate() { SignalManager.notifySubscribers(this, 'value'); } }