汪图南
  • RAG

    • RAG
  • 快速入门
  • 高级技巧
前端面试之道
  • 打包工具

    • Webpack
    • Rollup
  • TypeScript

    • TypeScript基础
    • TypeScript类型挑战
  • CSS预编译器

    • SASS
  • 自动化测试

    • Vue应用测试
  • Vue2.0源码分析
  • Vue3.0源码分析
  • 数据结构和算法(基础)
  • LeetCode(刷题)
  • JavaScript书籍

    • 你不知道的JavaScript(上)
    • 你不知道的JavaScript(中下)
    • JavaScript数据结构和算法
    • JavaScript设计模式与开发实践
    • 深入理解ES6
  • Git书籍

    • 精通Git
Github
  • RAG

    • RAG
  • 快速入门
  • 高级技巧
前端面试之道
  • 打包工具

    • Webpack
    • Rollup
  • TypeScript

    • TypeScript基础
    • TypeScript类型挑战
  • CSS预编译器

    • SASS
  • 自动化测试

    • Vue应用测试
  • Vue2.0源码分析
  • Vue3.0源码分析
  • 数据结构和算法(基础)
  • LeetCode(刷题)
  • JavaScript书籍

    • 你不知道的JavaScript(上)
    • 你不知道的JavaScript(中下)
    • JavaScript数据结构和算法
    • JavaScript设计模式与开发实践
    • 深入理解ES6
  • Git书籍

    • 精通Git
Github
  • 介绍

    • 介绍和参考
  • 源码目录设计和架构设计

    • 设计
  • Rollup构建版本

    • Rollup基础知识
    • Vue中的Rollup构建
  • 从入口到构造函数整体流程

    • 整体流程
    • initGlobalAPI流程
    • initMixin流程
    • stateMixin流程
    • eventsMixin流程
    • lifecycleMixin流程
    • renderMixin流程
  • 响应式原理

    • 介绍
    • 前置核心概念
    • props处理
    • methods处理
    • data处理
    • computed处理
    • watch处理
    • 深入响应式原理
    • 依赖收集
    • 派发更新
    • nextTick实现原理
    • 变化侦测注意事项
    • 变化侦测API实现
  • 虚拟DOM和VNode

    • 虚拟DOM
    • VNode介绍
    • Diff算法
  • 组件化

    • 介绍
    • $mount方法
    • render和renderProxy
    • createElement
    • createComponent
    • 合并策略
    • update和patch
    • 组件生命周期
    • 组件注册
  • 编译原理

    • 介绍
    • compileToFunctions
    • parse模板解析
    • optimize优化
    • codegen代码生成
  • 扩展

    • 扩展
    • directive指令
    • filter过滤器
    • event事件处理
    • v-model
    • 插槽
    • Keep-Alive
    • Transition
    • Transition-Group
    • Vue.use插件机制
  • Vue-Router

    • 介绍
    • 路由安装
    • matcher介绍
    • 路由切换
    • 内置组件
    • 路由hooks钩子函数
  • Vuex

    • 介绍
    • Vuex安装
    • Vuex初始化
    • Vuex辅助API
    • Store实例API

变化侦测注意事项

虽然Object.defineProperty()方法很好用,但也会存在一些例外情况,这些例外情况的变动不能触发setter。这种情况,我们分为对象和数组两类来分析。

对象

假设我们有如下例子:

export default {
  data () {
    return {
      obj: {
        a: 'a'
      }
    }
  },
  created () {
    // 1.新增属性b,属性b不是响应式的,不会触发obj的setter
    this.obj.b = 'b'
    // 2.delete删除已有属性,无法触发obj的setter
    delete this.obj.a
  }
}

从以上例子我们可以看到:

  • 当为一个响应式对象新增一个属性的时候,新增的属性不是响应式的,后续对于这个新增属性的任何修改,都无法触发其setter。为了解决这种问题,Vue.js提供了一个全局的Vue.set()方法和实例vm.$set()方法,它们实际上都是同一个set方法,我们会在后续的章节中介绍与响应式相关的全局API的实现。
  • 当一个响应式对象删除一个已有属性的时候,不会触发setter。为了解决这个问题,Vue.js提供了一个全局的vue.delete()方法和实例vm.$delete()方法,它们实际上都是同一个del方法,我们会在后续的章节中介绍与响应式相关的全局API的实现。

数组

假设我们有如下例子:

export default {
  data () {
    return {
      arr: [1, 2, 3]
    }
  },
  created () {
    // 1.通过索引进行修改,无法捕获到数组的变动。
    this.arr[0] = 11
    // 2.通过修改数组长度,无法捕获到数组的变动。
    this.arr.length = 0
  }
}

从以上例子我们可以看到:

  • 通过索引直接修改数组,无法捕捉到数组的变动。
  • 通过修改数组长度,无法捕获到数组的变动。

对于第一种情况,我们可以使用前面提到过的Vue.set或者vm.$set来解决,对于第二种方法,我们可以使用数组的splice()方法解决。

在最新版Vue3.0中,使用到了Proxy来代替Object.defineProperty()实现响应式,使用Proxy后以上问题全部可以解决,然而Proxy属于ES6的内容,因此对于浏览器兼容性方面有一定的要求。

最后更新时间: 2025/5/6 15:36
贡献者: wangtunan
Prev
nextTick实现原理
Next
变化侦测API实现