# 模拟 Vue2.x 双向数据监听

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>defineproperty</title>
</head>
<body>
  <div id="app">
    hello
  </div>
  <script>
    // 模拟 Vue 中的 data选项
    let data = {
      msg: 'hello',
      count: 10,
      age: 20
    }
    // 模拟Vue 实例
    let vm = {}

    // 一个属性转换
    Object.defineProperty(vm, 'msg', {
      // 可枚举 可遍历
      enumerable: true,
      // 可配置(可以使用 delete 删除,通过 defineProperty 重新定义)
      configurable: true,
      get() {
        console.log('get ---->', data.msg)
        return data.msg
      },
      // 赋值的时候执行
      set(newVal) {
        console.log('set ----->', newVal)
        // 相同的不执行
        if (newVal === data.msg) return
        data.msg = newVal
        document.querySelector('#app').textContent = data.msg
      }
    })

    vm.msg = 'hello world'
    console.log(vm.msg)
  </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  • 如果是多个属性,需要循环整个对象,而 Vue 2.x 是通过递归的方式遍历整个对象,从而实现响应式
  • 这里使用 forEach 方式模拟
// 模拟 Vue 中的 data选项
let data = {
  msg: 'hello',
  count: 10,
  age: 20
}
// 模拟Vue 实例
let vm = {}
Object.keys(data).forEach((key) => {
  // 一个属性转换
  Object.defineProperty(vm, key, {
    // 可枚举 可遍历
    enumerable: true,
    // 可配置(可以使用 delete 删除,通过 defineProperty 重新定义
    configurable: true,
    get() {
      console.log('get ---->', data[key])
      return data[key]
    },
    // 赋值的时候执行
    set(newVal) {
      console.log('set ----->', newVal)
      // 相同的不执行
      if (newVal === data[key]) return
      data[key] = newVal
      document.querySelector('#app').textContent = data.msg
    }
  })
})

// 如果是多个属性,需要循环整个对象,而 Vue 2.x 是通过递归的方式遍历整个对象,从而实现响应式
vm.msg = 'hello world'
vm.count = 5
console.log(vm.msg)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34