VUE响应式源码剖析

前言:本文是另一篇文章的续文,阅读前文后再阅读此文会更舒畅。

  • Vue响应式原理

Vue 递归遍历data对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。在 property 被修改时,也就是setter 触发,会重新渲染。(看不懂的话后面有简单解释)

  • 针对添加或删除对象不重新渲染页面现象,下面从官网找到解释:

Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。(本人读了好几遍,感觉这个解释不怎么合理,后面列出了个人见解)

下面是VUE实现源码~的大致,差不多就是这样实现的,自己阅读下,不难。

    const data = {
        head: 'hello today',
        obj: {
            name: 'ludeng',
            age: 18,
            sex: 'male'
        },
        arr: [1, 2, 3, 4]
    }
    
    // 变异数组,修改原型链上的push,pop等函数,使用它们会渲染页面
    const ArrayPrototype = Object.create(Array.prototype);
    ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method =>{
        ArrayPrototype[method] = function() {
            Array.prototype[method].call(this, ...arguments);
            render();
        }
    })
    
    function defineReactive(data, key, value){
        observer(data[key])
        Object.defineProperty(data, key, { 
            get() {     //当访问该属性时,会调用此函数
                console.log("读")
                return value;
            },
            set(newVal) {   //当属性值被修改时,会调用此函数
                console.log("写")
                value = newVal;
                render()
            }
        })
    }
    //监视对象及所有子项,不监视数组项
    function observer(data) {
        if(Array.isArray(data)) {
            data.__proto__ = ArrayPrototype;
            return;
        }
        if(typeof data === 'object') { //数组也是对象,vue不监控数组,上面单独处理数组
            for(const key in data) {
                defineReactive(data, key, data[key]);           
            }
        }
    }
    function render() {
        console.log("页面渲染了");
    }
    
    observer(data); //data是要监视的数据
    

先来稍微解析下上面代码,
defineReactive()就是给对象的某属性设置Object.defineProperty
observer()是循环一个对象的属性,每个属性都调用defineReactive()
数组那块就是修改原型链上的方法,使用push、pop方法时会渲染。

解释原理:使用递归,利用Object.defineProperty,给所有子成员添加get()&set()。当属性值被修改时会调用set() (set()里调用render()),所以修改对象属性值会渲染,而删除和添加不会(删除添加不会调用set())

举例解释:data{ obj: { name: "a", age: 18 } },递归处理obj, name, age。给obj添加属性(obj.a = "a"),obj本身地址未修改,不会调用set(),所以不会渲染,删除同理。
修改子成员(name,age)为什么会渲染?obj地址未修改啊!修改name渲染,因为name也被处理过,会调用set()。
数组则修改原型链上的push\pop等方法,调用变异数组方法会调用render()。所以使用push修改数组的时候,页面会重新渲染。

以上个人理解,可能会不准确,欢迎指正。

[vue官方]深入响应式原理:https://cn.vuejs.org/v2/guide/reactivity.html

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。