Vue 2 阅读理解(十五)之响应式系统(一)Object.defineProperty

简介: Vue 2 阅读理解(十五)之响应式系统(一)Object.defineProperty

响应式系统(一)


在 Vue 2 的官方文档 - 深入响应式原理 中介绍了 Vue 最独特的特性就是 非侵入性 的响应式系统,所有的数据模型都是对象形式。


在 Vue 2 中,每个 Vue 实例都接收一个 对象形式 Options 来初始化,并通过 Object.defineProperty 来对象里面的一些属性的 默认getter/setter 方法全部转化为 特殊的会触发依赖收集的 getter/setter 方法


1. 变化追踪过程


官方给出的回答是:每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。


网络异常,图片无法展示
|


2. re-render 与渲染 Watcher


上面的 每一个组件实例都对应的 Watcher 实例,通常我们称为 渲染 Watcher,在 $mount() 首次执行挂载时初始化。


渲染 Watcher 实例配置了一个 执行前函数 watcherOptions.before 和一个执行 watcher.run() 时执行的 回调函数 cb


watcherOptions.before 主要只有一行内容:


if (vm._isMounted && !vm._isDestroyed) {
  callHook(vm, 'beforeUpdate')
}


即在更新前会触发 beforeUpdate 生命周期钩子,执行配置的相关方法。

回调函数 cb 一样只有一点内容:


updateComponent = () => {
  vm._update(vm._render(), hydrating)
}


即用来更新 dom 内容(当然开发环境如果开启了性能分析的话,这里还有其他的处理)


这个过程即是上图的 re-render 过程: Watcher ==> Component Render Function


3. getter 与 setter


上面已经说了 wachter 在变化触发时会执行 vm._update()callHook('beforeUpdate'),而 getter/setter 是如何定义和触发更新的呢?


const property = Object.getOwnPropertyDescriptor(obj, key)  
const getter = property && property.get
const setter = property && property.set
Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
        const value = getter ? getter.call(obj) : val
        if (Dep.target) {
            dep.depend()
            if (childOb) {
                childOb.dep.depend()
                isArray(value) && dependArray(value)
            }
        }
        return isRef(value) && !shallow ? value.value : value
    },
    set: function reactiveSetter(newVal) {
        const value = getter ? getter.call(obj) : val
        if (!hasChanged(value, newVal)) {
            return
        }
        if (setter) {
            setter.call(obj, newVal)
        } else if (getter) {
            return
        } else if (!shallow && isRef(value) && !isRef(newVal)) {
            value.value = newVal
            return
        } else {
            val = newVal
        }
        childOb = !shallow && observe(newVal, false, mock)
        dep.notify()
    }
})


这里首先是 将对象的 默认 get 和 set 方法保存下来,通过 Object.defineProperty 重新定义该对象每个属性的 get 和 set 方法


get:


首先直接通过 默认 get 获取到值,并且如果这个值是个对象或者数组的话,还会进行深层次遍历,最后返回该属性的值。


当然这里为了兼容 v3 语法,新增了一个 isRef 的判断,用来处理 ref 定义的变量;在以前的版本都是直接返回 value 的。


set


作为重设对象属性值的方法,为了避免多次更新相同属性值,这里会在最前面增加一个 hasChanged(value, newVal) 的判断,如果多次更新的值一样,则不会重复设置和触发更新。


然后便是属性的更新:


  • 对于具有默认 setter 的属性(即在初始化时就定义了的对象属性),会直接调用默认 set 方法


  • 如果只有默认 getter 方法,说明该属性不能更新,则直接退出


  • 以上都不满足,则直接更新属性值为 newVal


最后一样会处理对象子属性,并通过 dep.notify() 来触发依赖该对象的 watcher 的更新。因为组件渲染 watcher 算是默认的watcher,所以如果该属性更新影响到了页面效果,则会触发视图更新。


目录
相关文章
|
20天前
|
API
vue3知识点:响应式数据的判断
vue3知识点:响应式数据的判断
25 3
|
23天前
|
缓存 JavaScript UED
优化Vue的响应式性能
【10月更文挑战第13天】优化 Vue 的响应式性能是一个持续的过程,需要不断地探索和实践,以适应不断变化的应用需求和性能挑战。
29 2
|
28天前
|
JavaScript 前端开发
Vue 2 和 Vue 3 之间响应式区别
10月更文挑战第7天
32 2
|
1月前
|
JavaScript 前端开发 网络架构
如何使用Vue.js构建响应式Web应用
【10月更文挑战第9天】如何使用Vue.js构建响应式Web应用
|
1月前
|
JavaScript
Vue 的响应式原理中 Object.defineProperty 有什么缺陷
Vue 的响应式原理主要依赖于 `Object.defineProperty`,但该方法存在一些缺陷:无法检测到对象属性的添加和删除,且对大量数据进行代理时性能较差。Vue 3 中改用了 Proxy 来解决这些问题。
|
1月前
|
JavaScript 前端开发
如何使用Vue.js构建响应式Web应用程序
【10月更文挑战第9天】如何使用Vue.js构建响应式Web应用程序
|
1月前
|
JavaScript 前端开发 数据安全/隐私保护
前端技术分享:使用Vue.js构建响应式表单
【10月更文挑战第1天】前端技术分享:使用Vue.js构建响应式表单
|
1月前
|
JSON JavaScript 前端开发
vue尚品汇商城项目-day00【项目介绍:此项目是基于vue2的前台电商项目和后台管理系统】
vue尚品汇商城项目-day00【项目介绍:此项目是基于vue2的前台电商项目和后台管理系统】
44 1
|
19天前
|
JavaScript 前端开发 API
vue3知识点:Vue3.0中的响应式原理和 vue2.x的响应式
vue3知识点:Vue3.0中的响应式原理和 vue2.x的响应式
23 0
|
2月前
vue2的响应式原理学“废”了吗?继续观摩vue3响应式原理Proxy
该文章对比了Vue2与Vue3在响应式原理上的不同,重点介绍了Vue3如何利用Proxy替代Object.defineProperty来实现更高效的数据响应机制,并探讨了这种方式带来的优势与挑战。
vue2的响应式原理学“废”了吗?继续观摩vue3响应式原理Proxy