“This is the 12th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”
There are many frameworks or libraries that support plug-in mechanisms, such as WebPack, Rollup, Vue, Markdown-it, KOA, Redux, and so on. Today we’ll look at how these frameworks support plugins.
Middleware for KOA
Koa is a lightweight Node server framework. See koAJS/KOA repository for the source code. Its native functionality is weak, but you can extend it as a backend by adding various middleware such as KOA-JSON, Koa-Router,koa-multer, etc.
Our approach to applying middleware to KOA is simple:
import json from 'koa-json'
import logger from 'koa-logger'
const app = new Koa()
app.use(json()).use(logger()).listen(3000)
Copy the code
These lines of code apply the KOA-JSON and KOA-Logger middleware in turn, and when the user requests the back end, it is handled by the KOA-JSON and KOA-Logger in turn. In fact, what KOA does is quite simple, and its use method is defined as follows:
class Application extends Emitter {
/ /...
use (fn) {
if (typeoffn ! = ='function') throw new TypeError('middleware must be a function! ')
debug('use %s', fn._name || fn.name || The '-')
this.middleware.push(fn)
return this}}Copy the code
Each call to the use method pushes a function into the this. Middleware array. This. Mdiddleware is combined into A Promise task queue in A listener callback, such as when this.middleware = [A, B, C, D], Then (C). Then (D), then execute the task queue, each task has the opportunity to process the CTX object, when all the tasks (i.e. the middleware functions used by the user) are completed, Call the respond function to automatically respond to the remaining tasks (ctx.status, ctx.method, etc.).
The function definition for the composite middleware for KOA2 is as follows
function compose (middleware) {
if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array! ')
for (const fn of middleware) {
if (typeoffn ! = ='function') throw new TypeError('Middleware must be composed of functions! ')}return function (context, next) {
// last called middleware #
let index = -1
return dispatch(0)
function dispatch (i) {
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
let fn = middleware[i]
if (i === middleware.length) fn = next
if(! fn)return Promise.resolve()
try {
return Promise.resolve(fn(context, function next () {
return dispatch(i + 1)}}))catch (err) {
return Promise.reject(err)
}
}
}
}
Copy the code
As you can see, each middleware is wrapped with a Promise, and if the next method is called in the middleware, the next middleware will be scheduled, and promise.resolve () will be returned if there is no next middleware.
The code to define a KOA-JSON middleware is as follows:
function(opts){
var opts = opts || {};
var param = opts.param;
var pretty = null == opts.pretty ? true : opts.pretty;
var spaces = opts.spaces || 2;
return function filter(ctx, next){
return next().then(() = > {
var body = ctx.body;
// unsupported body type
var stream = body
&& typeof body.pipe === 'function'
&& body._readableState
&& body._readableState.objectMode;
var json = isJSON(body);
if(! json && ! stream)return;
// query
var hasParam = param && hasOwnProperty.call(ctx.query, param);
var prettify = pretty || hasParam;
// always stringify object streams
if (stream) {
ctx.response.type = 'json';
var stringify = Stringify();
if (prettify) stringify.space = spaces;
ctx.body = body.pipe(stringify);
return;
}
// prettify JSON responses
if (json && prettify) {
return ctx.body = JSON.stringify(body, null, spaces); }}); }};Copy the code
The way to develop a KOA2 – middleware can be summarized as follows:
- Write a higher-order function, pass in the option object, return CTX and next, write the business logic in the return function, and call next when appropriate;
- Publish to the NPM repository, usually named after
koa-
The prefix
A piece of middleware has the following form:
export function middleware(options) {
return (ctx, next) = > {
// ...}}Copy the code
The form of using middleware is as follows:
const options = {/ * * /}
const app = new Koa()
app.use(middleware(options))
Copy the code
Markdown – it plug-in
Markdown-it is a markdown parser that converts markdown text into HTML tags. It also has a plug-in mechanism that allows us to extend markdown syntax and functionality with our own plug-ins or third-party plug-ins.
Markdown-it source code: Markdown-it
Markdown – it document: Ruler | markdown – it Chinese document (docschina.org)
The package name of markdown-it plug-in is usually prefixed with markdown-it-, such as markdown-it-table-of-contents. Markdown-it also uses plug-ins through the use function
const MarkdownIt = require("markdown-it");
const md = new MarkdownIt();
md.use(require("markdown-it-table-of-contents"));
Copy the code
The markdown-it-table-of-contents module exports a function, which looks something like this
function(md, options) {
// ...
}
Copy the code
Markdown-it-table-of-contents is used to generate the contents after parsing the [[TOC]] symbol in markdown text. In this plug-in function, we need several capabilities:
- Gets the position that the current parser has resolved to, called a cursor here
- Failed to move cursor or parse after creating new token
- Creating parsing rules
- Determine when parsing rules are used, before or after a rule
conclusion
The steps for designing a plugin mechanism can be summarized as follows:
- Determine what is exposed to the plug-in, that is, the problem domain that allows users to customize
- Determine the sequential relationship between plugins, which can be executed in the order in which the plugins were defined, or in such a topology that one plugin must precede or follow another
- Determine the function name and input parameter definition
- Provides a development paradigm for developing and applying a new plug-in
- coded