“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:

  1. 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;
  2. Publish to the NPM repository, usually named afterkoa-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:

  1. Gets the position that the current parser has resolved to, called a cursor here
  2. Failed to move cursor or parse after creating new token
  3. Creating parsing rules
  4. Determine when parsing rules are used, before or after a rule

conclusion

The steps for designing a plugin mechanism can be summarized as follows:

  1. Determine what is exposed to the plug-in, that is, the problem domain that allows users to customize
  2. 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
  3. Determine the function name and input parameter definition
  4. Provides a development paradigm for developing and applying a new plug-in
  5. coded