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

A simple introduction

Functions of Emitter are similar to DOM object events. Emitter emits events through emit, DOM emits events through dispatchEvent, and Dom objects register events through addListener. Dom and Emitter can be yourself, talking to yourself, emitting and on.

Details of the code

To implement the functions described above, an Emitter needs a container to store handlers registered by on. When Emitter calls emit, it calls the handlers registered by on internally.

function Emitter () {
    this._cbs = {} / / container
}
var EmitterProto = Emitter.prototype
EmitterProto.on = function (event, fn) {(this._cbs[event] = this._cbs[event] || []).push(fn) // Put it in the container
}
EmitterProto.emit = function (event) {
    var callbacks = this._cbs[event], args
    if (callbacks) {
        callbacks = callbacks.slice(0)
        args = slice.call(arguments.1)
        for (var i = 0, len = callbacks.length; i < len; i++) {
            callbacks[i].apply(this._ctx, args) // Call the registered handler function iteratively}}}Copy the code

Code optimization

Call to optimize

Now you’re going to register set,get, and Mutate on Emitter. You should write this:

var __emitter__ = new Emitter()
__emitter__.on('set'.function(){
    // ...
})
__emitter__.on('get'.function(){
    // ...
})
__emitter__.on('mutate'.function(){
    // ...
})
Copy the code

If it is a chain call, the code should be as follows:

var __emitter__ = new Emitter()
__emitter__.on('set'.function(){
    // ...
}).on('get'.function(){
    // ...
}).on('mutate'.function(){
    // ...
})
Copy the code

How do you implement chain calls? It’s really easy, just return this,

// ...
EmitterProto.on = function (event, fn) {
    // ...
    return this
}
// ...
Copy the code

That way, it’s easier for others to use your Emitter (serving others 🤘).

Performance optimization

Apply (this._ctx, args). Apply /call (this._ctx, args); With thousands of Emitter events in Vue (used in conjunction with the Observer), isn’t it energy intensive? In the source code, the above emit implementation is applyEmit, and the emit function accepts fixed parameters as follows:

/** * The internal, faster emit with fixed amount of arguments * using function.call */ / A is key, B is value, c: propagate emitterproto.emit =function (event, a, b, c) {}
Copy the code

conclusion

Read about An internal implementation of Dom object-like events, chain calls, and performance differences between Call and apply. The joy of reading source code is here, always through the source code to learn the knowledge that did not notice before, ha ha ha 😄.

The code address

Detailed source code, with Chinese annotations

Emitter has no unit tests, which is probably too easy 😓

Continuously updated… ❤ ️