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

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

写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 前端开发 响应式编程

推荐文章

java MySQL如何获取唯一订单编号?
2024-11-18 18:51:44 +0800 CST
如何在Vue3中处理全局状态管理?
2024-11-18 19:25:59 +0800 CST
如何优化网页的 SEO 架构
2024-11-18 14:32:08 +0800 CST
Vue3中如何处理SEO优化?
2024-11-17 08:01:47 +0800 CST
HTML5的 input:file上传类型控制
2024-11-19 07:29:28 +0800 CST
Go语言中的`Ring`循环链表结构
2024-11-19 00:00:46 +0800 CST
智慧加水系统
2024-11-19 06:33:36 +0800 CST
企业官网案例-芊诺网络科技官网
2024-11-18 11:30:20 +0800 CST
用 Rust 玩转 Google Sheets API
2024-11-19 02:36:20 +0800 CST
Rust开发笔记 | Rust的交互式Shell
2024-11-18 19:55:44 +0800 CST
WebSocket在消息推送中的应用代码
2024-11-18 21:46:05 +0800 CST
Golang 中你应该知道的 noCopy 策略
2024-11-19 05:40:53 +0800 CST
在 Rust 中使用 OpenCV 进行绘图
2024-11-19 06:58:07 +0800 CST
前端开发中常用的设计模式
2024-11-19 07:38:07 +0800 CST
Python上下文管理器:with语句
2024-11-19 06:25:31 +0800 CST
html文本加载动画
2024-11-19 06:24:21 +0800 CST
程序员茄子在线接单