编程 写Vue大篇幅的ref、computed,而reactive为何少见?

2024-11-18 21:26:50 +0800 CST views 493

写Vue大篇幅的ref、computed,而reactive为何少见?

在使用Vue3开发项目时,经常会遇到一个vue文件中定义了不下20个ref和computed,这不仅让代码显得冗长,也使得开发者感到困惑。那么有没有更好的解决方案呢?Vue设计ref、computed、reactive的初衷又是什么?让我们从源码和实践中找答案!

理解定义的初衷

Vue官方文档在介绍响应式对象的定义时,顺序为ref、computed、reactive、readonly,这与项目开发中它们的使用频率大致相符。

ref

我们先抛出一个问题:如果给ref b传入的参数a本身也是一个ref类型的值,是通过b.value还是b.value.value来获取值?

const a = ref(1);
const b= ref(a);

// console.log(b.value);
// console.log(a.value);

ref的定义:

function ref<T>(value: T): Ref<UnwrapRef<T>> {
  interface Ref<T> { value: T }
}

传入的value可以是简单值或复杂对象,返回的类型是Ref<UnwrapRef<T>>,确定结果是{ value: x }格式对象。

export type UnwrapRef<T> =
  T extends ShallowRef<infer V>
    ? V
    : T extends Ref<infer V>
      ? UnwrapRefSimple<V>
      : UnwrapRefSimple<T>

通过上述代码分析,ref函数需要对原始值进行处理:如果rawValue是Ref类型,则直接返回rawValue,不做处理。因此,代码中的a === b

const a = ref({ x: 1, y: 2 });
const b= ref(a);

如果rawValue是非Ref类型,则返回RefImpl的实例化对象。

reactive

reactive方法用于返回对象的深层响应式代理,适用于对象类型,默认会解包ref类型。

const count = ref(1);
const obj = reactive({ count });
console.log(obj.count === count.value); // true

需要注意,如果reactive包含集合对象,集合本身不会被解包。

const books = reactive([ref('Vue 3 Guide')]);
console.log(books[0].value);

computed

computed接受一个getter函数,返回一个只读的响应式ref对象。其定义如下:

function computed<T>(
  getter: (oldValue: T | undefined) => T,
  debuggerOptions?: DebuggerOptions
): Readonly<Ref<Readonly<T>>>

也可以定义可读可写的computed

function computed<T>(
  options: {
    get: (oldValue: T | undefined) => T;
    set: (value: T) => void;
  },
  debuggerOptions?: DebuggerOptions
): Ref<T>

示例:

const count = ref(1);
const plusOne = computed({
  get: () => count.value + 1,
  set: (val) => {
    count.value = val - 1;
  },
});

在这个例子中,plusOne允许我们通过set方法修改count的值,而这个特性在某些场景下非常有用。

watch监听Ref类型值的策略

watch函数可以监听Ref类型的值,不同的监听策略如下:

  • watch(refData, ...)可以触发回调函数,但不会监听深层属性的变化;
  • watch(refData.value, ...)可以监听深层属性的变化,但不能监听refData.value本身的变化。

深入研究源码可以发现,watch函数通过getter来取值,当source是Ref类型时,直接返回source.value

if (isRef(source)) {
  getter = () => source.value;
} else if (isReactive(source)) {
  getter = () => reactiveGetter(source);
}

什么时候使用computed

computed的优势在于,当getter函数中的响应式数据发生变化时,它会自动更新结果。

const name = ref('李磊');
const detail = reactive({ phone: 10000 });
const cptValue = computed(() => {
  return `${name.value}: ${detail.phone}`;
});

watch(cptValue, (newValue) => {
  console.log(newValue);
});

如何简化组件的复杂度

在Vue组件中,当功能复杂度增加时,定义的ref和computed会迅速增加。为了简化,我们可以将逻辑封装到外部模块,例如使用dialog.ts模块集中管理逻辑:

export const useDialog = () => {
  const title = ref('');
  const visible = ref(false);
  // 其他逻辑...
  
  return {
    title,
    visible,
  };
};

这样,在Vue文件中可以直接引用逻辑模块:

const { title, visible } = useDialog();

总结

  • ref适用于简单值的定义,且可以作为响应式引用的基础。
  • computed用于需要依赖其他响应式数据进行动态计算的场景。
  • reactive适用于对象类型的深度响应式需求,但在项目中使用较少。

通过将复杂的逻辑分离到独立的模块中,不仅能减少代码重复,还能提升项目的可维护性。

复制全文 生成海报 Vue 前端开发 响应式编程

推荐文章

如何在Vue中处理动态路由?
2024-11-19 06:09:50 +0800 CST
Manticore Search:高性能的搜索引擎
2024-11-19 03:43:32 +0800 CST
JS中 `sleep` 方法的实现
2024-11-19 08:10:32 +0800 CST
php客服服务管理系统
2024-11-19 06:48:35 +0800 CST
JavaScript数组 splice
2024-11-18 20:46:19 +0800 CST
淘宝npm镜像使用方法
2024-11-18 23:50:48 +0800 CST
Elasticsearch 文档操作
2024-11-18 12:36:01 +0800 CST
php腾讯云发送短信
2024-11-18 13:50:11 +0800 CST
Vue3中如何进行性能优化?
2024-11-17 22:52:59 +0800 CST
windows下mysql使用source导入数据
2024-11-17 05:03:50 +0800 CST
Vue中如何处理异步更新DOM?
2024-11-18 22:38:53 +0800 CST
16.6k+ 开源精准 IP 地址库
2024-11-17 23:14:40 +0800 CST
初学者的 Rust Web 开发指南
2024-11-18 10:51:35 +0800 CST
前端如何给页面添加水印
2024-11-19 07:12:56 +0800 CST
2024年微信小程序开发价格概览
2024-11-19 06:40:52 +0800 CST
程序员茄子在线接单