vue2、vue3的数据响应式原理及总结

vue2数据响应式原理:

核心:通过 Object.defineProtytype() 对对象的已有属性值的读取和修改进行劫持

数据劫持 -->给对象扩展属性 --> 属性设置

1、Object.defineProperty( )里面传入三个参数。

function defineProperty () {
  var _obj = {}

  Object.defineProperty(_obj, 'a', {
    value: 1
  })
  return _obj
}

var obj = defineProperty()
console.log(obj);  //{a:1}

2、Object.defineProperty( )里面传入两个参数。

第二个参数中属性值有四个可选属性:

​​​​

1、value:属性值

2、writeable:是否可写,默认为false

3、enumerable:是否可枚举,默认为false

4、configurable:是否可配置,默认为false

function defineProperty () {
  var _obj = {}

  Object.defineProperty(_obj, {
    a: {
      // 设置属性值
      value:1,
      // 属性值是否可写,默认为false
      writeable:true,
      // 是否可枚举,默认为false
      enumerable:true,
      // 是否可配置,默认为false
      configurable:true
    }
  })
  return _obj
}

var obj = defineProperty()

console.log(obj);  //{a:1}

3、Object.defineProperty( )里面传入两个参数,第二个参数中包含get方法和set方法时:

get方法在每一次获取属性值时,都会先走get方法,我们可以在返回该属性值之前,做一些事情;

set方法在每一次改变属性值时,都会先走set方法,相应的,我们也可以在这里做一些事情;

function defineProperty () {
    var _obj = {}

    Object.defineProperty(_obj, {
      firstVal: {
        get () {
          console.log(`获取${firstVal}的值`);
          return firstVal
        },
        set (newVal) {
          firstVal = newVal
          console.log(`修改了${firstVal}的值`);
        }
      },
      secondVal: {
        get () {
          console.log(`获取${secondVal}的值`);
          return secondVal
        },
        set (newVal) {
          secondVal = newVal
          console.log(`修改了${secondVal}的值`);
        }
      }
    })
    return _obj
  }

  var obj = defineProperty()

  console.log(obj);

Demo练习(计算器):

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <!-- 设置加减乘除按钮的颜色 -->
  <style type="text/css">
    .btn-group button.current {
      background-color: orange;
      color: #fff;
    }
  </style>
</head>

<body>
  <!-- 计算器搭建 -->
  <div class="j_calculator">
    <div class="result">0</div>
    <div class="input-group">
      <input type="text" value="0" class="f-input" /><br />
      <input type="text" value="0" class="s-input" />
    </div>
    <div class="btn-group">
      <button data-field="plus" class="current">+</button>
      <button data-field="minus">-</button>
      <button data-field="mul">*</button>
      <button data-field="div">/</button>
    </div>
  </div>
  <!-- 引入js文件 -->
  <script src="./index.js"></script>
</body>

</html>

index.js

// 定义一个计算类
class Compute {
  plus (a, b) {
    return a + b
  }
  minus (a, b) {
    return a - b
  }
  mul (a, b) {
    return a * b
  }
  div (a, b) {
    return a / b
  }
}
// 继承计算类
class Calculator extends Compute {
  constructor() {
    super()
    // 获取元素
    const oCal = document.getElementsByClassName('j_calculator')[0]
    this.fInput = oCal.getElementsByTagName('input')[0]
    this.sInput = oCal.getElementsByTagName('input')[1]

    this.oBtnGroup = oCal.getElementsByClassName('btn-group')[0]
    this.oBtnItems = this.oBtnGroup.getElementsByTagName('button')

    this.oResult = oCal.getElementsByClassName('result')[0]

    // 使用defineProperty定义响应式数据
    this.data = this.defineData()
    this.btnInx = 0
  }

  // 初始化函数
  init () {
    this.bindEvent()
  }

  // 事件绑定函数
  bindEvent () {
    this.oBtnGroup.addEventListener('click', this.onFieldBtnClick.bind(this), false);
    // 两个input输入框绑定同一个事件函数
    this.fInput.addEventListener('input', this.onInputClick.bind(this), false);
    this.sInput.addEventListener('input', this.onInputClick.bind(this), false);
  }

  // 切换加减乘除按钮,并加颜色
  onFieldBtnClick (ev) {
    const e = ev || window.event,
      tar = e.target || e.srcElement,
      tagName = tar.tagName.toLowerCase()

    tagName === 'button' && this.fieldUpdate(tar)
  }

  // 切换计算按钮时进行计算
  fieldUpdate (target) {
    this.oBtnItems[this.btnInx].className = ''
    this.btnInx = [].indexOf.call(this.oBtnItems, target)
    target.className += 'current'
    // 修改当前点击的按钮的属性,从而引发响应式
    this.data.field = target.getAttribute('data-field')
  }

  // 两个input输入框事件函数
  onInputClick (ev) {
    const e = ev || window.event,
      tar = e.target || e.srcElement,
      className = tar.className,
      val = Number(tar.value.replace(/\s+/g, '')) || 0;

    // 判断是哪一个输入框
    switch (className) {
      case 'f-input':
        this.data.fNumber = val
        break;
      case 's-input':
        this.data.sNumber = val
        break;
      default:
        break;
    }

  }

  // 定义响应式数据
  defineData () {
    let _obj = {},
      fNumber = 0,
      sNumber = 0,
      field = 'plus'
    // this指向改变,所以提前保存this
    const self = this
    Object.defineProperties(_obj, {
      fNumber: {
        get () {
          console.log('正在读取该值');
          return fNumber
        },
        set (newVal) {
          fNumber = newVal
          self.calculatorResult(fNumber, sNumber, field)
          console.log('"fNumber" the value has change');
        }
      },
      sNumber: {
        get () {
          console.log('正在读取该值');
          return sNumber
        },
        set (newVal) {
          sNumber = newVal
          self.calculatorResult(fNumber, sNumber, field)
          console.log('"sNumber" the value has change');
        }
      },
      field: {
        get () {
          console.log('正在读取该值');
          return field
        },
        set (newVal) {
          field = newVal
          self.calculatorResult(fNumber, sNumber, field)
          console.log('"field" the value has change');
        }
      }
    })
    return _obj
  }

  // 当输入值时进行计算
  calculatorResult (fNumber, sNumber, field) {
    this.oResult.innerHTML = this[field](fNumber, sNumber)
  }
}
new Calculator().init()

vue3数据响应式原理:

核心:vue3的数据响应式,是使用了ES6新增的Proxy代理对象,Proxy本身就是一个构造函数

var myObj = {
  a: 1,
  b: 2
}
// 定义响应式数据
let proxy = new Proxy(myObj, {
  //当读取对象某个属性时调用
  get (target, prop) {
    console.log(`有人读取了对象的${prop}属性`)
    // 第一种形式
    // return target[prop]

    // 第二种形式,使用Reflect函数
    // Reflect函数是一个全局函数,里面封装了许多方法,可以直接调用
    return Reflect.get(target, prop)
  },
  //当修改对象的某个属性或追加某个属性时调用
  set (target, prop, value) {
    console.log(`有人修改了对象的${prop}属性`)
    // target[prop] = value

    Reflect.set(target, prop, value)
  },
  //当删除对象的某个属性时调用
  //  代理Proxy里面还有许多方法,可查阅资料学习
  deleteProperty (target, prop) {
    console.log(`有人删除了对象身上的${propName}属性`)
    return Reflect.deleteProperty(target, prop)
  }
})
console.log(proxy);
console.log(proxy.a);
proxy.b = 3
console.log(proxy.b);

 最后总结:

1、defineProperty是对一个空对象进行设置属性,在读取和改变属性值的拦截一下,可以实现一些其他的逻辑处理。不支持函数拦截;

2、Proxy是对一个完整的对象进行代理,相当于拷贝了一份,当我们对原数据进行属性值操作时,这个代理对象会挡在前面对数据进行操作。支持对象代理,数组代理和函数代理。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值