Vue3+TS学习实践(二)

395 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情

上一篇章中我们简单的介绍了createApp函数,ref相关的一些API,以及v-model在父子组件传参上的写法,本篇章打算总结一下reactive相关的API。

Vue3+TS学习实践(一)

往期vite项目搭建过程地址

reactive

  • 返回对象的响应式副本(对象、数组...)
  • 如果我们使用ref绑定复杂数据类型(例如const data = ref<string[]>([])),在Vue3的底层其实也是使用reactive来定义的。

按照惯例,我们简单看看reactive的类型定义

1.png

export declare function reactive<T extends object>(target: T): UnwrapNestedRefs<T>

上面的注释对reactive进行了一下简单描述,大体意思如下:reactive会创建一个原始对象的副本。响应式的转化是深层次的,他会影响所有嵌套属性,在基于ES2015 Proxy的实现中,返回的 proxy 是等于原始对象的。建议只使用响应式 proxy,避免依赖原始对象。响应式对象也会自动打开其中包含的参照,因此不需要使用.value当访问和改变它们的值(当将 ref 分配给 reactive property 时,ref 将被自动解包)。

从它的定义上可以看出它是不可以绑定普通的数据类型

数组的异步赋值问题

碰到过这样一个问题,需求是在下拉框中需要使用后台给的数据,当我们在进入页面后对使用reactive定义的数组进行赋值,发现其失去了响应式(页面上的效果是下拉框中没有值了)。

这样直接赋值页面是不会产生变化的,因为会脱离响应式

let optionsData = reactive<string[]>([]);

onMounted(() => {
  optionsData = ['v1', 'v2', 'v3'];
})

当然如果是一个简单的数组,也可以使用ref来定义,这样在使用时通过.value来赋值也是可以的。

方法一、使用push

let optionsData = reactive<string[]>([]);

onMounted(() => {
  const data = ['v1', 'v2', 'v3'];
  optionsData.push(...data);
});

方法二、在数组外面包裹一层对象

type Data = {
  optionsData: Array<string>
}
let data = reactive<Data>({
   list: []
});
onMounted(() => {
  data.list = ['v1', 'v2', 'v3'];
});

推荐使用方法二

readonly

接受一个对象 (响应式或纯对象) 或 ref 并返回原始对象的只读代理。只读代理是深层的:任何被访问的嵌套 property 也是只读的。

import { reactive, readonly } from 'vue';
const data = reactive({
  count: 1,
});
const copyData = readonly(data);

data.count++;
copyData.count++;// 变更副本将失败并导致警告-报错

如果任何 property 使用了 ref,当它通过代理访问时,则被自动解包

const raw = {
  count: ref(123)
}
const copy = readonly(raw)
console.log(raw.count.value) // 123
console.log(copy.count) // 123

shallowReactive

创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (暴露原始值)。

const state = shallowReactive({
  foo: 1,
  nested: {
    bar: 2
  }
})
// 改变 state 本身的性质是响应式的
state.foo++
// ...但是不转换嵌套对象
isReactive(state.nested) // false
state.nested.bar++ // 非响应式

其他的API

  • isProxy: 检查对象是否是由 reactive 或 readonly 创建的 proxy
  • isReactive: 检查对象是否是由 reactive 创建的响应式代理
const data = reactive({
  count: 1
})
console.log(isReactive(data)) // true

如果该代理是由 readonly 创建的,但是包裹了由 reactive 创建的另一个代理,它也会返回 true

const data = reactive({
  count: 1
})
// 从普通对象创建的只读proxy
const plain = readonly({
  name: '张三'
});
console.log(isReactive(plain)) // false

// 从响应式 proxy 创建的只读 proxy
const dataCopy = readonly(data)
console.log(isReactive(dataCopy)) // -> true
  • isReadonly:检查对象是否是由 readonly 创建的只读代理
  • toRaw: 返回 reactive 或 readonly 代理的原始对象
  • shallowReactive: 创建一个响应式代理,它只跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (暴露原始值)-(类似于shallowRef只跟踪自身.value的变化)。
  • shallowReadonly: 创建一个 proxy,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换 (暴露原始值)
const data = shallowReadonly({
  foo: 1,
  nested: {
    bar: 2
  }
})
// 改变 data 本身的 property 将警告,报错
data.foo++
// ...但适用于嵌套对象
isReadonly(data.nested) // false -> 不是由readonly创建的只读代理
data.nested.bar++ // 可以使用