The main principle of Vue is mainly used to define the functions Dep, Watcher, observer. Let’s use these functions to briefly implement the data binding and interdependence parts of the VUE constructor and tease out their relationships. Omitted compilation and proxy proxy and other complex logic.

Dep

Dep is a dependency class, briefly implemented as

class Dep {
  constructor () {
    // Put the observer of the property at that time
    this.subs = []
  }
}
// target is used to mount the watcher observer at that time
Dep.target = null
Copy the code

observer

Do attribute hijacking and do other things


function observer (vm, key, val) {
  let dep = new Dep()
  Object.defineProperty(vm, key, {
    /** * get does two main things * 1. Collect and observe the wathcer of the current key (i.e. operations that depend on the current key) * 2. Get the value * /
    get () {
      // This is function 1
      if (Dep.target) {
        dep.subs.push(Dep.target)
      }
      // This is action 2
      return val
    },
    /** * set is also two things * 1. Modify target value * 2. Executes watcher */ that depends on the current key
    set (newVal) {
      // This is function 1
      val = newVal
      // This is action 2
      for(cb of dep.subs) {
        cb.call(vm)
      }
    }
  })
}
Copy the code

Watcher

Watcher is an observer class used to create operations (such as instructions, renders, calculated properties, etc.) that depend on attributes.

class Watcher {
  /** * vm: instance * cb: operation function that depends on an attribute */
  constructor (vm, cb) {
    // Mount the current operation to the Dep
    Dep.target = cb
    /** * Performs the operation with two functions * 1. Initializes the operation * 2. Trigger the property's get method so that the current CB is collected */
    cb.call(vm)
    Dep.target = null}}Copy the code

demo

  • Jsfiddle online code
  • The other is the GitHub code

So let’s write an example using the function defined above

<div>
  <p class="text"></p>
<div>
Copy the code
let vm = new Vue({
  // Suppose there is data
  data: {msg: 1},
  // There is a certain V-text instruction during compilation, which is abstracted as a vText function that depends on the attribute MSG (this function also represents all operations that depend on other attributes).
  renderFun: {
    vText () {
      document.querySelector('.text').innerText = this.msg
    }
  }
})
// Modify the vUE instance value and observe the change
vm.msg = 333
Copy the code

So let’s also write a simple constructor for vue

class Vue {
  constructor (options) {
    let data = options.data
    let renderFun = options.renderFun
    // initData
    Object.keys(data).forEach(key= > {
      observer(this, key, data[key])
    })
    // Simulate computation-dependent operations for properties, watcher, instructions, etc
    Object.keys(renderFun).forEach(key= > {
      new Watcher(this, renderFun[key])
    })
  }
}
Copy the code

Implementation process

See the two links in the demo section for the complete code

  1. To create a Vue instance, runnew Vue()
  2. Initialize data, yesdataAttribute hijacking
    • During hijacking, a dependency queue (dep.subs) and value (val) for the current property are created within the closure.getConducting observerwatcherCollection and worth acquiring;setUpdate values and depend on the queuewatcherThe implementation of the
  3. For example, during compilationcomputed\watcherorTemplate compilationIn the process ofinstructionThe function is initialized with therenderFunInstead of
  4. forrenderFunEach function function innew Watcher()work
  5. In order tovTextFor example, innew Wathcer()In the process of
    1. willvTextMount to global universalDep.targeton
    2. performvText, which has readvm.msgTo trigger the get of MSG attribute and enterDep.targetJudgment,Dep.targetnamelyvTextCollected intomsgthesubsDepends on the queue, at this pointvTextExecution completed, pageinnetTextHas been modified
    3. willDep.targetempty
  6. performvm.msg = 333, the triggermsgtheset
    1. setTo modify themsgThe value of the
    2. To performmsgDepends on all in the queuewatcherIs a function of, i.evText, the page ofinnerTextBe updated synchronously

conclusion

The relationship is to collect the watcher for the current property in the Observer GET into the DEP and execute the collected watcher in the Observer set.

The actual implementation process of Vue is not as simple as described above. For example, the implementation of Watcher is not a simple traversal implementation, and the Observer has been greatly simplified. Omitted functions such as _proxy, defineReactive, and so on.

Write such a minimalist implementation is mainly to comb through the trunk, reduce the difficulty of reading the source code.