In recent days, I took out the two frameworks of KOA and Express to observe them. I knew that I was a front-end novice who could make up for my shortcomings with diligence, so I decided to record my experience on this rainy afternoon (I have been isolated at home for nearly one and a half months, learning how to practice is sweet). There may be improper wording in the article, as well as technical errors, welcome to correct.
This article centers on KOA.
Introduction of koa
Koa is a Web development framework implemented based on the Node built-in module HTTP
Koa is a new web framework designed by the team behind Express, which aims to be a smaller, more expressive, and more robust foundation for web applications and APIs. By leveraging async functions, Koa allows you to ditch callbacks and greatly increase error-handling. Koa does not bundle any middleware within its core, and it provides an elegant suite of methods that make writing servers fast and enjoyable.
From the above official introduction:
koa
和express
It’s all from the same teamkoa
Designed to provide a smaller, more expressive, and more robust programming foundation for Web usage- Different from the
express
.koa
Internal usePromise
Object that is allowed to be used externallyasync... await
Implement asynchronous sequential operations insteadexpress
The callback function in - Error handling is easier,
Koa
The class hierarchyevents
Class, the implementation mechanism is still based onPublish/subscribe
model - There is no internal integration of any middleware, download and install as needed; Compared with the
express
Integrate all possible middleware such as:static
.body-parser
.multer
.views
.express-session
.cookie-parser
Middleware, so that the file is larger, but users do not need to download.
using
The preparatory work
-npm init Initializes the 'package.json' file -npm install koa -s Downloads the 'koa' packages that the production environment depends onCopy the code
Initialization file
- Create an app.js application file
const Koa = require('koa');
const app = new Koa();
const Koa = require('./koa');
const app = new Koa();
app.use(async(ctx, next) => {
console.log(1);
ctx.body = '1';
await next();
ctx.body = '11';
console.log(4);
});
app.use((ctx, next) = > {
console.log(2);
ctx.body = '2';
next();
ctx.body = '22';
console.log(5);
});
app.use((ctx, next) = > {
console.log(3);
ctx.body = '3';
next();
ctx.body = '33';
console.log(6);
});
app.on('err'.console.log);
app.listen(3000);
Copy the code
Print result:
1
2
3
6
5
4
Copy the code
The browser returns the result:
11
Copy the code
Summary features:
- Based on the
node
The encapsulationreq
.res
The internal method has only three core functionsuse
,listen
,on('error')
context
即ctx
The context is a collection of REq and RES (request,response)request
Encapsulate your own request methodresponse
Self-encapsulated response methods
implementation
Onions
model
From a practical point of view,koa
The middleware call is similar to oneOnions
Model, as shown below:
The directory structure
koa
| application.js
| lib
| context.js
| request.js
| response.js
| package.json
Copy the code
coding
package.json
{
"main": "lib/application.js",} for`koa`Package determines the entry file.Copy the code
application.js
const Emitter = require('events');
const http = require('http');
const context = require('./context');
const response = require('./response');
const request = require('./request');
const Stream = require('stream');
module.exports = class Koa extends Emitter{
constructor() {
super(a);this.middlewares = [];
this.context = Object.create(context);
this.response = Object.create(response);
this.request = Object.create(request);
}
use(middleware) {
this.middlewares.push(middleware);
}
handleRequest(req, res) {
const ctx = this.createContext(req, res);
ctx.statusCode(404);
this.compose(this.middlewares, ctx)
.then((a)= > {
let body = ctx.body;
if (body instanceof Stream) {
body.pipe(res);
} else if (typeof body === 'object'){
ctx.set('Context-Type'.'application/json');
res.end(JSON.stringify(body));
} else if (typeof body === 'string' || Buffer.isBuffer(body)) {
res.end(body);
} else {
res.end('Not Found')
}
}, err => {
this.emit('error', err);
ctx.statusCode(500);
ctx.end('Internal Server Error')
})
}
compose(middlewares, ctx) {
function dispatch(index) {
if (index === middlewares.length) return Promise.resolve();
try {
return Promise.resolve(middlewares[index](ctx, () => dispatch(index +1)));
} catch(err) {
return Promise.reject(err); }}return dispatch(0);
}
createContext(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.req = request.req = req;
context.res = response.res = res;
returncontext; } listen(... args) {const server = http.createServer(this.handleRequest.bind(this));
return server.listen(...args);
}
}
Copy the code
Resolution:
Object.create(targetObject);
Here,targetObject
是context
.response
.request
, this step is to prepare three objects for prototype chain inheritancecontext
: Used internallyObject.prototype.__defineGetter__
和Object.prototype.__defineSetter
, the step is rewritingcontext
Attribute is access attribute, value and assignment through the function form, more logical operation. Such as:context.method
The currentcontext
There is nomethod
Properties, byObject.prototype.__defineGetter__
Returns internally when calledresponse.method
Then the value can be completed.response
和request
Both are expanded nativereq
.res
Methods and properties on the object. Such as:req.method
It’s just expandingresponse
Added to the property of
Here is:
context.js
const proto = {};
function defineGetter(property, key) {
proto.__defineGetter__(key, function() {
return this[property][key]; })}function defineSetter(property, key) {
proto.__defineSetter__(key, function(newValue) {
this[property][key] = newValue;
})
}
defineGetter('request'.'method');
defineGetter('request'.'path');
defineGetter('response'.'body');
defineGetter('response'.'set');
defineGetter('request'.'params');
defineGetter('response'.'statusCode');
defineSetter('response'.'body');
module.exports = proto;
Copy the code
request.js
const url = require('url');
const querystring = require('querystring');
module.exports = {
get method() {
return this.req.method;
},
set method(val) {
this.req.method = val;
},
get path() {
// Url {
// protocol: null,
// slashes: null,
// auth: null,
// host: null,
// port: null,
// hostname: null,
// hash: null,
// search: '? id=1',
// query: 'id=1',
// pathname: '/get',
// path: '/get? id=1',
// href: '/get? id=1'
/ /}
const { pathname, query } = url.parse(this.req.url);
this._params = querystring.parse(query);
return pathname;
},
get params() {
return this._params;
},
get url() {
return this.req.url; }},Copy the code
response.js
module.exports = {
_body: undefined,
get header() {
const { res } = this;
return res.getHeaders();
},
get headers() {
return this.header;
},
get status() {
return this.res.statusCode;
},
get body() {
return this._body;
},
set body(val) {
this.statusCode(200);
this._body = val;
},
statusCode(newValue) {
this.res.statusCode = newValue; }, set(... args) {console.log(args);
this.res.setHeader(...args);
}
}
Copy the code