Vue2的响应式
核心:通过 Object.defineProtytype() 对对象的已有属性值的读取和修改进行劫持;
数据劫持 --> 给对象扩展属性 --> 属性设置
-  
实现原理:
-  
对象类型:通过
Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。 -  
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
-  
比如说在 vue2 中,data 数据中有一个数组, 当我们调用data 数组中的push方法,我们调用的是经过二次封装的push, 和 array原型中的push 方法不是一个事;
 -  
被封装的 push 方法中主要做了两件事:
-  
帮们正常调用数据的push方法
 -  
帮我们更新界面 ;
 
 -  
 
 -  
 
 -  
 
-  
存在问题:
-  
新增属性、删除属性, 界面不会更新。
 -  
直接通过下标修改数组, 界面不会自动更新。
 
 -  
 
Vue2框架中数据响应式的优缺点
<template>
    <div>
        <h1>我是Vue2写的效果</h1>
        <h2 v-show="person.name">姓名: {{fperson.name}}</h2>
        <h2 v-show="person.sex">性别: {{person.sex}} </h2>
        <h2>年龄: {{ person.age }} </h2>
        <h2>爱好: {{ person.hobby}} </h2>
        <button @click="addSex">添加一个sex属性</button>
        <button @click="deleteName">删除name属性</button>
        <button @click="updateHobby">修改第一个爱好的名字</button>
    </div>
</template>
    <script>
    import Vue from 'vue'
    export default {
        name:'App'
        data() {
           return {
              person:{
                  name:'张三',
                  age:18,
                  hobby: ["学习", "吃饭"]
              }
           }
        }
        methods: {
         // 新增属性、删除属性, 界面不会更新
         // 添加对象属性 
         addSex() {
            // 数据改变页面没有监听到! 
            this.person.sex = '女'
            // 解决方式: $set() 给对象追加一个响应式数据 
            this.$set(this.person, 'sex', '女')
            Vue.set(this.person, 'sex', '女')  
         }
        // 删除对象属性 
        deleteName(){
            // delete this.person.name
            // 移除一个响应式数据 
            this.$delete(this.person, name)
            Vue.delete(this.person, 'name', '女')
        }
        // 直接通过下标修改数组, 界面不会自动更新。
        updateHobby() {
            // this.person.hobby[0] = '逛街'
            this.$set(this.person.hobby, 0, '逛街')
            // 调用 Array 中的变更方法可以实现修改
            this.person.hobby.splice(0, 1, '逛街')
        }
    } 
    </script>
} 
Vue2数据响应式原理
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
语法
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty#%E8%AF%AD%E6%B3%95
 
Object.defineProperty(obj, prop, descriptor) 
obj: 要定义属性的对象。prop:要定义或修改的属性的名称或 Symbol 。descriptor: 要定义或修改的属性描述符。- 返回值: 被传递给函数的对象。
 
function defineProperty () {
  var _obj = {}
 
  Object.defineProperty(_obj, 'a', {
    value: 1
  })
  return _obj
}
 
var obj = defineProperty()
console.log(obj);  //{a:1} 
第三个参数里面还有6个配置控住属性
- writable:是否可重写
 - value: 当前值
 - get: 读取时内部调用的函数
 - set: 写入时内部调用的函数
 - enumerable:是否可以遍历
 - configurable: 是否可再次修改配置项
 
<body>
    <script type="text/javascript">
        // 源数据
        let person = {
            name: '张三', 
            age: 18
        }
        // 模拟vue2中实现响应式 
        let p = {} 
        
        // p 对象身上追加响应式数据 
        Object.defineProperty(p, 'name', {
            // 可配置的, 配置删除使用 
            configurable: true, 
            get() { // 有人读取name时调用 
                return person.name
            },
            set(value) { // 有人修改name时调用 
                console.log('有人修改了name属性, 我发现了, 我要去更新页面!');
                person.name = value
            }
        })
        Object.defineProperty(p, 'age', {
            configurable: true,
                
            value:"男",       //设置属性值
            enumerable:true,  //控制属性是否可以枚举,默认值是false
            writable:true,    //控制属性是否可以被修改、可写,默认值是false
            configurable:true //控制属性是否可以被删除、可配置,默认值是false
            get() { // 有人读取age时调用 
                return person.age
            },
            set(value) { // 有人修改age时调用 
                console.log('有人修改了age属性, 我发现了, 我要去更新页面!');
                person.age = value
            }
        })
        
        // 数据修改
        // p.age = 20     -- 有人修改了age属性, 我发现了, 我要去更新页面!
        // 数据删除 
        // delete p.name  --  true 主要需要 configurable: true 
        // 属性添加; 但是不是响应式的没有 set get 方法; 
        // p.sex = '男'
    </script>
</body> 
writable:true 控制属性是否可以被修改,控制台也看的当为TRUE的时候属性值可以被修改
configurable:true 控制属性是否可以被删除
enumerable:true 控制属性是否可以枚举,true的话简单的说就是可以遍历获取该值还有最重要的两个属性 set和get(即存取器描述:定义属性如何被存取),这两个属性是干嘛的?
注意:当使用了getter或setter方法,不允许使用writable和value这两个属性(如果使用,会直接报错滴)
get 是获取值的时候的方法,类型为 function ,获取值的时候会被调用,不设置时为undefined
set 是设置值的时候的方法,类型为 function ,设置值的时候会被调用,undefined
get或set不是必须成对出现,任写其一就可以
get:
当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
每一次获取属性值时,都会先走get方法,我们可以在返回该属性值之前,做一些事情;
set:
当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
每一次改变属性值时,都会先走set方法,相应的,我们也可以在这里做一些事情;
可以实现一个数据的联动效果
Vue3.0的响应式
-  
实现原理:
-  
通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
 -  
通过Reflect(反射): 对源对象的属性进行操作。
 -  
MDN文档中描述的Proxy与Reflect:
-  
Proxy:Proxy - JavaScript | MDN
 -  
Reflect:Reflect - JavaScript | MDN
 
 -  
 
 -  
 
new Proxy(data, {
	// 拦截读取属性值
    get (target, prop) {
    	return Reflect.get(target, prop)
    },
    // 拦截设置属性值或添加新属性
    set (target, prop, value) {
    	return Reflect.set(target, prop, value)
    },
    // 拦截删除属性
    deleteProperty (target, prop) {
    	return Reflect.deleteProperty(target, prop)
    }
})
proxy.name = 'tom'    
  
Vue3响应式原理_Proxy
<body>
    <script>
         // 源数据
         let person = {
            name: '张三', 
            age: 18
        }
        //模拟 Vue3中实现数据响应式 
        // 定义一个代理对象p;  让p去映射对person的操作, 能检测到变化!
        const p = new Proxy(person, {
            // 完成数据的响应式, 捕获数据的修改
            get(target, propName) {// 获取读取 target: 源数据; propName: 读取修改的属性 
                console.log(`有人读取了 p 身上的${ propName }属性!`);
                return target[propName]
            },
            set(target, propName, value) { // 修改,追加调用 
                console.log(`有人修改了 p 身上的${ propName }属性, 我要去更新页面了!`);
                target[propName] = value
            },
            deleteProperty(target, propName) { // 删除调用  target: 要删除的源数据;  propName: 删除属性 
                console.log(`有人删除了 p 身上的${ propName }属性, 我要去更新页面了!`);
                return delete target[propName]
            }
        })
        // p.sex ="男"      添加属性 
        // delete p.age     true 删除属性 
        // p.name = "ls"    有人修改了 p 身上的name属性, 我要求修改页面了!
        // delete p.age     有人删除了 p 身上的age属性, 我要去更新页面了!
        // p.sex ='男'      有人修改了 p 身上的sex属性, 我要去更新页面了!
    </script>
</body> 
 
最后总结:
1、defineProperty是对一个空对象进行设置属性,在读取和改变属性值的拦截一下,可以实现一些其他的逻辑处理。不支持函数拦截;
2、Proxy是对一个完整的对象进行代理,相当于拷贝了一份,当我们对原数据进行属性值操作时,这个代理对象会挡在前面对数据进行操作。支持对象代理,数组代理和函数代理。
  












![刷题记录:牛客NC17509挖沟[prim+kruskal算法详解]](https://img-blog.csdnimg.cn/img_convert/ae4f364e10243cf7b1f8600f3eda537b.png#pic_center)





