What is express
?
Fast, Unopinionated, minimalist Web Framework for Node. Express is a Web server based on Node, which is characterized by: Fast, simple, and small.
Features:
- Robust Routing Robust routing system
- Focus on high performance
- Super high test coverage
- HTTP helpers (redirection, caching, etc)
HTTP
Various enablers such as redirection, caching, etc - View System Supporting 14+ Template Engines supports more than 14 View engines
- Content negotiation?
- Executable for generating applications quickly
When I first started working with Node, the backend framework I used was Express, and I could only write the simplest interfaces based on the official documentation API. Koa compared (see the reference comparison koa | analysis), may use in the internal inherited development package module, can be introduced directly, without having to download separately. Existing Express-Generator scaffolding, global download installation can be used
Let’s cut to the chase.
Init project Initializes the project
mkdir express_practice
cd express_practice
npm init -y
Copy the code
The Installation is installed
This is a Node.js module available through the NPM Registry. Express is a Node module that can be downloaded through the NPM Registry repository. Before installing, download and install Node.js.node.js 0.10 or higher is required. Install node > 0.10 Installation is done using the NPM install command.
npm i express --save
or
npm i express
or
npm i express -S
Copy the code
Use the Usage
The new app. Js
Create a file for the Web application.
touch app.js
Copy the code
Introducing dependency modules
- Express Core Module
- App. Router is a class
- User is a secondary routing instance
const express = require('express');
const app = express();
const Router = app.Router;
const user = new Router();
Copy the code
User Indicates the request under the route
- Three parameters
- Next call does not pass the parameter, is to give way by the system in no matching path and method, to the next middleware; Passing in parameters is throwing an error
user.get('/remove', (req, res, next) => {
res.send(`/user/remove`)
next();
})
user.get("/add", (req, res, next) => {
res.end('/user/add');
next();
})
Copy the code
App application request
app.get('/'.function (req, res) {
res.send('Hello World')
})
app.get('/'.function (req, res) {
res.send('Hello World')})Copy the code
Register the user secondary routing system
- Registered middleware
- Interface 1: /user/remove
- Interface 2: /user/add
app.use('/user', user);
Copy the code
Error capture processing
- Error middleware
- Four parameters, the first parameter is the caught error
- The error was caused by calling next in the previous request
app.use(function(err, req, res, next) {
if (err) res.send(err);
})
Copy the code
Listen on port
app.listen(3000);
Copy the code
Express Core Concepts
The Express core uses the CommonJS specification, which is basically callback based middleware and requests.
Outlet Structure Directory structure
- express
- index.js
- lib
- express.js
- application.js
- router
- index.js
- layer.js
- route.js
Copy the code
Fulfill implementation
Create a package file and its subfiles
mkdir express && cd express
touch index.js
mkdir lib && cd lib
touch express.js
touch application.js
mkdir router && cd router
touch index.js
touch layer.js
touch route.js
Copy the code
express/index.js
Express is essentially a function that returns an instance of an Application class
module.exports = require("./lib/express");
Copy the code
Express uses the CommonJS specification. Exports and imports of files are: require(), module.exports respectively.
express/lib/express.js
The introduction ofApplication
Application systems andRouter
Routing system
- Application and routing systems have their own roles to play.
createApplication
Is a factory function, instantiated inside the functionApplication
Instance object and return it for each introductionexpress
The package module generates a single application instance.createApplication.Router
You mount a Router class that uses the routing system independently.
const Application = require('./application');
const Router = require('./router');
Copy the code
createApplication
A function that instantiates an application instance
function createApplication() {
return new Application();
}
Copy the code
Add the Router attribute to the function
createApplication.Router = Router;
Copy the code
Note: A function is also an object, and attributes can be added.
The export function
module.exports = createApplication;
Copy the code
express/lib/application.js
Download third-party tool libraries
npm i methods --save
Copy the code
Importing dependency packages
http
node
Core module, does not need to download the installation, used to callhttp.createServer()
Create a Web service.Router
Routing system class, see detailsrouter.jsmethods
Third-party librariesdeclare const methods: string[];
const http = require('http');
const Router = require('./router');
const methods = require('methods');
Copy the code
createApplication
类
- Instance attributes
config
- Instance attributes
_router
, memory optimization will be followed *
function Application(){
this.config = {};
this._router = new Router;
}
Copy the code
Set instance method
- If the parameter is set to 2, the operation is performed
- If the parameter is set to 1, the value operation is performed
Application.prototype.set = function (key, value) {
if (arguments.length === 2) {
this.config[key] = value;
} else {
return this.config[key]; }}Copy the code
Layze_route instance method
Performance optimization:
- Modify the instance properties in Constructor.
- Replace the Application once instantiated, initial _router attribute, the client if not call any method | requests param method, just call the use method, can cause performance of waste.
- There is only one _router, which means you only need to instantiate it once for assignment.
function Application(){
this.config = {};
- this._router = new Router;
}
Copy the code
Application.prototype.lazy_route = function() {
if (!this._router) return this._router = new Router();
}
Copy the code
Param instance method
The implementation is left to the _router routing system
Application.prototype.param = function (key, handler) {
this.lazy_route();
this._router.param(key, handler);
}
Copy the code
Use instance method
The implementation is left to the _router routing system
Application.prototype.use = function(path, handler) {
this.lazy_route();
this._router.use(path, handler);
}
Copy the code
Method Instance method
Handlers are passed in to the _Router routing system
methods.forEach(method= > {
Application.prototype[method] = function(path, ... handlers) {
if (method === 'get' && arguments.length === 1) {
return this.set(path);
}
this.lazy_route();
this._router[method](path, ...handlers);
}
})
Copy the code
Listen instance method
- call
http.createServer(function(req, res))
, create a server service; - Prepare a
done
Method, used when the routing system does not have a matching route (method and path)next
Methods; - call
_router.handle
And the incomingreq
.res
.done
Three parameters make way for the system to process the request and respond to it; - Specify the port number to listen on, optionally passing in the callback function.
Application.prototype.listen = function() {
let server = new http.createServer((req, res) = > {
function done() {
res.end(`Cannot ${req.method} ${req.url}`);
}
this.lazy_route();
res.send = res.end;
this._router.handle(req, res, done); }); server.listen(... arguments); }Copy the code
exportApplication
类
module.exports = Application;
Copy the code
express/lib/router/index.js
Introducing dependency modules
const url = require('url');
const methods = require('methods');
const Route = require('./route');
const Layer = require('./layer');
Copy the code
Declare aproto
object
- for
Router
Inside the constructor of the classrouter
Function inheritance
const proto = {};
Copy the code
createRouter
类
constructor
Function returns one insiderouter
Function, which contains multiple properties/methods- Why override the constructor?
- Used for level-2 routing
// User is a middleware, const user = new app.Router(); // Req, res, and next are passed in when the application instance calls use app.use('user', user); // The user function call is the router call, which then calls router.handle Copy the code
function Router() {
const router = function (req, res, next) {
router.handle(req, res, next);
}
router.stack = [];
router.__proto__ = proto;
router.paramsCallback = {}; // { key: [fn, fn] }
return router;
}
Copy the code
Note:
-
The router mechanism is a classic publish/subscribe model.
-
Subscription phase:
Call use/ get/ post/… Add a layer to the stack array
-
Release phase:
- call
app.listen()
When seeApplication.prototype.listen
- call
-
There are two ways to instantiate a Router:
- call
app[method](path, handlers)
when - call
app.Router
Example of secondary routing
- call
Param method
proto.param = function (key, handler) {
if (this.paramsCallback[key]) {
this.paramsCallback[key].push(handler);
} else {
this.paramsCallback[key] = [handler]; }}Copy the code
The route method
- Instantiate a
route
routing - Instantiate a
layer
层 - Complete a subscription
- Return to the
route
proto.route = function (path) {
const route = new Route();
const layer = new Layer(path, route.dispatch.bind(route));
layer.route = route;
this.stack.push(layer);
return route;
}
Copy the code
Method of use
- Calling the route method
- Instantiate a
layer
层 layer.route
Set toundefined
Value, which is easy to identify as common middleware at the time of releaseroute
routing
proto.use = function (path, handler) {
if (typeof path === 'function') {
handler = path;
path = "/";
}
const layer = new Layer(path, handler);
layer.route = undefined;
this.stack.push(layer);
}
Copy the code
[method] method
- Each time it is called, a new layer is added
layer
- The trigger
route[method]
methods.forEach(method= > {
proto[method] = function (path, ... handlers) {
let route = this.route(path); route[method](handlers); }})Copy the code
Process_params parameter method
proto.process_params = function (layer, req, res, done) {
if(! layer.keys || layer.keys.length ===0) {
return done();
}
// key [id, name]
let keys = layer.keys.map(item= > item.name);
let params = this.paramsCallback;
let idx = 0;
function next () {
if (keys.length === idx) return done();
let key = keys[idx++];
processCallback(key, next);
}
next();
function processCallback(key, out) {
let fns = params[key];
if(! fns) {return out();
}
let idx = 0;
let value = req.params[key];
function next() {
if (fns.length === idx) return out();
letfn = fns[idx++]; fn(req, res, next, value, key); } next(); }}Copy the code
Handle method
- Before release subscription methods | middleware;
out
Function isApplication.prototype.listenInternally incomingdone
Method, indicating the end;dispatch
Is the core method, using recursive method;removed
Is the subpath of the secondary route. If the request path is /user/add, the first match is made/user
At this time, the path is /add. When /add fails to match, it will be recursively calleddispatch
Call the next layer and append /user back.
proto.handle = function (req, res, out) {
let { pathname } = url.parse(req.url);
// Start processing from the first layer of the stack
let idx = 0;
let removed = "";
// The core method err is passed from the next function call to indicate error capture
const dispatch = err= > {
// Stack is empty, directly out
if (idx === this.stack.length) return out();
// If removed does not indicate that the last round failed to be matched, the current round is re-matched and the removed round is added again
/ / empty removed
if (removed) {
req.url = removed + req.url;
removed = "";
}
// Get the current layer and pointer down
let layer = this.stack[idx++];
// If an error is caught
if (err) {
// The current layer is not error middleware
if(! layer.route) { layer.handle_err(err, req, res, dispatch); }else {
Use ((err, req, res, next) => {})
// Leave it to it
returndispatch(err); }}else {
// No errors and match
if (layer.match(pathname)) {
if(! layer.rout) {// If it is middleware, execute the corresponding method directly
// Delete the middleware path here
// /user/add /
if(layer.handler.length ! = =4) {
if(layer.path ! = ='/') { // If middleware is /
removed = layer.path;
req.url = req.url.slice(removed.length)
}
layer.handle_request(req, res, dispatch);
} else{ dispatch(); }}else {
/ / routing
if (layer.route.methods[req.method.toLowerCase()]) {
req.params = layer.params;
this.process_params(layer, req, res, () => { layer.handle_request(req, res, dispatch); })}else{ dispatch(); }}}else {
dispatch();
}
}
}
dispatch();
}
Copy the code
exportRouter
类
module.exports = Router;
Copy the code
express/lib/router/layer.js
Downloading dependency packages
npm i path-to-regexp --save
Copy the code
Importing dependency packages
- path-to-regexp
const { pathToRegexp } = require('path-to-regexp');
Copy the code
Create a Layer class
Layers are used in two scenarios
- in
Router.stack
For each array elementlayer
; - in
Route.stack
In, ditto.
function Layer(path, handler) {
this.path = path;
this.handler = handler;
this.reg = pathToRegexp(this.path, this.keys=[]);
}
Copy the code
The match method
Layer.prototype.match = function(pathname) {
let match = pathname.match(this.reg);
if (match) {
this.params = this.keys.reduce((memo, current, index) = > (memo[current.name] = match[index+1], memo), {});
return true;
}
if (this.path === pathname) return true;
if (!this.route) {
if(this.path === '/') {return true;
}
return pathname.startsWith(this.path+'/')}}Copy the code
Handle_request method
The core
Layer.prototype.handle_request = function(req, res, next) {
this.handler(req, res, next);
}
Copy the code
Handle_err method
Layer.prototype.handle_err = function(err, req, res, next) {
// It could be the wrong middleware
if (this.handler.length === 4) {
return this.handler(err, req, res, next);
}
next(err);
}
Copy the code
Layer was derived class
module.exports = Layer;
Copy the code
express/lib/router/route.js
Importing dependency packages
const methods = require('methods');
const Layer = require('./layer');
Copy the code
createRoute
类
function Route() {
this.stack = [];
this.methods = {};
}
Copy the code
The methods instance method is used to quickly determine whether the current matching method is correct, saving performance
Dispatch instance method
The core
Route.prototype.dispatch = function (req, res, out) {
let index = 0;
let method = req.method.toLowerCase();
const dispatch = err= > {
if (err) return out(err);
if (index === this.stack.length) return out();
let layer = this.stack[index++];
if (layer.method === method) {
layer.handle_request(req, res, dispatch);
} else dispatch();
}
dispatch();
}
Copy the code
[method] Instance method
methods.forEach(method= > {
Route.prototype[method] = function (handlers) {
handlers.forEach(handler= > {
let layer = new Layer('/', handler);
layer.method = method; // What method does the user call
this.methods[method] = true; // If the user binds the method, I will record it
this.stack.push(layer); }); }});Copy the code
Export the Route class
module.exports = Route;
Copy the code
DONE
After writing for a long time, I will finish – a simple diagram will be added for better understanding and learning.
If the article is wrong, welcome correction.
There was an article about turning the clock fast, sitting for hours and still having a little head on it.
Good news~~~
Today I heard the good news that all the makeshift hospitals in Wuhan have been closed. Our motherland is great. Now it is time to start counting down to resume work