Writing aexpress
A series of
express
Basic usage of
const express = require("express"); const app = express(); App. Get ("/test ", (the req, res, next) = > {the console. The log (" club technician in place * 1 "); // res.end(" Club technician starts service 1"); next(); }); App. Get ("/test ", (the req, res, next) = > {the console. The log (" club technician in place * 2 "); Res. end(" Club technician starts service 2"); }); app.listen(8888, (err) => { ! Err && console.log(" Is there big health in the clubhouse?" ); });Copy the code
- When I visit
localhost:8888/test
When it returns:Club technician starts service 2
, the server printed
Club technician in place *1 Club technician in place *2Copy the code
- What can be seen from above?
express
One is returned after the call is introduced by defaultapp
object
app.listen
The process listening port is started- Each time a request is received, the corresponding
url
andmethod
Will trigger the corresponding mount inapp
To the corresponding callback function - call
next
Method that triggers the next one
Let’s implement a simple oneexpress
The framework
- Define what is ours
express
File entry, used hereclass
To implement the
class express {
}
module.exports = express;
Copy the code
- Required native modules
http
To create a process listening port
const { createServer } = require("http");
Copy the code
- Give the class definition
listen
Method to listen on a port
class express { listen(... args) { createServer(cb).listen(... args); }}Copy the code
- This can be done by calling
class
的listen
To callhttp
The modulelisten
Herecb
We can ignore that, but you have to know that every time you receive a request, you’re going to callcb
Delta function, this is delta functioncreateServer
The native module encapsulates it for us
Implementation receives the request trigger
- implementation
app.get app.post
Methods such as- Now when we receive the response, the cb callback is triggered, so let’s print it out and see what the argument is.
Class express {cb() {return (req, res) => {console.log(res, res, "guest "); }; } listen(... args) { createServer(this.cb()).listen(... args); }}Copy the code
- Found that at this time
req
和res
That’s what we want for readable and writable streams.
- Begin to write
get
和post
methods- Note that there are routes that are ‘/’, which are 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,
});
}
Copy the code
- Initialize the get and POST arrays to store the corresponding
path
和handle
.
-
To trigger the route callback, first find the handle method of the url in the corresponding request mode and then trigger the callback.
-
How to find the handle method corresponding to the URL in the corresponding request mode? We go through it once when we get the request
- We’re thinking about matching multiple routes, which means we might have two of them as we started with
get
The way oftest
routing
- We’re thinking about matching multiple routes, which means we might have two of them as we started with
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); }Copy the code
- Find the corresponding array according to method, traverse to find the route of the request, trigger the callback, at this time can normally return data
[ { method: 'get', path: '/test', handle: [Function] } ] ,method
Copy the code
- The easiest one at this point
express
It’s done, but we seem to have forgotten the most important piece of middleware
Accomplish the most important middleware functionality
- The first thing to know is,
express
There are two types of middleware, one with routing, which determines whether to trigger based on the route - The other type is unrouted, such as static resources. Is triggered once when the user accesses any route
- Then we need one
all
Array storage this arbitrary route needs to match the triggered
constructor() {
this.routers = {
get: [],
post: [],
all: [],
};
}
Copy the code
- The previous direct push method was too rough. If the user needs middleware functionality and does not pass routing, then special processing is required, which is handled by an intermediate function
- transform
get
,post
Method, definitionhandleAddRouter
methods
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);
}
Copy the code
- Trigger each time before adding
handleAddRouter
If it ispath
For empty middleware, pass function directly, thenpath
Set it to ‘/’
- We also left a point,
next
Implementation of, because we now addall
This array means that there may be multiple middleware, so a single request may trigger multiple routes
Note here that the promise. Then source code implementation is slightly similar to express’s next, koA’s Onion Ring, and Redux’s middleware implementation, and when you can actually dig into the source code of the front and back frameworks, most of them are similar
- Read my article, enough to break all the back and forth source code. And can be handwritten out, we only learn the core, grasp the key study, savage growth!
implementationnext
- Ideas:
- The first step is to find all the routes that match
- Then execute them one by one (look
next
A call)
- define
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);
};
}
Copy the code
matchedList
That’s all the routes we’re looking for
- In order to complete the
next
, we are going toreq ,res , matchedList
Store it in a closure, definehandle
methods
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); }; }Copy the code
- So we’re done
next
Method, just call it manuallynext
The next matched route callback is called
- It takes less than a hundred lines of code to do this simple
express
The framework
Write in the last
-
As long as you according to my these articles to realize time, study hard, for one year with a P6 should have no problem, I was at https://github.com/JinJieTan/Peter- all the source code for the warehouse, remember to a star
-
I hope you can learn the principles of frameworks through these articles, and then write some of your own frameworks to move to a higher level
-
My name is Peter, who used to be an architect of desktop software for the 200,000 super group. Now I am working in Mingyuan Cloud as the head of the branch’s front end. At present, Shenzhen needs to recruit two middle and senior front end, majoring in 3D data visualization
-
If you found this article helpful, don’t forget to give it a thumbs up.