preface

The thought of writing “based on Koa what can we do because now most front for the node is just stay in the case of how to use, did not like the Vue, React, and so on to study the node, but the node is essential for the development of our front end, because he is a service in the front of back end, First of all, Node will certainly not be assigned to the professional backend to develop node, only the front-end to develop, so the face of the students who have no in-depth understanding of Node is undoubtedly very uncomfortable, so I think this article is needed to take you to contact node.

The reason for choosing Koa over Express is that most of express’s apis are already well packaged, but enterprise applications need good scalability, and Koa is a better fit. (Actually because the last article had a flag that said KOA)

Koa source code analysis

Introduction of Koa

Koa’s design is very clever, with only five built-in concepts, as follows:

  • Application:
  • -Leonard: The Context?
  • Request: the Request
  • Response: the Response
  • -Leonard: Middleware.

When writing applications based on Koa, we do two things:

  • Write middleware to handle requests
  • Define utility methods or properties on an Application instance, Context instance, Request instance, or Response instance for use in middleware. So, writing a Koa app is about enhancing Koa in five different dimensions: Application, Context, Request, Response, and Middleware.

The middleware

Koa middleware is an important concept. Koa middleware is similar to the Onion model. As follows:

const Koa = require('koa');
const app = new Koa();

app.use(async (ctx, next) => {
  console.log(1);
  await next();
  console.log(2);
});

app.use(async (ctx, next) => {
  console.log(3);
  await next();
  console.log(4);
});

app.use(async (ctx, next) => {
  console.log(5);
  await next();
  console.log(6);
});

app.use(async ctx => {
  ctx.body = 'Hello World';
});

app.listen(3000);
Copy the code

The output is 1, 3, 5, 6, 4, 2

Three or two things about the Koa process

Initialize the Koa

Const app = new Koa(); To build an application instance. The main steps in Koa constructot are as follows:

constructor() { super(); this.middleware = []; this.context = Object.create(context); // Some methods, such as assert, onError, throw this.request = object. create(request); // Some attributes, such as headers, URL, path this.response = object.create (response); // Some methods, such as: body, redirect}Copy the code

The process of initialization is to add the corresponding properties and methods and initialize them.

Adding Middleware

New middleware in a Koa application can be added directly through the use() method mounted on the app. In essence, app.use() is just a function that pushes the middleware into the middleware queue, as follows

function use(middleware) {
    if(typeof middleware ! = ='function') throw new TypeError('middleware must be a function! '); / /... this.middlewares.push(middleware);return this;
}
Copy the code

Listen on port

The Koa application instance calls app.listen(port number) to listen on the port. The port callback is actually handleRequest, which creates the CTX and passes the CTX along with the middleware function this.Handlerequest (CTX, fn). This method is used to assign parameters to CTX when a request comes in and then call Middleware in turn to process the request. At this point, the Koa application is started.

functionlisten(... args) { debug('listen');
    const server = http.createServer(this.callback());
    returnserver.listen(... args); }function callback() {
    const fn = compose(this.middleware);
    if(! this.listenerCount('error')) this.on('error', this.onerror);
    const handleRequest = (req, res) => {
        const ctx = this.createContext(req, res);
        return this.handleRequest(ctx, fn);
    };
    return handleRequest;
}
4. 
Copy the code

In the above code const fn = compose(this.middleware); The compose implementation calls middleware in turn to handle requests, which is the key to koA’s Onion model implementation:

function compose (middleware) {
 if(typeof middleware ! = ='function') throw new TypeError('middleware must be a function! ');
 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'))
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      if(! fn)return Promise.resolve()
      try {
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}
Copy the code

Handle the request

Listen calls this.createcontext (req, res) and this.handlerequest (CTX, fn) to the port listening to the request. The former listens for the value of the request header and initializes properties such as header and context. It also uses req to ensure that context.request is properly formatted and initialized.

functioncreateContext(req, res) { const context = Object.create(this.context); const request = (context.request = Object.create(this.request)); const response = (context.response = Object.create(this.response)); context.app = request.app = response.app = this; context.req = request.req = response.req = req; context.res = request.res = response.res = res; request.ctx = response.ctx = context; request.response = response; response.request = request; context.originalUrl = request.originalUrl = req.url; State = {}; context.state = {};return context;
}
Copy the code

For this.Handlerequest (CTX, FN) it is more about concatenating the middleware used. The handleRequest function, after executing the middleware function, executes the handleResponse function. The intermediate functions are our business logic, which may have the functions of setting response headers, printing logs, setting response.body, etc. HandleResponse converts ctx.response to the corresponding HTTP response and returns it.

function handleRequest(ctx, Middleware) {
    const res = ctx.res;
    res.statusCode = 404;
    const onerror = err => ctx.onerror(err);
    const handleResponse = () => respond(ctx);
    onFinished(res, onerror);
    return Middleware(ctx)
        .then(handleResponse)
        .catch(onerror);
}
Copy the code

conclusion

Summary of some features of Koa, first Koa involving light suitable for secondary packaging, in the face of complex business scenarios has a wealth of extensibility, especially multiple modules in the enterprise development rely on reuse of scene, but it also has some disadvantages, in people development because there is no unified code specification is easy to cause confusion structure is clear, Considering these circumstances, we can encapsulate Koa twice, encapsulating it into a corresponding business framework. At the same time, even if you don’t do business framework, you can also make some magic changes to Koa based on Koa source code to add some methods and attributes suitable for business development. In short, Koa is just a foundation for a tall building, and it’s up to the architect to build it. Koa has provided a lot of freedom, so it’s up to you to do it.


You may also wish to think together, based on this lightweight design we can do what changes to make it more suitable for our business scenarios, welcome everyone to speak, my ideas and practice will be shared in the next article.