双向数据绑定分为两个部分:

  1. 数据更新 ——-> 通知视图更新
  2. 用户操作改变视图数据 ————> 通知数据更新

主要由以下几个部分组成:

  1. Observer: 用于进行数据劫持,同时通过get / set钩子闭包来调用Dep进行依赖收集
  2. Dep:用于收集响应式数据中每一项(对象的属性)的依赖,也就是相应的watcher
  3. Watcher: 用于订阅响应式数据,原理是在初始化时通过去访问一次响应式数据触发其get钩子,从而订阅某数据,并且还用于指定数据变更后的回调函数。
  4. Compiler: 编译模板,将模板的花括号中的变量,指令渲染成对应数据,并且给每项数据新建一个watcher,绑定回调函数用于更新DOM

Observer

  1. function Observer(data, vm) {
  2. observe(data)
  3. function observe(data) {
  4. for (let key in data) {
  5. let _value = data[key]
  6. let dep = new Dep()
  7. if (_value && typeof value === 'object') observe(_value)
  8. Object.defineProperty(data, key, {
  9. configurable: true,
  10. enumerable: true,
  11. get: function reactiveGetter() {
  12. if (Dep.target) dep.addSub(Dep.target)
  13. console.log('访问');
  14. return _value
  15. },
  16. set: function reactiveSetter(newValue) {
  17. if (_value === newValue) return
  18. _value = newValue
  19. if (newValue && typeof newValue === 'object') observe(newValue)
  20. console.log('修改');
  21. dep.notifyAll()
  22. return _value
  23. }
  24. })
  25. }
  26. }
  27. }

Dep

  1. function Dep() {
  2. this.subscribers = []
  3. this.addSub = function (sub) {
  4. if (this.subscribers.includes(sub)) return
  5. this.subscribers.push(sub)
  6. }
  7. this.notifyAll = function () {
  8. this.subscribers.forEach(sub => {
  9. sub.update()
  10. })
  11. }
  12. }

watcher

  1. function Watcher(target, cb, vm) {
  2. this.update = function () {
  3. cb()
  4. }
  5. this.runWatch = function () {
  6. Dep.target = this
  7. this.getTargetValue(target, vm)
  8. Dep.target = null
  9. }
  10. this.getTargetValue = function (target, vm) {
  11. let value = vm.$data
  12. target.split('.').forEach(key => value = value[key])
  13. return value
  14. }
  15. this.runWatch()
  16. }

Compiler

  1. function Compiler(el, vm) {
  2. console.log(vm);
  3. this.$el = el
  4. Array.from(this.$el.children).forEach(child => {
  5. console.dir(child)
  6. if (child.nodeType === 1) {
  7. let reg = /\{\{(.*)\}\}/
  8. let text = child.textContent.trim()
  9. if (text && reg.test(text)) {
  10. let key = RegExp.$1.trim()
  11. child.innerText = vm.$data[key]
  12. const watcher = new Watcher(key, () => {
  13. child.innerText = watcher.getTargetValue(key, vm)
  14. }, vm)
  15. }
  16. }
  17. })
  18. }