Vue教程09:双向绑定对象中属性原理

阅读更多系列文章请访问我的GitHub博客,示例代码请访问这里

以06课双向绑定为基础实现双向绑定对象属性

代码参考/lesson09/01. watch监听对象属性.html

在06课中,实现了对数据的监听,当然Proxy对象同时也可以监听对象类型的数据,我们需要做的只是将相应的变化渲染到页面中。

首先,我们先将_data中的值修改为

// 用_data保存数据
let _data = {
  userInfo: {
    name: 'lee',
    age: 18
  }
}

HTML修改为:

<div id="app">
  姓名:{{userInfo.name}}<br/>
  年龄:{{userInfo.age}}
</div>

因此需要将render方法中查找模板中要写入值的正则从

/\{\{\w+\}\}/g

替换为:

/\{\{[\w\.]+\}\}/g

这样就可以匹配到HTML模板中的{{userInfo.name}},但我们从对象中获取数据必须使用data[“userInfo”][“name”],而不能直接用data[userInfo.name],因此接下来需要拼接出相应的格式查找到数据,就可以将数据渲染到页面中。

完整代码如下:

// 将模板中{{}}内部的内容,用数据替换
el.innerHTML = template.replace(/\{\{[\w\.]+\}\}/g, str => {
  str = str.substring(2, str.length - 2);

  // 将userInfo.name拼接为["userInfo"]["name"],以便查找对象中的属性。
  return eval('_data["' + str.split('.').join('"]["') + '"]')
})

这样一来我们就实现了将对象中的属性数据渲染到页面中。
当然同理,我们就可以实现对象中属性的双向绑定,完整代码如下:

JavaScript:

const el = document.querySelector('#app')

// 获取标签内容作为页面模板
let template = el.innerHTML

// 用_data保存数据
let _data = {
  userInfo: {
    name: 'lee',
    age: 18
  }
}

// 为_data设置拦截,通过修改data中属性的值,来修改
let data = new Proxy(_data, {
  // 当数据修改时,会被set方法拦截,从而得知数据被修改的值value,之后可以将value渲染到页面中,obj为_data
  set(obj, key, value) {
    console.log(`设置${key}属性为${value}`)

    eval('_data["' + key.split('.').join('"]["') + '"] = value')

    // 将数据渲染到页面中
    render()
  }
})

// 初始化时渲染页面
render()

function render() {
  // 将模板中{{}}内部的内容,用数据替换
  el.innerHTML = template.replace(/\{\{[\w\.]+\}\}/g, str => {
    str = str.substring(2, str.length - 2);

    // 将userInfo.name拼接为["userInfo"]["name"],以便查找对象中的属性。
    return eval('_data["' + str.split('.').join('"]["') + '"]')
  })

  // 但检测到数据改变时,将input的值同步
  Array.from(document.getElementsByTagName('input'))
    // 查找含有v-model属性,即设置了双向绑定的input
    .filter((ele) => ele.getAttribute('v-model'))
    .forEach((input, index) => {
      const name = input.getAttribute('v-model')
      eval('input.value = data["' + name.split('.').join('"]["') + '"]')

      // 输入框的值变化时,将data中相应属性的值改变
      input.oninput = function () {
        data[name] = input.value
        eval('data["' + name.split('.').join('"]["') + '"] = input.value')
      }
    })
}

HTML:

<div id="app">
  <input type="text" v-model="userInfo.name"><br />
  姓名:{{userInfo.name}}<br/>
  年龄:{{userInfo.age}}
</div>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值