Use node An express framework

Handwritten an express series

Basic usage of express

const express = require("express");
const app = express();

app.get("/test", (req, res, next) => {
  console.log("Club technicians in place*1");
//   res.end("club technician starts service 1");
  next();
});

app.get("/test", (req, res, next) => {
  console.log("Club technicians in place*2");
  res.end("Club technician starts service 2");
});

app.listen(8888, (err) => {
  !err && console.log("Is there a big health care in the club?");
});
  • When I visited localhost:8888/test, I returned: Club technician starts service 2, and the server prints
Club technicians in place*1
 Club technicians in place*2
  • What can you see from above?

    • By default, express returns an app object after introducing the call
    • app.listen starts the process listening port
    • Each time a request is received, the corresponding url and method will trigger the corresponding callback function mounted on the app
    • Calling the next method will trigger the next

Together to implement a simple express framework

  • Define the express file entry that belongs to us, which is implemented by class
class express {

}

module.exports = express;
  • The required native module http creates a process listening port
const { createServer } = require("http");
  • Define the listen method and listen port for class
class express {
  listen(...args) {
    createServer(cb).listen(...args);
  }
}
  • In this way, we can call the listen of http module by calling the listen of class. We can ignore the cb here first. You should know that every time we receive a request, we will call the cb function. This is encapsulated by the createServer native module

The implementation receives the request trigger

  • Implement app get app. Post and other methods

    • At present, when we receive the response, we will trigger cb this callback function. Let's print it and see what the parameters are?
class express {
  cb() {
    return (req, res) => {
      console.log(res, res, "Here comes the guest");
    };
  }
  listen(...args) {
    createServer(this.cb()).listen(...args);
  }
}
  • It is found that the req and res at this time are exactly the readable and writable streams we want

  • Start writing get and post methods

    • Note here that there are routes that are '/', which will be triggered once regardless of any route
  constructor() {
    this.routers = {
      get: [],
      post: [],
    };
  }

  get(path, handle) {
    this.routers.get.push({
      path,
      handle,
    });
  }
  post(path, handle) {
    this.routers.post.push({
      path,
      handle,
    });
  }
  • During initialization, the array of get and post is defined to store the corresponding path and handle
  • When a route callback needs to be triggered, first find the handle method of the corresponding url under the corresponding request mode, and then trigger the callback
  • How to find the handle method corresponding to the url in the corresponding request mode? It needs to traverse once when receiving the request

    • Here we need to consider matching multiple routes, which means that we may encounter test routes with two get methods as in the beginning
  cb() {
    return (req, res) => {
      const method = req.method.toLowerCase();
      console.log(this.routers[method], ",method");
      const url = req.url;
      this.routers[method].forEach((item) => {
        item.path === url && item.handle(req, res);
      });
    };
  }
  listen(...args) {
    createServer(this.cb()).listen(...args);
  }
  • The above finds the corresponding array according to the method, traverses to find the requested route and triggers the callback. At this time, the data can be returned normally
[ { method: 'get', path: '/test', handle: [Function] } ] ,method
  • At this point, the simplest express has been completed, but we seem to have forgotten the most important middleware

Complete the most important middleware functions

  • First of all, we need to know that there are two kinds of express middleware. One is with routing, that is to determine whether to trigger according to the routing
  • The other is without routing, such as static resources It is triggered once when the user accesses any route
  • Then we need an all array to store that any route needs to be triggered by matching
 constructor() {
    this.routers = {
      get: [],
      post: [],
      all: [],
    };
  }
  • The previous direct push method was too rough If the user needs the middleware function and does not transmit routes, it needs to do special processing. Here, it is processed through an intermediate function
  • Transform the get and post methods and define the handleAddRouter method
  handleAddRouter(path, handle) {
    let router = {};
    if (typeof path === "string") {
      router = {
        path,
        handle,
      };
    } else {
      router = {
        path: "/",
        handle: path,
      };
    }
    return router;
  }

  get(path, handle) {
    const router = this.handleAddRouter(path, handle);
    this.routers.get.push(router);
  }

  post(path, handle) {
    const router = this.handleAddRouter(path, handle);
    this.routers.post.push(router);
  }

  use(path, handle) {
    const router = this.handleAddRouter(path, handle);
    this.routers.all.push(router);
  }

  • Before each addition, the handleAddRouter is triggered once. If the middleware with empty path directly passes in the function, the path will set it to '/'
  • We also left a point, the implementation of next, because we now add the array of all, which means that there may be multiple middleware, so multiple routes may be triggered when a request comes

Note here that promise The then source code implementation is a little similar to the next of express, the onion ring of koa and the middleware implementation of redux. When you can really understand the front and back-end framework source code, you will find that they are mostly similar

  • Reading my article is enough to break all the front and rear source code And it can be written by hand. We only learn the core, focus on key learning and grow savagely!

Implement next

  • Idea:

    • First, find all matching routes
    • Then execute one by one (see the call of next)
  • Define the search method to find all matching routes
  search(method, url) {
    const matchedList = [];
    [...this.routers[method], ...this.routers.all].forEach((item) => {
      item.path === url && matchedList.push(item.handle);
    });
    return matchedList;
  }

  cb() {
    return (req, res) => {
      const method = req.method.toLowerCase();
      const url = req.url;
      const matchedList = this.search(method, url);
    };
  }
  • matchedList is all the routes we want to find
  • To complete next, we need to store req, res and matchedlist into the closure and define the handle method
  handle(req, res, matchedList) {
    const next = () => {
      const midlleware = matchedList.shift();
      if (midlleware) {
        midlleware(req, res, next);
      }
    };
    next();
  }
  cb() {
    return (req, res) => {
      const method = req.method.toLowerCase();
      const url = req.url;
      const matchedList = this.search(method, url);
      this.handle(req, res, matchedList);
    };
  }

  • In this way, we have completed the next method. As long as we manually call next, we will call the next matching route callback function

  • Less than a hundred lines of code completed this simple express framework

Write at the end

  • As long as you carefully implement it yourself according to my articles, it should be no problem to get P6 within a year
  • The road is simple. I hope you can really learn the principle of framework through these articles, and then write some frameworks yourself to a higher level
  • I'm Peter. I used to be the architect of 200000 person super group desktop software. Now I work in Mingyuan cloud as the head of the front end of the branch. At present, two middle and senior front ends need to be recruited in Shenzhen. I look forward to your arrival in the direction of 3D data visualization
  • If you feel this article is helpful to you, don't forget to read and pay attention to it Our technical team will continue to produce original articles and witness your growth together

Tags: Javascript node.js Front-end html5 css

Posted by chito on Tue, 24 May 2022 16:59:57 +0300