chencl1986 / Blog

Welcome to lee's blog.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Vue教程06:数据同步、双向绑定原理

chencl1986 opened this issue · comments

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

利用Proxy实现数据同步

代码示例:/lesson06/01. 数据同步

利用Proxy拦截可以对数据的修改,得知数据被修改时,将模板中相应属性的部分替换,就完成了简单的数据同步功能。

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

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

// _data为初始化
let _data = {
  name: 'lee',
  age: 18
}

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

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

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

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

    return _data[str];
  })
}

HTML:

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

数据双向绑定

在data改变时,查找页面中含有相应v-model属性的input标签,将input的value值改为data的值。
同时在input标签的值变化时,将input的值设置为data的值,就实现了双向绑定。

JavaScript:

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

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

// _data为初始化
let _data = {
  name: 'lee',
  age: 18
}

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

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

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

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

    return _data[str];
  })

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

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

HTML:

<div id="app">
  <input type="text" v-model="name"><br />
  姓名:{{name}}<br/>
  年龄:{{age}}
</div>