vue数据响应式原理

283 阅读2分钟

vue2.0数据响应式原理

概念

vue中,当data中的数据一旦发生变化,会自动同步试图进行更新

前置

想实现数据响应式,那么我们需要先了解关于对象的属性的特性

getOwnPropertyDescriptor

该方法返回一个对象,该对象描述给定对象上特定属性的配置(即直接存在于对象上而不在对象的原型链中的属性)。返回的对象是可变的

const data = {
  name: '张三',
  age: 14
}
console.log(Object.getOwnPropertyDescriptor(data, 'name'))
  1. configurable: 是否能够通过delete删除属性
  2. enumerable: 是否可枚举,也就是能否通过for-in循环得到属性
  3. value: 属性值
  4. writable: 是否可修改
  5. getter: 在读取属性时,调用的函数
  6. setter: 在写入属性时调用的函数

defineProperty

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

属性配置

在未针对对象特性设置之前,属性是可删除、枚举、修改


const data = {
  name: '张三',
  age: 14
}
console.log(Object.getOwnPropertyDescriptor(data, 'name'))
data.name = '赵四'
for(key in data) {
  console.log(key)
}
delete data.name

通过Object.defineProperty配置之后


const data = {
  name: '张三',
  age: 14
}
// 配置对象
Object.defineProperty(data, 'name', {
  configurable: false,
  enumerable: false,
  writable: false
})
console.log(Object.getOwnPropertyDescriptor(data, 'name'))
data.name = '赵四'
for(key in data) {
  console.log(key)
}
delete data.name

数据读写

读数据,会触发get,并且get内部需要返回数据,才能读取到数据

但是由于Object.defineProperty的缺陷,直接在return 后面 返回data.name,会进入死循环,所以需要通过一个全局变量进行管理

const data = {
  name: '张三',
  age: 14
}
​
let value = data.name
​
Object.defineProperty(data, 'name', {
  get () {
    console.log('读数据了')
    return value
  },
  set (val) {
    console.log('写数据了')
  }
})
​
console.log(data.name)

写数据


const data = {
  name: '张三',
  age: 14
}
​
let value = data.name
​
Object.defineProperty(data, 'name', {
  get () {
    console.log('读数据了')
    return value
  },
  set (val) {
    console.log('写数据了' + val)
    value = val
  }
})
​
console.log(data.name)
data.name = '赵四'
console.log(data.name)

读数据了
张三
写数据了赵四
读数据了
赵四

实现简易的数据响应式

Object.defineProperty不是为了实现数据响应式出现的,而是vue利用了Object.defineProperty的特性从而实现了数据响应式

单层数据响应


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div>
    <p id="name"></p>
    <p id="age"></p>
  </div>
  <script>
    // 定义数据
    const data = {
      name: '张三',
      age: 19
    }
​
    // 同步视图的方法
    function setView() {
      document.getElementById('name').innerHTML = data.name 
      document.getElementById('age').innerHTML = data.age 
    }
    setView()
​
    // 利用循环对数据进行监听
    Object.keys(data).forEach(key => {
      // 利用外部数据进行数据的读写控制
      let getValue = data[key]
      Object.defineProperty(data, key, {
        // 读数据
        get () {
          return getValue
        },
        // 写数据
        set (val) {
          getValue = val
          setView()
        }
      })
    })
  </script>
</body>
</html>

多层数据响应


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div>
    <p id="name"></p>
    <p id="age"></p>
    <p id="idCard"></p>
    <p id="address"></p>
  </div>
  <script>
    // 数据
    const data = {
      name: '张三',
      age: 19,
      info: {
        idCard: 233455667889001223,
        address: '下沙'
      }
    }
​
    // 同步视图的方法
    function setView() {
      document.getElementById('name').innerHTML = data.name 
      document.getElementById('age').innerHTML = data.age 
      document.getElementById('idCard').innerHTML = data.info.idCard 
      document.getElementById('address').innerHTML = data.info.address 
    }
    setView()
​
    observer(data)
​
    // 递归的方法
    function observer(data) {
      Object.keys(data).forEach(key => {
        if (typeof data[key] === 'object') {
          return observer(data[key])
        }
        let getValue = data[key]
        Object.defineProperty(data, key, {
          get () {
            return getValue
          },
          set (val) {
            getValue = val
            setView()
          }
        })
      })
    }
  </script>
</body>
</html>

数据无法响应的情况

cn.vuejs.org/v2/guide/re…

对象

对象新增的数据无法响应

数组

通过索引修改数组

\