美文网首页
利用 Object.defineProperty 来实现 vue

利用 Object.defineProperty 来实现 vue

作者: 人话博客 | 来源:发表于2018-12-28 18:25 被阅读0次

先说一下,我们需要达到的目标.

  • modeldata 发生变化时,dom元素的value要发生变化.
  • dom元素的value,发生变化时,modeldata 要发生变化.
目标过程

核心思想即是:

  • 我们需要监控到 model.dataset,当执行set 的时候,改变对应的dom.value
  • 我们需要监控到 dom.valueset,当执行到 set 的时候,改变对应的 model.value

1.Object.defineProperty 设置自定义对象的 set

由于,我们需要监控到model.valueset.
常规做法,一般是不能做到了.

let model = {}
model.value = 'value' // 这里设置上了就设置上了,没有办法获取到设置到的动作.

使用 Object.defineProperty来监听model.value属性的set行为.

let value = undefined
  let model = {}
  Object.defineProperty(model, 'value', {
    set: function (newVal) {
      // 在这检控到了model.value值的set设置时机
      value = newVal
      console.log('model.value setted!!!')
    },
    get: function () {
      return value
    }
  })
model.value='aaaaa'
model.value setted!!!

可以正常监控到 model.value 属性的 set 时机.


2.dom是HTMLElement对象,能用 Object.defineProperty来设置某个属性的set吗?

由于 dom 元素,本质也是一个 HTMLElement对象.
所以,如果.value 属性内部设置的configurable:true 的话,我们是可以利用Object.defineProperty 来重新设置 .valueset ,拿到dom.value 修改时机的.

按照一样的逻辑,使用Object.defineProperty来设置dom元素的value的set时机

 // 拿到input的dom对象
  let defaultInputVal = undefined
  let input = document.querySelector('#input')
  Object.defineProperty(input, 'value', {
    set: function (newVal) {
      defaultInputVal = newVal
      console.log('input.dom.value setted!')
    },
    get: function () {
      return defaultInputVal
    }
  })
dom对象无法使用Object.defineProperty来设置属性

结果发现,使用同样的办法,在dom上无法使用 Object.defineProperty拿到属性.
如果其内部定义 value 属性设置的 configurable:false 的话,那么在我这里再次定义,也没有报错误:

Cannot redefine property:value

反正不管怎么样,自定义对象的那套 Object.defineProperty 想利用 set 方式拿到 DOM 对象某个属性的 set 时机是拿不到了.


3.利用事件拿到 dom 对象属性的 set 时机

那么,就退而求其次的使用事件吧.
(反正最终是要拿到dom对象的.value属性赋值的那个时机)

let input = document.querySelector('#input')
  input.addEventListener('keyup', function (e) {
    console.log(e.target.value)
  }, false)
利用事件拿到dom对象的属性set.gif

实现数据的双向绑定.

现在我们有的:

  • 利用 Object.defineProperty拿到的了model.valueset 时机
  • 利用 事件 拿到了 dom 元素的 .valueset 时机.
  • 接下来的就简单了,在 model.value.set 内部同时修改 dom.value
  • dom.value.set 内部修改 model.value

代码如下:

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Object.defineProperty</title>
</head>

<body>
  <h3>利用Object.defineProperty & dom.event 实现数据双向绑定</h3>
  <input type="text" id="input">
  <div id="contentText"></div>
</body>

<script>
  let value = undefined
  let contentText = document.querySelector('#contentText')
  let model = {}
  Object.defineProperty(model, 'value', {

    set: function (newVal) {

      // 在这检控到了model.value值的set设置时机
      value = newVal
      // 把model的新值赋值给dom
      input.value = newVal
      contentText.textContent = newVal

    },
    get: function () {
      return value
    }
  })

  let input = document.querySelector('#input')
  input.addEventListener('keyup', function (e) {
    // console.log(e.target.value)
    // 在这里拿到了dom的value更新值
    // 把dom的新值赋值给model
    model.value = e.target.value
    console.log(`input的数据改变了,model.value=${model.value}`)
  }, false)
</script>

</html>

效果:

Object.defineProperty&DOM.EVENT

4.补充

ES6.Proxy & DOM.EVENT

其实本质上,只要拿到了model.valueset设置时机,在结合dom本身支持的事件机制,就能实现数据的双向绑定.

ES6 新推出的 Proxy 也能监控到 Objset,所以也能完成这样的双向绑定.

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>利用 Proxy 来实现数据的双向绑定</title>
</head>

<body>
  <h3>利用ES6提供的Proxy来实现数据属性双向绑定</h3>
  <input type="text" id="input">
  <div id="textContent"></div>
</body>
<script>
  let input = document.querySelector('#input')
  let textContent = document.querySelector('#textContent')
  input.addEventListener('keyup', function (e) {
    proxyModel.value = e.target.value
    console.log(`model.value=${proxyModel.value}`)
  }, false)
  let model = {
    value: undefined
  }

  let proxyModel = new Proxy(model, {
    get: function (obj, prop) {
      return obj[prop]
    },
    set: function (obj, prop, val) {
      obj[prop] = val
      input.value = val
      textContent.textContent = val
    }
  })
</script>

</html>

结果:


总结

双向绑定实现起来其实非常简单.无非就下面三步.

  • 利用 Proxy 或者 Object.defineProperty 都能设置属性的set
  • 利用 Event 机制,拿到 dom 元素设置值的时机.
  • 在双方对应的 set 时机里来实现数据的双向绑定.

相关文章

网友评论

      本文标题:利用 Object.defineProperty 来实现 vue

      本文链接:https://www.haomeiwen.com/subject/diuelqtx.html