面试官:你知道怎么解决vue2响应式丢失吗?

vue2是如何追踪数据变化形成响应?

概括版:

通过遍历劫持对象的所有属性来实现响应式数据(Object.defineProperty)。

专业版:

1.初始化阶段

  • 在Vue实例化过程中,Vue会遍历数据对象(通常是data属性中定义的数据)的所有属性,通过Object.defineProperty方法为每个属性添加getter和setter函数。这样做的目的是为了在属性被访问或修改时能够进行依赖收集和派发更新。

2.依赖收集

  • 当Vue实例渲染过程中的模板(Template)中读取数据时,对应的属性的getter函数会被触发。Vue会通过一个全局变量(通常称为"依赖收集器"或"Watcher")来追踪这些属性的依赖关系。
  • 每个Watcher对象会记录当前模板中使用的所有响应式数据。
  • 当模板中的数据变化时,Watcher对象会负责通知相关的视图进行更新。

3. 触发更新

  • 当数据发生变化时,数据对象的setter函数会被触发。这时,Vue会通知依赖收集器中的Watcher对象,告诉它们数据已经发生了变化。
  • Watcher对象会执行相应的回调函数,更新视图中依赖这些数据的部分。

4.更新视图

  • 一旦Watcher对象收到数据变化的通知,它会执行相应的回调函数,比如重新渲染模板或执行一些其他的副>作用。
  • Vue通过虚拟DOM(Virtual DOM)来最小化真实DOM的操作,提高性能。当数据变化时,Vue会重新计算虚>>拟DOM与上一次渲染结果的差异,并将这些差异应用到真实DOM上,实现局部更新而不是整体重新渲染。 5.批量更新
  • Vue在更新视图时会进行批量更新。如果多次修改了同一个响应式数据,Vue会将这些更新合并为一次,以提高性能和减少不必要的DOM操作。

响应式丢失

但是通过遍历劫持对象的所有属性来实现响应式数据的方式,并不完美,存在很多缺点。例如当我们通过数组下标去修改数据时或者修改数组的长度、给对象不存在的属性赋值、或者使用delete删除对象的属性,这几种方式都不会被vue监听到,从而导致响应式丢失,视图不更新的问题。

js 复制代码
<template> <!-- 模板部分 -->
  <div id="app"> <!-- Vue 应用的根元素 -->
    <p>Array: {{ someArray }}</p> <!-- 显示数组的内容 -->
    <p>Object: {{ someObject }}</p> <!-- 显示对象的内容 -->
    <button @click="modifyArrayIndex">Modify Array Index</button> <!-- 点击按钮,调用 modifyArrayIndex 方法 -->
    <button @click="modifyArrayLength">Modify Array Length</button> <!-- 点击按钮,调用 modifyArrayLength 方法 -->
    <button @click="addObjectProperty">Add Object Property</button> <!-- 点击按钮,调用 addObjectProperty 方法 -->
    <button @click="deleteObjectProperty">Delete Object Property</button> <!-- 点击按钮,调用 deleteObjectProperty 方法 -->
  </div>
</template>

<script> <!-- 脚本部分 -->
import Vue from 'vue'; // 导入 Vue 模块

export default { // 导出 Vue 组件对象
  data() { // 数据对象
    return {
      someArray: [1, 2, 3], // 初始化数组
      someObject: { name: 'John', age: 30 } // 初始化对象
    };
  },
  methods: { // 方法对象
    modifyArrayIndex() { // 修改数组中的元素
      // 通过数组下标修改数据
      this.someArray[0] = 100;
    },
    modifyArrayLength() { // 修改数组的长度
      // 修改数组长度
      this.someArray.length = 0;
    },
    addObjectProperty() { // 给对象添加新属性
      // 给对象不存在的属性赋值
      this.$set(this.someObject, 'newProp', 'New Value');
    },
    deleteObjectProperty() { // 删除对象的属性
      // 删除对象属性
      this.$delete(this.someObject, 'age');
    }
  }
};
</script>

<style scoped> 
#app {
  font-family: Arial, sans-serif; 
  padding: 20px; 
}
button {
  margin-right: 10px; 
}
</style>

如何解决响应式丢失

1. Vue.$set(官方推荐)

  • 对象: Vue.set(object, key, value)this.$set(object, key, value)
  • 数组:Vue.set(array, index, value)this.$set(array, index, value)
  • this.$set(要操作的对象或数组, 要新增或者修改的数组或对象key, 对应的值)
js 复制代码
addObjectProperty() { // 给对象添加新属性 
    // 给对象不存在的属性赋值 
    this.$set(this.someObject, 'newProp', 'New Value'); 
},

modifyArrayIndex() { // 修改数组中的元素 
    // 通过 Vue.set 修改数组 
    Vue.set(this.someArray, 0, 100); 
},

2. 使用数组的原型方法,修改数组

直接修改数组索引不会触发视图更新,但使用数组的变异方法(如 splice()、push()、pop()、shift()、unshift()、splice()、sort()、reverse() 等)可以保持响应式。这是因为这些方法会被 Vue 包装成响应式的操作。

js 复制代码
modifyArrayIndex() { // 修改数组中的元素 
    // 使用数组的变异方法修改元素 
    this.someArray.splice(0, 1, 100); 
},

3. 重新赋值

  • 对象:当需要添加新属性时,可以通过重新赋值整个对象来确保新属性是响应式的。
  • 数组:当需要修改数组时,可以通过重新赋值整个数组来确保数组变化是响应式的。
js 复制代码
对象:
addObjectProperty() { // 给对象添加新属性
  // 重新赋值整个对象,确保新属性是响应式的
  this.someObject = { ...this.someObject, newProp: 'New Value' };
},

数组:
modifyArrayIndex() { // 修改数组中的元素
  // 重新赋值整个数组,确保数组变化是响应式的
  this.someArray = [100, ...this.someArray.slice(1)];
},

4. Vue.$forceUpdate(手动强制更新视图)

如果无法避免直接修改数组索引或对象属性,可以手动触发视图更新。可以使用 this.$forceUpdate() 或者 this.$set(this.someObject, '__ob__', this.someObject.__ob__) 来强制更新视图。

js 复制代码
//数组
modifyArrayIndex() { // 修改数组中的元素
  // 通过数组索引直接修改元素
  this.someArray[0] = 100;
  // 手动触发视图更新
  this.$forceUpdate();
},

//对象
addObjectProperty() { // 给对象添加新属性
  // 给对象添加新属性
  this.someObject.newProp = 'New Value';
  // 手动触发视图更新
  this.$set(this.someObject, '__ob__', this.someObject.__ob__);
},

5.Object.assign(使用修改栈能触发视图更新的特性)

Object.assign(target, ...sources)

  • target:目标对象,将源对象的属性复制到该对象。
  • sources:一个或多个源对象,从这些对象中复制属性到目标对象。可以传入多个源对象,后面的源对象属性会覆盖前面的源对象属性。
  • Object.assign() 方法会返回目标对象。
js 复制代码
addObjectProperty() { // 给对象添加新属性
  // 使用 Object.assign() 将新属性添加到目标对象,并重新赋值整个对象
  this.someObject = Object.assign({}, this.someObject, { newProp: 'New Value' });
},
相关推荐
mldong1 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排2 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60612 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角3 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!3 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰4 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi5 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen6 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马7 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒7 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端