@vitarx/responsive
Version:
Vitarx responsive package
134 lines (133 loc) • 5.05 kB
JavaScript
import { toRaw } from '../../core/index.js';
import { Ref } from './ref.js';
/**
* 创建一个响应式引用信号
*
* 创建一个包装对象,使其成为响应式数据源。当引用的值发生变化时,
* 所有依赖于该引用的计算和副作用将自动更新。默认情况下,对嵌套对象进行深度代理。
*
* @template Value - 信号值的类型
* @template Deep - 是否使用深度信号,默认为true
* @param {Value} [value=undefined] - 信号初始值
* @param {object | boolean} [options] - 信号的选项配置,支持直接传入boolean指定deep配置
* @param {boolean} [options.deep=true] - 是否深度代理嵌套对象,为true时会递归代理所有嵌套属性
* @param {function} [options.compare=Object.is] - 值比较函数,用于决定是否触发更新,默认使用Object.is进行比较
* @returns {Ref<Value, Deep>} - 创建的响应式引用信号
* @example
* // 创建一个基本类型的ref
* const count = ref(0)
* console.log(count.value) // 0
* count.value++
* console.log(count.value) // 1
*
* // 创建一个对象ref并使用自定义比较函数
* const user = ref({ name: 'Zhang', age: 25 }, {
* compare: (prev, next) => prev.name === next.name
* })
*
* // 创建一个一个浅层ref
* const shallow = ref({ a:{b:1} }, false)
*
* // 创建一个嵌套对象ref
* const userInfo = ref({ name: 'Zhang', profile: { age: 25 } })
*
* // 嵌套ref
* const count2 = ref(ref(1))
* count2.value++
* console.log(count2.value) // 2
*/
export function ref(value, options) {
if (isRef(value))
return value;
if (typeof options === 'boolean') {
return new Ref(value, { deep: options });
}
return new Ref(value, options);
}
/**
* 创建一个浅响应式引用信号
*
* @template Value - 信号值的类型
* @param {Value} [value=undefined] - 信号初始值
* @param {object} [options] - 信号的选项,包括是否使用深度信号和比较函数
* @param {function} [options.compare=Object.is] - 值比较函数,用于决定是否触发更新
* @returns {Ref<Value, false>} - 浅响应式引用信号对象
* @example
* // 创建一个基本的浅响应式引用
* const count = shallowRef(0)
* console.log(count.value) // 0
* count.value++
* console.log(count.value) // 1
*
* // 展示嵌套对象的响应式行为差异
* const user = shallowRef({ name: 'Zhang', profile: { age: 25 } })
*
* // 修改顶层属性会触发更新
* user.value.name = 'Li' // 会触发更新
*
* // 修改嵌套对象的属性不会触发更新
* user.value.profile.age = 26 // 不会触发更新,因为profile对象没有被代理
* // 更新嵌套对象且触发更新
* user.forceUpdate() // 1. ✅ 修改完成过后直接调用forceUpdate方法
* user.value = { ...user.value, profile: { ...user.value.profile, age: 26 } } // 2. ❌ 虽然会触发,但是不推荐
*
* // 使用自定义比较函数的场景
* const list = shallowRef([1, 2, 3], {
* compare: (prev, next) => prev.length === next.length
* })
*/
export function shallowRef(value, options) {
if (isRef(value))
return value;
return new Ref(value, { ...options, deep: false });
}
/**
* 判断是否为 Ref 对象
*
* 注意和 `isRefSignal` 的区别,`isRef` 只判断是否为 `Ref` 对象,而 `isRefSignal` 是判断对象是否具有响应式的 value 属性
*
* @param {any} val - 任意值
* @return {boolean} 是否为 Ref 对象
* @example
* // 创建一个 Ref 对象
* const count = ref(0)
*
* // isRef 只检查对象是否为 Ref 实例
* console.log(isRef(count)) // true
* console.log(isRef(0)) // false
* console.log(isRefSignal(count)) // true ,因为 Ref 实例也是一个 RefSignal 的实现
*
* // 对于自定义的Ref对象,两者的表现不同
* class CustomRef {
* [SIGNAL_SYMBOL]:true
* [REF_SIGNAL_SYMBOL]:true
* // 省略了构造函数等相关代码,只是为了演示
* get value() {//...}
* set value(newValue) {//...}
* }
* const customRef = new CustomRef(0)
* console.log(isRef(customRef)) // false,因为不是 Ref 实例
* console.log(isRefSignal(customRef)) // true,因为 CustomRef 符合RefSignal的特征
*/
export function isRef(val) {
return val instanceof Ref;
}
/**
* 解除Ref对象的包装,返回其原始值
*
* 在响应式系统中,该函数用于获取Ref对象包装的原始值。如果传入的是普通值,则直接返回该值。
* 这个函数在处理可能是Ref对象或普通值的参数时特别有用,可以统一处理两种情况。
*
* @template T - 值的类型
* @param {T | Ref<T>} ref - 需要解包的值,可以是Ref对象或普通值
* @returns {T} 如果输入是Ref对象,返回其.value值;如果是普通值,则原样返回
* @example
* // 处理Ref对象
* const count = ref(0)
* console.log(unref(count)) // 0 等效于 toRaw(count)
* // 处理普通值
* console.log(unref(100)) // 100
*/
export function unref(ref) {
return toRaw(ref);
}