数据响应式(Object.defineProperty和Proxy)

前言(使用vue2时出现的问题以及背后的思考)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue2</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        <button @click="update">点击更改</button>
        <button @click="addProperty">添加属性</button>
        <button @click="deleteProperty">删除属性</button>
        {{list}}<br>
        {{obj}}
    </div>

    <script>
        const app = new Vue({
            el:'#app',
            data(){
                return {
                  list:[
                    {
                      name:'张三',
                      age:20
                    },
                    {
                      name:'李四',
                      age:30
                    },
                  ],
                  obj:{
                       name:'张三'
                   }
                }
            },
            methods:{
               update(){
                  //修改其属性,具备响应式
                  // this.list[0].name = '王五'

                  //通过this.$set实现其响应式 
                  this.$set(this.list,0,'张三')

                  /*直接修改无效,Object.defineProperty无法追踪到数组的索引设置(例如使用 
                   arr[index] = value)这种改变 */
                  //this.list[0] = '王五'
               },
               addProperty(){
                  //不具有响应式,Object.defineProperty无法监控对象属性值的新增或删除
                  this.obj.text = '哈哈'
                  //具有响应式
                  this.obj = { age:20 }
                  // 若想保留响应式需使用this.$set(this.obj,'name','王五')
                  console.log(this.obj)
               },
               deleteProperty(){
                  //delete this.obj.name响应式不生效,
                  // delete this.obj.name
                  //需要通过this.$delete(重写方法)删除
                  this.$delete(this.obj,'name')
               }
            }
        })
        /*
          出现以上问题的原因是Vue2响应式原理是基于Object.defineProperty实现的,Object.defineProperty无法
          检测到对象属性的动态添加。于是作者尤雨溪重写了数组的方法,并加入了Vue.set来解决这个问题。
          示例:
            data(){
                return {
                   obj:{
                      name:'李四'
                   }
                }
            },
            methods:{
               update(){
                 this.$set(this.obj,'age', 20) //向obj添加新属性age
               }
            }

        */
    </script>
</body>
</html>

1.Object.defineProperty (ES5)

api局限性:向对象追加属性不会被劫持,向数组追加同样无法劫持,想要保留响应式需要重写方法。

  1.1  对象响应式处理

/**
 * 对象响应式
 * @param {*} target 
 * @param {*} key 
 * @param {*} value 
 */
export const defineObjectReactive = (target,key,value)=>{
    Object.defineProperty(target,key,{
        enumerable:true,
        configurable: true,
        get(){
            return value
        },
        set(newVal){
           if(value === newVal) return 
           value = newVal
           console.log('触发了',newVal)
        }
    })
} 

/**
 * 对象每一项转为响应式
 * @param {*} target 
 */
export const transformToReactive = (target)=>{
    Object.keys(target).forEach(item=>{
        defineObjectReactive(target,item,target[item])
    })
}

  1.2 重写数组方法

//重写数组方法
export const defineArrayReactive = (arr) => {
  const origin = Array.prototype;
  const originMethod = Object.create(origin);

  const methods = [
    "push",
    "pop",
    "shift",
    "unshift",
    "splice",
    "sort",
    "reverse",
  ];
  methods.forEach((method) => {
    const result = (originMethod[method] = function (...args) {
      origin[method].call(this, ...args);
      console.log(args);
      //可以在此处处理数组修改
      return result;
    });
  });
  if(Array.isArray(arr)){
      //重设此数组的prototype
      arr.__proto__ = originMethod
  }
};

2.proxy (ES6)

相较于Object.defineProperty的优势可以监听对象和数组追加内容。

        const reactiveProxy = (params)=>{
            const data =  new Proxy(params,{
                get(target,value){
                       return target[value]
                    },
                set(target,props,value){
                    //    //给源对象赋值 
                       target[props] = value
                       console.log(target)
                }
            })
            return data
        }

        const data2 = reactiveProxy(obj)
        console.log(data2)

相关推荐

  1. 数据响应(Object.definePropertyProxy)

    2024-07-20 13:58:03       16 阅读
  2. vue3 原理【详解】Proxy 实现响应

    2024-07-20 13:58:03       33 阅读
  3. Vue3:refreactive实现响应数据

    2024-07-20 13:58:03       38 阅读
  4. 响应编程-数据劫持

    2024-07-20 13:58:03       18 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-20 13:58:03       57 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-20 13:58:03       60 阅读
  3. 在Django里面运行非项目文件

    2024-07-20 13:58:03       48 阅读
  4. Python语言-面向对象

    2024-07-20 13:58:03       60 阅读

热门阅读

  1. 云计算的三种服务模式

    2024-07-20 13:58:03       19 阅读
  2. wps的xls文件,如何过滤掉空白没有数据的行

    2024-07-20 13:58:03       17 阅读
  3. Provider(5) - AdjustChannelsBufferProvider

    2024-07-20 13:58:03       17 阅读
  4. lua 游戏架构 之 SceneLoad场景加载(一)

    2024-07-20 13:58:03       19 阅读
  5. Thread类的基本用法

    2024-07-20 13:58:03       17 阅读
  6. C?C++?

    2024-07-20 13:58:03       18 阅读
  7. ArcGIS Pro SDK (九)几何 10 弧

    2024-07-20 13:58:03       16 阅读
  8. AB测试介绍

    2024-07-20 13:58:03       19 阅读
  9. MULESOFT

    MULESOFT

    2024-07-20 13:58:03      22 阅读