preface

The interview is often asked some Vue source related problem, usually, I will catch up with the nuggets before the interview on gluten to deal with the interview, what the principle of two-way binding ah, what virtual dom tree ah, actually I never carefully studied, one is really more food, can’t be used, a second job also don’t give yourself difficult. Behind but think about it, a lot of things, for it is easy, not for the difficult, difficult to set up (weight) to progress, to a little more every day Vue source, on the source selection of Vue, I chose the most of the old version (0.1) 😬 (really afraid yourself look hard, reading mode to read, from easy to difficult a file of a file, Read a file to see it again after the unit test, after fully read and copy and paste the code into a local running test cases for the block of code to write some Chinese annotation, play tag on their own warehouse, began to write articles to comb summary (should have hesitated before writing an article on the nuggets, because this kind of Vue source code parsing articles have a lot of, but also write very well, I write again whether to return existence significance, the back still want to write, running water summary is also good 💧).

The body of the

This is the second article I sent about Vue source code. When I started to read Vue source code, I chose to read files (modules) which are less code and relatively independent functions. For example, the Batcher and Binding code combined is less than 200 lines, and Binding also introduces Batcher. Batcher+Binding should be able to write a summary article. Binding may not be very clear to me now. Binding constructor (compiler, directive, value should be an observer, publisher), etc.

A simple introduction

  • Batcher is an asynchronous batch task queue. There are three methods: push, Flush, batch, and reset.
  • A Binding (dirs,subs) is an asynchronous update to the value (dirs,subs) that is bound by the Batcher.

Batcher

Batcher only has more than 50 lines of code, I directly posted the code, and then the key places will be full of comments, that sentence is like taking is cheap, show me the code lol 😄

// constructor
function Batcher() {
    this.reset()
}
// Alias of the prototype chain
var BatcherProto = Batcher.prototype

/ / reset method
BatcherProto.reset = function () {
    // method inside utils.js, utils.hash => object.create (null)
    // It returns an empty object with no prototype chain,
    // this.has = {}, but this.has.__proto__ === null
    this.has = utils.hash() / / the job id as the key
    this.queue = [] / / the queue
    this.waiting = false // Whether to wait for batch processing
}

// The task is queued
// Job consists of two parts: id and execute
BatcherProto.push = function (job) {
    if(! job.id || !this.has[job.id]) {
        this.queue.push(job)
        this.has[job.id] = job
        if(!this.waiting){
            this.waiting = true
            / / nextTick internal implementation for requestAnimationFrame | | setTimeout (fn, 0)
            // Bind's implementation returns a closure that encapsulates call, which is not implemented through Bind
            utils.nextTick(utils.bind(this.flush, this))}}else if(job.override){ // Whether to override
        var oldJob = this.has[job.id]
        oldJob.cancelled = true // oldJob is not executed
        this.queue.push(job)
        this.has[job.id] = job
    }
}

// Tasks are queued in batches
BatcherProto.flush = function () {
    // before flush hook
    / / hooks
    if (this._preFlush) this._preFlush()
    // do not cache length because more jobs might be pushed
    // as we execute existing jobs
    // The comments to the source code describe this situation,
    // waiting is true, flush is already in javascript message queue,
    // This. Queue may still have jobs pushed into it,
    // Or it is possible when executing these jobs, so do not cache length
    for (var i = 0; i < this.queue.length; i++) {
        var job = this.queue[i]
        if(! job.cancelled) { job.execute() } }this.reset()
}
Copy the code

Binding

Properties in Vue instances have a corresponding Binding, while DOM directives and some computed properties depend on a Binding. Let me draw a diagram to illustrate the relationship between some of the properties.


BindingProto.update = function (value) {
    // If it is not an attribute or a function, it is directly assigned
    if (!this.isComputed || this.isFn) {
        this.value = value
    }
    // Directives subs are not empty
    if (this.dirs.length || this.subs.length) {
        var self = this
        // Enter the asynchronous queue for batch processing
        bindingBatcher.push({
            id: this.id,
            execute: function () {
                if(! self.unbound) { self._update() } } }) } }/** * Actually update the directives. */
BindingProto._update = function () {
    var i = this.dirs.length,
        value = this.val()
    while (i--) {
        this.dirs[i].$update(value) / / dirs updates
    }
    this.pub() // Notify subs of updates,
}
BindingProto.pub = function () {
    var i = this.subs.length
    while (i--) {
        this.subs[i].update()
    }
}
Copy the code

Explain the attributes in the Binding constructor:

  1. Deps (dependences: rely on)
  2. Dirs (Instructions on the DOM)
  3. Subs translate subclasses, or you can interpret computed attributes that depend on binding values

The Binding constructor and unbinding method are used to define the subs, deps, and Binding relationships.

  1. Binding constructor:
function Binding(compiler, key, isExp, isFn) {
    // ...
    this.dirs = [] / / directive instruction
    this.subs = []
    this.deps = []
    // ...
}
Copy the code
  1. Unbinding method (destructor):
BindingProto.unbind = function() {/ /... var subswhile (i--) {
        subs = this.deps[i].subs
        var j = subs.indexOf(this)
        if (j > -1) subs.splice(j, 1)
    }
    // ...
}
Copy the code

You can see from the code above

  1. Binding.deps. Subs contains a binding
  2. Binding. deps is an array of binding instances
  3. Binding. subs is also an array of binding instances
  4. Binding and subs,binding and deps are one-to-many relationships.

conclusion

😢😢😢… Binding is a Binding for Batcher.

The code address

Batcher

Batcher unit tests

Binding

Binding unit Tests

Potholes encountered by running test cases locally: There were asynchronous operations in the unit test. When I ran it locally, the test passed and a bit of code was changed. Karma was not a watch change, and the test case was automatically run. The test case of the browser window after refreshing the Karma passed again.

Continuously updated… ❤ ️