为什么shallowReactive深层对象变化时,也会响应式地显示在页面上?

319 阅读1分钟

关键词: vue3,typescript

<template>
  <!-- age有响应式,值的改变会更新页面 -->
  <div>{{ obj.age }}</div>
  <!-- info.pos没有响应式,值的改变不会更新页面, -->
  <!-- 但age的改变会导致页面更新,因此info.pos也会更新 -->
  <!-- (造成了info.pos也是响应式的假象) -->
  <div>{{ obj.info.pos }}</div>
</template>
<script lang="ts">
import {
  defineComponent,
  isReactive,
  onMounted,
  shallowReactive,
} from "vue";

export default defineComponent({
  name: "App",
  setup() {
    const obj = shallowReactive({
      age: 100,
      year: 0,
      info: {
        pos: 999,
      },
    });
    console.log("obj>>>>", isReactive(obj.info));
    setInterval(() => {
      // age和info.pos都会响应式地显示在页面上,
      // 本来应该是:age响应式显示,info.pos不会
      obj.age++; 
      obj.info.pos++;
      
      // 改为以下代码即可:
      // // obj.age++; // 注释了obj.age++
      // obj.info.pos++;
      
    }, 1000);

    return {
      obj
    };
  },
});
</script>

原因

shallowReactive() 返回的对象 obj 为浅响应式,它跟踪其自身 property 的响应性(即第一层,如 obj.age );但不执行嵌套对象的深层(如 obj.info.pos )响应式转换 ,会暴露其原始值。

obj 只是不会响应式显示在屏幕,它在程序中还是会一直执行着 ++ 。

obj.age 和 obj.info.pos 通处于一个环境,都被同个Fragment包着,obj.age++触发改变,对应的模板领域会进行更新,所以页面给相当于重新渲染了一遍。如果obj.age没有在模板中用到,其值的变化不会触发重新渲染。

注释掉obj.age++的原因是:取消这个响应性值的变化而导致的页面更新。避免了obj.info.pos 也是响应式的假象。

参考:

  1. Vue3官网:shallowReactive
  2. bilibili:尚硅谷Vue.JS教程快速入门到项目实战(Vue3/VueJS技术详解)