◆

Vue3 响应式 API:reactive、readonly、toRaw 详解

2023-02-02 01:370

reactive

  • 返回对象的响应式转换后的数据。响应式转换是“深层”的——它影响所有嵌套 property。
  • 在基于 ES2015 Proxy 的实现中,返回的 proxy 是不等于原始对象的。建议只使用响应式 proxy,避免依赖原始对象。
  • 通过使用 Proxy 来实现对对象内部所有数据的劫持, 并通过 Reflect 操作对象内部数据

    tip

    reactive 将解包所有深层的 refs,同时维持 ref 的响应性。 当将 ref 分配给 reactive property 时,ref 将被自动解包。

如果使用 reactive 定义基础数据类型,则返回的仍是基础数据类型 :::

<template>
  <div>reactiveBool 的值:{{ reactiveBool }}</div>

  <div>reactiveTest.a 的值:{{ reactiveTest.a }}</div>
  <div>reactiveTest.b 的值:{{ reactiveTest.b }}</div>

  <div>reactiveLetTest.a 的值:{{ reactiveLetTest.a }}</div>
  <div>reactiveLetTest.b 的值:{{ reactiveLetTest.b }}</div>

  <div>refTest.a 的值:{{ refTest.a }}</div>
  <div>refTest.b 的值:{{ refTest.b }}</div>

  <div>reactiveRefTest.a 的值:{{ reactiveRefTest.a }}</div>
  <div>reactiveRefTest.b 的值:{{ reactiveRefTest.b }}</div>

  <div>reactiveObj.a 的值:{{ reactiveObj.a }}</div>
  <div>reactiveObj.mytest 的值:{{ reactiveObj.mytest }}</div>
  <div>refParam 的值:{{ refParam }}</div>
</template>

<script setup>
import { reactive, ref } from 'vue';

let reactiveBool = reactive(false);

const reactiveTest = reactive({
  a: 1,
  b: 2,
});

let reactiveLetTest = reactive({
  a: 1,
  b: 2,
});

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

const reactiveRefTest = reactive(refTest);

const refParam = ref('mytest');

const reactiveObj = reactive({ a: 1 });
reactiveObj.mytest = refParam;

setTimeout(() => {
  reactiveBool = true;

  reactiveTest.a = 'reactive 更新了 a 的值';
  reactiveLetTest.a = 'reactiveLetTest 更新了 a 的值';
  refTest.a = 'refTest 更新了 a 的值';
  reactiveRefTest.a = 'reactiveRefTest 更新了 a 的值';

  refParam.value = '使用 ref 去改变 refParam 的值';
}, 1500);

setTimeout(() => {
  // 会导致下方的失效 这样重新赋值后会,reactiveLetTest 会变成普通对象 失去响应式的效果
  // reactiveLetTest = {
  // 	a: 'reactiveLetTest 更新了 a 的值',
  // 	b: 'reactiveLetTest 更新了 b 的值'
  // }
  // 这个效果和上面的效果一致
  // reactiveLetTest = reactive({
  // 	a: 'reactiveLetTest 更新了 a 的值',
  // 	b: 'reactiveLetTest 更新了 b 的值'
  // })
  reactiveObj.mytest = '使用 reactiveObj 去改变 refParam 的值';
}, 3000);

setTimeout(() => {
  reactiveLetTest.b = 'reactiveLetTest 更新了 b 的值';
}, 5000);
</script>

readonly

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

tip

下面的示例 readonlyVars,readonlyRefs、readonlyReactives 这三个值是无法改变的

<template>
  <div>normalVar 的值:{{ normalVar }}</div>
  <div>normalRef 的值:{{ normalRef }}</div>
  <div>normalReactive.a 的值:{{ normalReactive.a }}</div>
  <div>normalReactive.b.c 的值:{{ normalReactive.b.c }}</div>

  <hr />

  <div>readonlyVar 的值:{{ readonlyVar }}</div>
  <div>readonlyRef 的值:{{ readonlyRef }}</div>
  <div>readonlyReactive.a 的值:{{ readonlyReactive.a }}</div>
  <div>readonlyReactive.b.c 的值:{{ readonlyReactive.b.c }}</div>

  <hr />

  <div>readonlyVars 的值:{{ readonlyVars }}</div>
  <div>readonlyRefs 的值:{{ readonlyRefs }}</div>
  <div>readonlyReactives.a 的值:{{ readonlyReactives.a }}</div>
  <div>readonlyReactives.b.c 的值:{{ readonlyReactives.b.c }}</div>
</template>

<script setup>
import { reactive, readonly, ref } from 'vue';

let normalVar = 'readonlyVar';
let normalRef = ref('readonlyRef');
let normalReactive = reactive({
  a: 'readonlyReactive a',
  b: {
    c: 'readonlyReactive c',
  },
});

let readonlyVar = 'readonlyVar';
let readonlyRef = ref('readonlyRef');
let readonlyReactive = reactive({
  a: 'readonlyReactive a',
  b: {
    c: 'readonlyReactive c',
  },
});

let readonlyVars = readonly(readonlyVar);
let readonlyRefs = readonly(readonlyRef);
let readonlyReactives = readonly(readonlyReactive);

setTimeout(() => {
  normalVar = '更改 normalVar 的值';
  normalRef.value = '更改 normalRef 的值';
  normalReactive.a = '更改 normalReactive.a 的值';
  normalReactive.b.c = '更改 normalReactive.b.c 的值';

  readonlyVar = '更改 readonlyVar 的值';
  readonlyRef.value = '更改 readonlyRef 的值';
  readonlyReactive.a = '更改 readonlyReactive.a 的值';
  readonlyReactive.b.c = '更改 readonlyReactive.b.c 的值';
}, 1500);

setTimeout(() => {
  readonlyVars = '1更改 readonlyVar 的值';
  readonlyRefs.value = '1更改 readonlyRef 的值';
  readonlyReactives.a = '1更改 readonlyReactive.a 的值';
  readonlyReactives.b.c = '1更改 readonlyReactive.b.c 的值';
}, 2000);
</script>

isProxy

检查对象是否是由 reactive 或 readonly 创建的 proxy。

<script setup>
import { isProxy, reactive, readonly, ref } from 'vue';

const normal = ref(1);
const proxyTest = new Proxy(
  {
    name: '小红',
    age: 15,
  },
  {},
);
const refTest = ref(1);
const reactiveTest = reactive({
  a: 1,
});
const readonlyTest = readonly(reactive({ a: 1, b: { c: 3 } }));

// false
console.log('normal', isProxy(normal));
// false
console.log('normalProxy', isProxy(proxyTest));
// false
console.log('refTest', isProxy(refTest));
// true
console.log('reactiveTest', isProxy(reactiveTest));
// true
console.log('readonlyTest', isProxy(readonlyTest));
</script>

isReactive

检查对象是否是由 reactive 创建的响应式代理。 如果该代理是 readonly 创建的,但包裹了由 reactive 创建的另一个代理,它也会返回 true。

<script setup>
import { isProxy, isReactive, reactive, readonly, ref } from 'vue';

const normal = ref(1);
const refTest = ref(1);
const reactiveTest = reactive({
  a: 1,
});
const readonlyTest = readonly(reactive({ a: 1, b: { c: 3 } }));

// false
console.log('normal', isReactive(normal));
// false
console.log('refTest', isReactive(refTest));
// true
console.log('reactiveTest', isReactive(reactiveTest));
// true
console.log('readonlyTest', isReactive(readonlyTest));
</script>

isReadOnly

检查对象是否是由 readonly 创建的只读代理。

<script setup>
import { isProxy, isReadonly, reactive, readonly, ref } from 'vue';

const normal = ref(1);
const refTest = ref(1);
const reactiveTest = reactive({
  a: 1,
});
const readonlyTest = readonly(reactive({ a: 1, b: { c: 3 } }));

// false
console.log('normal', isReadonly(normal));
// false
console.log('refTest', isReadonly(refTest));
// false
console.log('reactiveTest', isReadonly(reactiveTest));
// true
console.log('readonlyTest', isReadonly(readonlyTest));
</script>

toRaw

返回 reactive 或 readonly 代理的原始对象。可用于临时读取数据而无需承担代理访问/跟踪的开销,也可用于写入数据而避免触发更改。

<template>
  <p>{{ state }}</p>
  <button @click="myFn">按钮</button>
</template>

<script setup>
import { reactive, ref, toRaw } from 'vue';

let state = reactive({ name: 'lnj', age: 18 });
// 获取state的源数据
let obj2 = toRaw(state);

function myFn() {
  // 获取的源数据更改,不会触发页面更新
  obj2.name = 'zs';
  console.log(obj2); // {name: "zs", age: 18}
}
</script>

shallowReactive

  • shallowReactive 监听了第一层属性的值,一旦发生改变,则更新视图;其他层,虽然值发生了改变,但是视图不会进行更新
  • 相当于只对第一层做了 proxy 代理
  • 如果有一个对象数据, 结构比较深, 但变化时只是外层属性变化,使用 shallowReactive
<template>
  {{ state }}
  <br />
  <br />
  <button @click="change1">更新 state.a 的值</button>
  <br />
  <br />
  <button @click="change2">更新 state.first 下嵌套属性的值</button>
</template>

<script setup>
import { shallowReactive } from 'vue';

const obj = {
  a: 1,
  first: {
    b: 2,
    second: {
      c: 3,
    },
  },
};

const state = shallowReactive(obj);

function change1() {
  state.a = 7;
}

function change2() {
  state.first.b = 8;
  state.first.second.c = 9;
  console.log(state);
}
</script>

shallowReadonly

shallowReadonly 设置了第一层属性的值为只读,其他层可以修改

<template>
  {{ state }}
  <br />
  <br />
  <button @click="change1">更新 state.a 的值</button>
  <br />
  <br />
  <button @click="change2">更新 state.first 下嵌套属性的值</button>
</template>

<script setup>
import { shallowReactive } from 'vue';

const obj = {
  a: 1,
  first: {
    b: 2,
    second: {
      c: 3,
    },
  },
};

const state = shallowReactive(obj);

function change1() {
  state.a = 7;
}

function change2() {
  state.first.b = 8;
  state.first.second.c = 9;
  console.log(state);
}
</script>
💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVEVue3 响应式 API:reactive、readonly、toRaw 详解加载中