关于vue 的MVVM:Object.defineProperty 和 Element.addEventListener() 实现双向数据绑定

原文章参考:https://www.jianshu.com/p/ea9d556d6529

本文主要通过 JS 的 Object.defineProperty 和 Element.addEventListener() 来实现

数据<-->元素双向数据绑定

用于监听 js 数据对象。MVVM 中的 model

Object.defineProperty   

用于监听 js 数据对象。MVVM 中的 model

Element.addEventListener() 

 


什么是MVVM?

  • 概念介绍
    • MVVM分为三个部分:分别是M(Model,模型层 ),V(View,视图层),VM(ViewModel,V与M连接的桥梁,也可以看作为控制器)
      1、 M:模型层,主要负责业务数据相关;
      2、 V:视图层,顾名思义,负责视图相关,细分下来就是html+css层;
      3、 VM:V与M沟通的桥梁,负责监听M或者V的修改,是实现MVVM双向绑定的要点;
    • MVVM支持双向绑定,意思就是当M层数据进行修改时,VM层会监测到变化,并且通知V层进行相应的修改,反之修改V层则会通知M层数据进行修改,以此也实现了视图与模型层的相互解耦;

关于Object.defineProperty

用法:

 Object.defineProperty( ) 可以再一个对象中定义或者修改一个属性,然后返回这个对象,并且可对该属性的可写行可便利性存取描述符(get、set)等进行设定;

参数:

Object.defineProperty(obj, prop, descriptor);
  • obj: 需要进行定义或修改属性的对象;
  • prop: 需要进行定义或者修改的属性;
  • descriptor: 该属性的描述符(包含存取描述符和数据描述符),该参数以一个对象的形式传入,该参数有六个选项:
    • value: 设定该属性的值;
    • writable: 布尔,该属性是否可写入(是否可改变其value);
    • enumerable: 布尔,该属性是否可被遍历得到(for...in, Object.keys等);
    • configurable: 布尔,设定该属性是否可被删除,且除writable外的其他描述符是否可被修改;
    • get: 函数,该属性的值被获取时执行的回调函数(例如console.log(prop)),默认为undefined;
    • set: 函数,该属性的值被设置时执行的回调函数,默认为undefined
  // 没有get和set
        let obj = {

        }
        Object.defineProperty(obj, 'a', {
            value: 123, // 该属性值为123
            enumerable: false, // 不可被遍历得到
            writable: false, // 不可被重新写入
            configurable: false // 不可被删除,且enumerable和value不能通过Object.defineProperty重新定义
        })
        // 使用get和set
        let obj = {
            // 设定默认值
            _data: {
                a: 123
            }
        }
        Object.defineProperty(obj, 'a', {
            get() {
                // 当获取a时执行
                console.log('a被获取了')
                return obj._data.a
            },
            set(value) {
                // 当修改a时执行
                obj._data.a = value
                console.log('a的值被修改了')
            }
        })

数据描述符和存取描述符
descriptor的六个选项中,有数据描述符和属性描述符,他们是不可共存的,否则会报错(因为功能上有所重复和冲突),其中

  • 数据描述符:value,writable;
  • 存取描述符:get,set;
  • 两者皆可共存:configurable,enumerable;
  • 报错例子
     let obj = {

        }
        Object.defineProperty(obj, 'a', {
            value: 123,
            get() {
                // 当获取a时执行
                console.log('a被获取了')
                return obj._data.a
            },
            set(value) {
                // 当修改a时执行
                obj._data.a = value
                console.log('a的值被修改了')
            }
        })

  • 兼容性
    至少IE8以上

使用 Object.defineProperty( )  和 Element.addEventListener( ) 实现MVVM的表单数据双向绑定

有这么一个表单和一个person对象

  • 需要实现的功能(该表单与person对象的双向绑定)
    • 1.用户在表单中输入的数据实时绑定在person对象的同名属性中;
    • 2.在person对象中进行属性的修改会实时反映在表单中;
  • 获取form的DOM节点,并且设置默认值,该默认值在Vue中就是data对象中的数据;

  • 实现用户输入数据与person对象内同名属性的同步修改

  • 一、用Object.defineProperty为person对象设定name和age两个属性,并且他们的get获取的是_data里的数据

  • 二、监听form表单中的input事件,并且将获取的目标DOM节点的value值赋值给person对象中的响应属性即可完成用户输入与对象值间的绑定

    •  

      效果图:

       

  • 实现person对象同名属性的修改同步用户表单框中的输入值

     

     

     

    效果图:修改直接修改person对象中的name和age属性的值,可以同步显示在输入框中

     

  • 这时候其实已经完成了数据的双向绑定


  • 完整例子:

  • <html lang="en">
    <head>
      <meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
      <title>无标题文档</title>
      <style>
    
      </style>
    </head>
    
    <body>
    <form id="form">
      name:<input id="nameInput" type="text" name="name">
      age:<input id="ageInput" type="text" name="age">
    </form>
    </body>
    <script>
    
      let person={
        data:{
          name:"",
          age:""
        }
      }
    
      Object.defineProperty(person,"name",{
        get(){
          console.log("name被获取了")
          return person.data.name
        },
        set(value){
          console.log("name被修改了")
          person.data.name=value
          document.getElementById("nameInput").value=value
    
        }
      })
    
      Object.defineProperty(person,"age",{
        get(){
          console.log("age被获取了")
          return person.data.age
        },
        set(value){
          console.log("age被修改了")
          person.data.age=value
          document.getElementById("ageInput").value=value
        }
      })
    
      var form=document.getElementById("form")
    
      form.addEventListener("input",(e)=>{
        let value=e.target.value
        let name=e.target.getAttribute("name") //触发Object.defineProperty(person,"name",{get()} 的get 函数
        person[name]=value
      })
    
    </script>
    
    </html>
    

    封装函数后完整例子:

    <html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
        <title>无标题文档</title>
        <style>
    
        </style>
    </head>
    
    <body>
    <form id="form">
        name:<input id="name" type="text" name="name">
        age:<input id="age" type="text" name="age">
    </form>
    
    </body>
    <!--<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>-->
    <script>
    
        let person={
            data:{
                name:"",
                age:""
            }
        }
    
        //封装函数
        function binding(obj,key){
            Object.defineProperty(obj,key,{
                get(){
                    console.log(key+"被获取了")
                    return obj.data[key]  //用data存放属性值并返回给get函数,相当于在获取这个属性值时获得了存放在data里面的值
                },
                set(val){
                    console.log(key+"被修改了")
                    obj.data[key]=val
                     document.getElementById(key).value=val
                }
            })
        }
    
        var key
        for(key in person.data){
            binding(person,key)
        }
    
        var form=document.getElementById("form")
    
        form.addEventListener("input",(e)=>{
            let value=e.target.value
            let name=e.target.getAttribute("name")
            person[name]=value  //设置person属性值。把触发Object.defineProperty(){} 的set 函数
            console.log(person[name])})
    
    </script>
    
    </html>
    

     实现双向绑定:


PS:

function binding(obj,key){
        Object.defineProperty(obj,key,{
            get(){
                console.log(key+"被获取了")
                return obj.data[key]
            },
            set(val){
                console.log(key+"被修改了")
                obj.data[key]=val
                 document.getElementById(key).value=val
            }
        })
    }

疑问:1. Object.defineProperty( ) 里面的

get 函数:return obj.data[key]  
set 函数:obj.data[key]=val,

这两个 obj.data[key] 为什么不直接使用 obj[key] 来赋值呢?为什么person 对象里的 name和 age 属性外面还要再加一层data呢?

回答:因为传入 Object.defineProperty(obj,key,{}) 的是 obj 和 key 这两个变量。Object.defineProperty(obj,key,{}) 会对 obj.key 的属性值进行监听,在获取值时,会自动调用get( ) 函数;设置值时,会自动调用 set( ) 函数。

所以,如果在 get 和set 函数中直接使用 obj.key,就会一直触发 get() 和 set( ) 函数,导致死循环,堆栈溢出。

因此,需要在 person 对象的 name 和 age 属性外面再加一层 data,用于在get和set 中使用。也就是说,使用 person.data.name 来存放 person.name 的值。 也就是说,person 对象有属性值有

  • person.data.name
  • person.data.age
  • person.name
  • person.age

     Object.defineProperty(obj,key,{
            get(){
                console.log(key+"被获取了")
                return obj.data[key]
            },
            set(val){
                console.log(key+"被修改了")
                obj.data[key]=val
                 document.getElementById(key).value=val
            }

 直接使用 obj.name 导致堆栈溢出:

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值