数据发生改变,界面发生更新并不是理所当然的

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  {{message}}
  {{message}}
  {{message}}

  {{name}}
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      message: 'aaa'
      name: 'bbb'
  }
  })
</script>
</body>
</html>

上面输出message的数据在浏览器上,思考两个问题:
1、app.message修改数据,vue内部是如何监听到message数据发生改变的?
这里vue内部使用到了Object.defineProperty方法。这个方法是如何作用的呢?
首先在定义两个变量message和name时,data会将它们传入vue内部,传入后,vue内部会拿到一个对象obj:

  const obj = {
    message: 'aaa',
    name: 'bbb'
  }

之后vue对obj进行遍历,拿到obj中的每一个key,同时也可以拿到每一个key所对应的值:

  Object.keys(obj).forEach(key => {
    let value = obj[key]
  })

这时就可以对值value的改变进行监听了,那是如何监听的呢,就使用到了Object.defineProperty方法。
Object.defineProperty方法需要传入三个参数,第一个参数是我们的对象,第二个参数是对象要定义的属性,
第三个参数应该是要定义的值,但在vue中是这样做的:

    Object.defineProperty(obj, key, {
      set(newValue){
        console.log('监听' + key + '改变');
        value = newValue
      },
      get(){
        console.log('获取' + key + '对应的值')
        return value
      }
    })

这里对所有的属性全部重新进行了定义,这样一旦对一个属性进行重新赋值的时候,就会执行set方法,这样就可以对改变进行监听:
图片说明
同样也可以监听到获取值的动作:
图片说明
2、当数据发生改变时,vue是如何知道通知哪些人发生改变的
监听到属性改变时,vue就要告诉正在用该属性的元素发生改变,那么vue是如何知道哪些元素正在使用该属性呢?这里就涉及到对

<div id="app">
  {{message}}
  {{message}}
  {{message}}

  {{name}}
</div>

进行解析,在解析时会获取到该元素的值,就会调用一次get方法,这样就会知道哪些元素对get进行了调用,对这些属性在vue内部进行重新的命名后就使用到了发布订阅者模式,也就是让这些元素对set方法进行订阅:

  //发布者
  class  Dep {
    constructor() {
      this.subs = []
    }
    addSub(wather) {
      this.subs.push(wather)
    }
    notify() {
      this.subs.forEach(item => {
        item.update()
      })
    }
  }
  //订阅者
  class Watcher {
    constructor(name) {
      this.name = name;
    }
    update() {
      console.log(this.name + 'update');
    }
  }

  const dep = new Dep()

  const w1 = new Watcher('张三')
  dep.addSub(w1)

  const w2 = new Watcher('李四')
  dep.addSub(w2)

  const w3 = new Watcher('王五')
  dep.addSub(w3)

发布者中定义了一个subs数组保存订阅的对象,addSub方法用来将对象添加到subs数组中,notify方法遍历数组并通知所有的订阅者数据发生了改变;订阅者中对订阅者的名称进行保存,同时需要对自身数据进行更新。
这样我们在set方法中对dep对象调用它的notify方法时就会通知到所有使用name的元素数据发生了改变。
图片说明