I am participating in Nuggets Creators Camp # 4 (link: juejin.cn/post/706419…) , click here to learn more!

One, foreword

This article is mainly aimed at 1 to 3 years of primary and intermediate front-end engineers, hoping to learn with you.

Why learn source code?

After I learned the daily development and application of Vue, React and Node, I seemed to encounter some bottlenecks and didn’t know how to go further.

Front-end learning routes are popping up all over the web, and there are also public accounts that sell anxious tweets.

On the basis of combining many front-end routes, I gradually came up with my own ideas: Front-end engineers first need is a software engineer, for non-computer professionally-trained worker front-end code farmer, are you familiar with some scenes, such as knowledge in daily work or further study front, sum up experience in one or analysis for a long time of arcane knowledge, corresponding computer field is early some one theory, that is to say we need to “learning, And know why. “After all, front-end technology is computer-based. Secondly, the need to have their own in-depth exploration of the technical field, and learning framework source code is to talk to the author, experience the ideas and logic of excellent technical people, can improve the technical level more quickly.

Why Koa?

Looking at the source code is only a method, means, not an end, this share is Koa source code interpretation.

The Koa framework plays an important role in Node learning as an HTTP middleware framework that makes Web applications and apis easier to write. This is often mentioned in interviews, such as “Do you understand Node middleware mechanics?” “, “How is the KOA Onion model implemented?” .

In terms of learning cost, Koa is different from large frameworks such as Vue and React, but has simple code, simple overall implementation, clever, and easy to expand small frameworks.

The enterprise Node framework and egg.js applications are also based on Koa encapsulation,

What is Koa? What are the components?

Koa is a new Web framework, built by the same people behind Express, that aims to be a smaller, more expressive, and more robust cornerstone of web application and API development.

When we clone the source of Koa framework from github Koa repository, we can see that there are only four parts of Application. Js, context.js, request.js and Response. js through the lib file, Koa is composed of these four parts.

How are they related?

  1. From the package.json file of Koa source code, the entry file is obtainedlib/application.js, knownapplication.jsContext is a Koa entry and is also an Application library that mounts context, Request, and Response to the Application instance
  2. context.jsUpper and lower library
  3. request.jsIs the request library
  4. response.jsIs the response library

Two, martial arts secrets

1. Use frames

At the beginning of learning a new framework, I must be able to use the framework as the goal, and AT the beginning of MY study of Koa, I also required myself to take this as the goal in a short period of time. I can choose the point I am interested in or enter it through an application scenario. It is not recommended to directly follow the official documents all the way. Without a point of interest or application scenario to support it, not only will you not experience the true strengths and weaknesses of Koa, but you will gradually forget what you have learned over time. So I encourage you to learn things with a purpose, so that you can learn more helpful knowledge in a relatively short period of time.

There are a lot of articles on how to use Koa in nuggets, which I will not cover in this article. You can also check out my KOA2 building project to learn more.

2. Understand source code

When working with the framework, we often get a lot of weird questions, and the official documentation tends to be more of a basic tutorial, and there are questions that you can’t find online.

Then there is the author in the use of Vue, and sometimes the Vue code written by the big guy is very different, often issued such an exclamation, “the original Vue can be written like this”, “I feel that the Vue and the big guy to learn is not a”, “I really will Vue?” . Investigate its reason, in addition to the big guy has more rich basic knowledge, but also certainly dabble in the source code, and we encounter problems that can not be solved, but also have a new idea – in the source code to find the answer.

Only with a solid grasp of Koa’s source code can you cope with complex requirements.

3. Implementation framework

When we read the source code, it will be a good experience of the framework coding style, then we may want to Koa framework concise, clever design, can achieve a simple version of their own Koa, of course, to better understand the ingenious design of the framework

How to achieve Koa first needs to understand the author’s usual approach, outline, find the entry point, draw the architecture diagram. Through the flow chart or architecture diagram of the way to clarify the whole framework context, and then one by one to achieve the function.

Of course, can also be “first copy, and then think of their own way” to achieve, after all, “the world code a copy”, oh, literati thing is not called “copy”, called “reference”.

Type II of Koa

Today we mainly learn Koa martial arts secret in the second type – to understand the source code, we speak today is mainly Koa2 version, we through the use of the framework Koa, has been the most simple use:

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

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

app.listen(3000);
Copy the code

With this simple example, let’s try to analyze:

  • From experience with front-end engineering, you can guess where the Koa entry file should bepackage.jsonmainAttributes are indicated;
  • Koa instance is passednewKeyword created, that is, in the entry file, the default export is Koa application class;
  • App is a Koa instance, mounted on this instanceuse,listenMethods.

You can either pull down the Koa framework directly from Github, or you can use the framework Koa, install the Koa dependency from node_modules/ Koa /**

Check it out in the package.json file of the Koa project source code

{
  "main": "lib/application.js"
}
Copy the code

With guess 1 verified, we go to the lib/application.js file and export the application application class, which is the Koa class, by default.

We found not only listen and use methods, but also onError and callback methods in the Application class.

When we look at these methods, the syntax doesn’t seem too difficult, but the combination of them doesn’t seem to make sense.

Because there will be some code robustness processing and some default configurations in the Koa framework source code that may add to the learner’s burden, we will only cover the important parts today.

1. The application module

What does this entry file mainly do?

  1. Build the Application instance, koA entry
  2. Create an HTTP service and call back on success
  3. Callbacks are functions that are passed in through use registration
  4. Mount context, Request, and Response to the Application instance
  5. When koA is used, there are CTX objects and you construct your own CTX objects
  6. Pass the CTX object to the callback function
  1. The introduction of
// Check whether it is a genterator method
const isGeneratorFunction = require("is-generator-function");
// Set the anonymous space for debug
const debug = require("debug") ("koa:application");
// A callback that is executed when the request completes
const onFinished = require("on-finished");
/ / the introduction of the response
const response = require("./response");
// Middleware mechanism, peel the onion model
const compose = require("koa-compose");
/ / into the context
const context = require("./context");
/ / introduce requiest
const request = require("./request");
// Tool kit for determining HTTP status
const statuses = require("statuses");
// Node native event-driven module
const Emitter = require("events");
// Node native toolkit module
const util = require("util");
// Node native Stream
const Stream = require("stream");
// Node native HTTP module
const http = require("http");
// Used to return the property specified by the object
const only = require("only");
// Convert the koA-based generator median to Promise based middleware
const convert = require("koa-convert");
// Give some information
const deprecate = require("depd") ("koa");
// The module used to create Http errors
const { HttpError } = require("http-errors");
Copy the code
  1. Application class
module.exports = class Application extends Emitter {
  constructor(options) {
    // Call the parent class to construct
    super(a);// Set some initial values
    options = options || {};
    // Middleware queue array
    this.middleware = [];
    // Create a context/request/ Response local library to mount to the KOA instance
    this.context = Object.create(context);
    this.request = Object.create(request);
    this.response = Object.create(response);
    // Mount the debug inspect method to custom
    if (util.inspect.custom) {
      this[util.inspect.custom] = this.inspect; }}// Listen on the port and start the service
  listen(. args) {
    debug("listen");
    // Use HTTP modules to create services, pass in callback functions, and listen on ports
    const server = http.createServer(this.callback());
    returnserver.listen(... args); }// Returns three properties on this object
  toJSON() {
    return only(this["subdomainOffset"."proxy"."env"]);
  }
  inspect() {
    return this.toJSON();
  }
  // Collect the middleware and push the middleware queue
  use(fn) {
    if (typeoffn ! = ="function")
      throw new TypeError("middleware must be a function!");
    // Check if this is a generators function,v3 will have generators enabled
    if (isGeneratorFunction(fn)) {
      deprecate(
        "Support for generators will be removed in v3. " +
          "See the documentation for examples of how to convert old middleware " +
          "https://github.com/koajs/koa/blob/master/docs/migration.md"
      );
      // Convert the middleware to the promise form
      fn = convert(fn);
    }
    debug("use %s", fn._name || fn.name || "-");
    this.middleware.push(fn);
    return this;
  }
  callback() {
    // Pass the middleware queue to the Onion module
    const fn = compose(this.middleware);

    if (!this.listenerCount("error")) this.on("error".this.onerror);
    / / handle the request
    const handleRequest = (req, res) = > {
      // Build the CTX object
      const ctx = this.createContext(req, res);
      return this.handleRequest(ctx, fn);
    };

    return handleRequest;
  }
  // Used to process requests
  handleRequest(ctx, fnMiddleware) {
    // Get the native writable stream through the passed CTX
    const res = ctx.res;
    // Set the default statusCode 404
    res.statusCode = 404;
    const onerror = (err) = > ctx.onerror(err);
    const handleResponse = () = > respond(ctx);
    onFinished(res, onerror);
    return fnMiddleware(ctx).then(handleResponse).catch(onerror);
  }
  // Build the context object
  createContext(req, res) {
    // Separate instances are built for each request and response
    const context = Object.create(this.context);
    const request = (context.request = Object.create(this.request));
    const response = (context.response = Object.create(this.response));
    // Assign this to the app of context/request/ Response
    context.app = request.app = response.app = this;
    // Assign the passed req to the req of context/request/response
    context.req = request.req = response.req = req;
    // Assign the incoming res to the context/request/response res
    context.res = request.res = response.res = res;
    // Assign context to CTX of request/ Response
    request.ctx = response.ctx = context;
    // Assign response to request's response
    request.response = response;
    // Assign request to request for Response
    response.request = request;
    // Assign req's URL to context's originalUrl and request's originalUrl
    context.originalUrl = request.originalUrl = req.url;
    / / mount state
    context.state = {};
    return context;
  }
  // Handle exceptions
  onerror(err) {
    if (404 === err.status || err.expose) return;
    if (this.silent) return;

    const msg = err.stack || err.toString();
    console.error(`\n${msg.replace(/^/gm."")}\n`);
  }
  static get default() {
    returnApplication; }};// Respond to the request
function respond(ctx) {
  // Allow BYPassing KOA by setting CTx. respond
  if (false === ctx.respond) return;
  // Determine the writable property on the CTX prototype chain
  if(! ctx.writable)return;

  const res = ctx.res;
  let body = ctx.body;
  const code = ctx.status;

  // ignore body
  if (statuses.empty[code]) {
    // strip headers
    ctx.body = null;
    return res.end();
  }
  // body: json
  body = JSON.stringify(body);
  if(! res.headersSent) { ctx.length = Buffer.byteLength(body); } res.end(body); }// Mount HTTP exception handling to HttpError in KOA
module.exports.HttpError = HttpError;
Copy the code
  1. application.jsIs a Koa entry, and has the other three modules mounted on its own instance.
  2. application.jsExported from the inheritance fromEmitterInstance constructor. Enables event listening and event firing capabilities in the Koa framework.
  3. useThe method pushes the incoming function to the middleware queue and returns the Koa instance, facilitating chain calls.
  4. listenThe method is essentially righthttp.createServerAfter the encapsulation, at the same time create a successful call underApplicationClass callback method, incallbackMethod to merge middleware, context processing, rightRequestresponseTo deal with.
  5. As for thecomposeThe Onion model is not in the Koa source code, this parsing will not look atkoa-composeNext oneSource code implementation of KoaWill be parsed and implemented.

2. The context module

  1. The introduction of
// Node native toolkit module
const util = require("util");
// HTTP failed processing
const createError = require("http-errors");
const httpAssert = require("http-assert");
// Set the proxy library to delegate the proxy
const delegate = require("delegates");
// HTTP status toolkit
const statuses = require("statuses");
/ / operation cookies
const Cookies = require("cookies");
// To emphasize uniqueness, can only be accessed within the current module, not elsewhere
const COOKIES = Symbol("context#cookies");
Copy the code
  1. Delegate
/** * Response delegation. */

delegate(proto, "response") // So that ctx.response can be accessed on CTX
  .method("attachment")
  .method("redirect")
  .method("remove")
  .method("vary")
  .method("has")
  .method("set")
  .method("append")
  .method("flushHeaders")
  .access("status")
  .access("message")
  .access("body")
  .access("length")
  .access("type")
  .access("lastModified")
  .access("etag")
  .getter("headerSent")
  .getter("writable");

/** * Request delegation. */

delegate(proto, "request") // Ctx.request can be accessed on CTX
  .method("acceptsLanguages")
  .method("acceptsEncodings")
  .method("acceptsCharsets")
  .method("accepts")
  .method("get")
  .method("is")
  .access("querystring")
  .access("idempotent")
  .access("socket")
  .access("search")
  .access("method")
  .access("query")
  .access("path")
  .access("url")
  .access("accept")
  .getter("origin")
  .getter("href")
  .getter("subdomains")
  .getter("protocol")
  .getter("host")
  .getter("hostname")
  .getter("URL")
  .getter("header")
  .getter("headers")
  .getter("secure")
  .getter("stale")
  .getter("fresh")
  .getter("ips")
  .getter("ip");
Copy the code
  1. Can be achieved bynode_modulesfinddelegateLibrary, it can be seen
  • The method function is a registration function
  • Getters are registered read-only properties
  • The access function registers readable and writable properties
  1. CTX is a proxy for request and Response and provides convenient access to request and response objects. For example, we want to access ctx.Response. status, but we can access it directly through the delegate.

3. Request module

  1. requestThe module abstracts and encapsulates the Request object. Some attributes are treated specially.
  2. requestModules are relatively simple and have a lot of code, so I don’t want to read too much.

3. The response module

  1. responseThe module abstracts and encapsulates the response object, and performs special processing on some attributes.
  2. responseModules are relatively simple and have a lot of code, so I don’t want to read too much.

Four,

This article describes why to learn source code, how to learn source code and how to learn a new framework.

Koa is just a framework, the learning method in this article can be applied to many frameworks, of course, you also have a better learning method, also welcome to say from the comment section, together with growth.

At the same time, this article is the second Koa series, the third Koa is on the road source code implementation, welcome to like, attention, support a wave. Thanks!!

I am Front-end Creek, welcome interested students to pay attention to the front-end Creek official account, also welcome to add me on wechat wXL-15153496335.