Through the source code reading, familiar with the KOA implementation process, while some unfamiliar syntax and some wonderful places to learn

I briefly read several core parts of KOA, combined with the introduction of some blogs to understand the implementation process, and picked out a few important points to record. Practice projects to do a few, there have been a few doubts, took to find the answer.

  1. Why ctx.body can be directly feedback to Response. body
  2. What is a slice? – An onion
  3. What combination of middleware is used?

compose

Koa has two main capabilities, HTTP encapsulation, and a combination of middleware. Compose completes the second function.

'use strict'
module.exports = compose
function compose (middleware) {
  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array! ') // Check if it is an array
  for (const fn of middleware) {
    if (typeoffn ! = ='function') throw new TypeError('Middleware must be composed of functions! ')}return function (context, next) {
    let index = -1
    return dispatch(0)
    function dispatch (i) {
      if (i <= index) return Promise.reject(new Error('next() called multiple times')) // Use the promise shortcut
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next // Next is not added to middleware, but is an argument to the compose function,
      if(! fn)return Promise.resolve() // At the bottom of the stack,
      try {
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}
Copy the code

Just contact JS to see this code is still very difficult, especially in the use of Promise, and when the callback function is executed, etc., look closely, the core is in a sentence:

return Promise.resolve(fn(context, dispatch.bind(null, i + 1))); 
return Promise.resolve(fn(context, () = > dispatch(i + 1))); // Same as the previous line
Copy the code

Middleware acts as a maintenance array of middleware, or maintenance stack compose, that uses MidLleware to continuously distribute tasks down, known as dispatches in the code.

delegates

Delegates function as delegate agent, if accessing CTX. Body, it is accessing CTx.response.body

Delegate (proto,’request’) in context.js

module.exports = Delegator;
function Delegator(proto, target) {
  if(! (this instanceof Delegator)) return new Delegator(proto, target); // Determine if the constructor needs to be called again.
  this.proto = proto; // Proto is an object in context.js
  this.target = target; // target is a key of the 'request' or 'response' object
  this.methods = [];
  this.getters = [];
  this.setters = [];
  this.fluents = [];
}
Delegator.prototype.method = function(name){
  var proto = this.proto;
  var target = this.target;
  this.methods.push(name);
  proto[name] = function(){ // Delegate proxy core statement.
    return this[target][name].apply(this[target], arguments);
  };
  return this;
};
Delegator.prototype.access = function(name){
  return this.getter(name).setter(name);
};
Delegator.prototype.getter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.getters.push(name);
  proto.__defineGetter__(name, function(){
    return this[target][name];
  }); DefineProperty () or get is the older usage
  return this;
};
Delegator.prototype.setter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.setters.push(name);
  proto.__defineSetter__(name, function(val){
    return this[target][name] = val;
  }); DefineProperty () or directly set is now used
  return this;
};
Delegator.prototype.fluent = function (name) {
  var proto = this.proto;
  var target = this.target;
  this.fluents.push(name);
  proto[name] = function(val){
    if ('undefined'! =typeof val) {
      this[target][name] = val;
      return this;
    } else {
      return this[target][name]; }};return this;
};
Copy the code

It’s a small amount of code, more than 50 lines

There are several lessons to be learned.

if(! (this instanceof Delegator)) return new Delegator(proto, target);
Copy the code

Call the constructor again by checking this object, so it can be used externally

const delegates = require('delegates')  
delegates('arg1'.'arg2'),
// The new Function() Function can be wrapped as an object.
Copy the code

This is reminiscent of new.target, which is also used to determine whether the constructor is called through new

(New.target also includes reflect.construct ()) the internal judgment mechanism may be similar to the this judgment above.

Plus, whether it’s a method or a getter or whatever, it’s going to end up there

return this
Copy the code

This has the advantage of continuously calling method and so on. Such as

delegate(proto, 'response')
  .method('attachment')
  .method('redirect')
  .method('remove')
Copy the code