Vue教程09:雙向綁定對象中屬性原理

閱讀更多系列文章請訪問個人GitHub博客,示例代碼請訪問這裏

以06課雙向綁定爲基礎實現雙向綁定對象屬性

代碼參考/lesson09/01. watch監聽對象屬性.htmlhtml

在06課中,實現了對數據的監聽,固然Proxy對象同時也能夠監聽對象類型的數據,咱們須要作的只是將相應的變化渲染到頁面中。git

首先,咱們先將_data中的值修改成github

// 用_data保存數據
let _data = {
  userInfo: {
    name: 'lee',
    age: 18
  }
}
複製代碼

HTML修改成:bash

<div id="app">
  姓名:{{userInfo.name}}<br/>
  年齡:{{userInfo.age}}
</div>
複製代碼

所以須要將render方法中查找模板中要寫入值的正則從app

/\{\{\w+\}\}/g
複製代碼

替換爲:less

/\{\{[\w\.]+\}\}/g
複製代碼

這樣就能夠匹配到HTML模板中的{{userInfo.name}},但咱們從對象中獲取數據必須使用data["userInfo"]["name"],而不能直接用data[userInfo.name],所以接下來須要拼接出相應的格式查找到數據,就能夠將數據渲染到頁面中。ui

完整代碼以下:spa

// 將模板中{{}}內部的內容,用數據替換
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:code

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>
複製代碼
相關文章
相關標籤/搜索