Introduction: Babel plug-in, Webpack plug-in, Vue CLI plug-in, why so many excellent frameworks are using plug-in system? What is a plug-in architecture? What benefits did it bring? What scenarios can it be applied to?

Definition of plug-in architecture

Plug-in architecture, also known as microcore architecture, refers to the relatively small software kernel, and the main functions and business logic are implemented through plug-ins. Plug-in architecture generally has two core concepts: kernel and plug-in.

  • A kernel (pluginCore) usually contains only minimal functionality for the system to run;
  • Plugins are independent modules that typically provide a single function.

In addition to the plug-in management function, the kernel also abstracts all the business to be done, abstracting the minimum granularity of the basic interface for the plug-in side to call. In this way, the efficiency of plug-in development will be greatly improved. For example, the browser is a typical plug-in architecture. The browser is the kernel and the page is the plug-in, which provides very rich functionality by loading different pages from different urls. Also, when we develop web pages, browsers provide a lot of APIS and capabilities that are mounted through Windows, such as DOM, BOM, Event, Location, and so on.

Designing a complete plug-in architecture system consists of three elements:

  • PlugCore: a plug-in kernel that provides plug-in runtime and manages the plug-in load, run, and uninstall lifecycle (analogous to a browser).
  • pluginAPI: Basic interface required by the plug-in runtime (analogous to the browser example, equivalent towindow);
  • Plugin: Independent modules that provide a single function (analogous to the browser example, which is equivalent to different web pages).

Second, practice of plug-in architecture

We will analyze the practice of plug-in architecture of jQuery, Babel and Vue CLI, three excellent open source libraries, from the perspectives of plugCore, pluginAPI and Plugin.

2.1 Plug-in architecture of jQuery

JQuery is a JavaScript library that greatly simplifies JavaScript programming and gets more done with less code. The standards of the early browsers were not uniform, and it was a pain to develop web pages that were compatible with users of different browsers. On the basis of adapting to the differences of different browsers, jQuery provides a more perfect and easy-to-use API for front-end developers to complete web programming. Web pages written with jQuery can also run normally on browsers of different manufacturers under one set of code. Before MV* frameworks became popular, jQuery was the absolute champion. JQuery is extensible and has a complete plugin ecosystem, with all the plug-ins you need for web development found in its ecosystem. Let’s take a look at the jQuery plugin architecture.

Plug-in definition:

$.fn = jquery. proType JQuery’s plug-in mechanism is mounted via a prototype chain.

Plug-in mechanism execution process

The demo sample

$appYou can look it up on the prototype chainmyPlugin:

Summarize from the three elements:

  • PluginCore: To extend different plugins via prototype chain assignment, which can be called after obtaining jQuery instances.
  • PluginAPI: The core interface of the jQuery package (jQuery relies on its excellent API)
  • Plugin: Unlimited and can be a JavaScript type.

2.2 Plug-in architecture of Babel

Babel is a toolchain for converting ECMAScript 2015+ version code into backwardly compatible JavaScript syntax so it can run in current and older versions of browsers or other environments. Many feature and syntax conversions are involved in the code conversion process, and ECMAScript proposals are constantly updated. How do you organize the large (and growing) number of transformation rules? Let’s look at how Babel works

Babel conversion source, divided into three steps:

  • Parse: Lexical Analysis and Syntactic Analysis are performed to generate the abstract syntax tree (AST).
  • Transform: Traverses all nodes in the AST and performs corresponding transformation operations. This process uses different plug-ins to complete the transformation of various features and syntax.
  • Generate: Generate object code based on the transformed AST.

Babel uses the plug-in architecture in the AST transformation process (step 2 in the figure above). The use of a plug-in architecture for this transformation process is described in detail below.

Plug-in definition:

/ / lite version
export default function (pluginApi, pluginOtpions) {
	return {
		name: 'Plug-in name'.visitor: {
			enter(nodePath){},// each node is called on entry
			exit(nodePath){},// each node is called when it leaves
			[NodeType](nodePath) { },  // nodePath. Type === NodeType}}}Copy the code

A plug-in is a function that returns an object containing a visitor. Part of the concept description of plug-in definition:

  • Name: indicates the name of the plug-in
  • PluginAPI: The API passed in by the plug-in runtime
  • Visitor: Is an object whose key is the type of the node and whose value is a function where AST conversion takes place.
  • NodePath: is an instance object of an AST node.@babel/parser/src/parser/node.jsWhere, type field: node type. AST traversal is based on node typevisitorTo implement the transformation. Common types: VariableDeclaration, ArrowFunctionExpression, and more@babel/types.

(I think pluginAPI should also include nodePath, because each node instance contains, in addition to the syntax and lexical description, a method of converting between the required grammars.)

Plug-in sample

Transform arrow functions to normal functions: @babel/plugin-transform-arrow-functions

import { declare } from "@babel/helper-plugin-utils";
import type NodePath from "@babel/traverse";

export default declare((api, options) = > {
  api.assertVersion(7);

  const { spec } = options;
  return {
    name: "transform-arrow-functions".visitor: {
      ArrowFunctionExpression(
        path: NodePath<BabelNodeArrowFunctionExpression>,
      ) {
        // In some conversion cases, it may have already been converted to a function while this callback
        // was queued up.
        if(! path.isArrowFunctionExpression())return;

        path.arrowFunctionToExpression({
          // While other utils may be fine inserting other arrows to make more transforms possible,
          // the arrow transform itself absolutely cannot insert new arrow functions.
          allowInsertArrow: false.specCompliant: !!spec,
        });
      },
    },
  };
});
Copy the code

The implementation of the plugin is as follows:

  • The first step is to execute the plug-in and get the containing visitor object;

  • In the second step, ATS traverses the node, detects nodePath type === ‘ArrowFunctionExpression’, and finds the function whose key is ArrowFunctionExpression in the ViStor object.

  • Third, pass nodePath into this function to call (AST is modified in this step);

The idea of executing a single plug-in is clear. How can multiple plug-ins work together during ATS traversal?

The workflow of Babel’s plug-in architecture in transforming the source code looks like this:

  • Step 1: Get a description of all the plugins that Babel configured by parsing the Babel configuration file (or the command-line –plugins parameter).

  • Second step, the require of the plug-in is entered into memory, the plug-in function is obtained, and the plug-in function is executed to obtain multiple objects containing viSTOR field. (detailed logic: @ Babel/core/SRC/config/full. Js)

  • Third step, multiple object containing vistor fields into a large visitor (detailed logic: @ Babel/core/SRC/transformation/index. Js)

Object of visitor:

The value in the visitor object is changed from a single function to **Array

  • The fourth step, AST traversal, each node according toNodeTypeTo obtainvisitor[NodeType]And execute them in turn.

Summarize from the three elements:

  • PluginCore: plug-in load and integration (viSTOR merge), AST traversal is called to find vistor[NodeType] and call in sequence;
  • PluginAPI: nodePath, in AST traversal, provides interfaces for converting between different types of nodes;
  • Plugin: Visitor [NodeType]=function(nodepath), consists of visitor for different nodetypes, handling features and syntax.

2.3 Plug-in Architecture of the Vue CLI

Vue CLI is a complete system for rapid development based on Vue. Js. CLI plug-ins are NPM packages that provide optional functionality to your Vue project, such as Babel/TypeScript translation, ESLint integration, unit testing, and end-to-end testing. The name of the Vue CLI plug-in starts with @vue/cli-plugin- (built-in plug-in) or vue-cli-plugin- (community plug-in) and is very easy to use. In the following sections, we will examine the definition, execution, and installation of CLI plug-ins.

Plug-in definition

Plug-ins must be vue-cli-plugin-named NPM packages, and the directory structure is defined strictly by file names.

Because @vue/cli-service is used as a plug-in for the project through the NPM package defined in Dependencies and devDependencies in package.json.

├─ Generator. Js # Generator /index.js # Service ├─ Generator /index.js # Service ├─ Download. json └─ prompts # promptCopy the code

File name and content description:

  • generator.js: will be executed when the plug-in is added, can install NPM package, modify the project source code and other functions;
// Export the module definition
module.exports = (pluginApi, pluginOptions, rootOptions) = > {}
Copy the code
  • prompts.js: Ask the user to configure the option of their plug-in when installing, internal is throughinquirerImplementation, after being called to get the option configuration and ingenerator.jsSaved as a parameter when called;
// Export the module definition
 module.exports = [
   { 
     type: 'input'.name: 'locale'.message: 'The locale of project localization.'.validate: input= >!!!!! input,default: 'en'}]Copy the code
  • index.js: entry to the Service plug-in,@vue/cli-serviceExecuted at startup
    Copy the code

Module.exports = (pluginApi, options) => {// Modify webPack configuration API. ChainWebpack (webpackConfig => {})}

> more details you can go to the [Vue Cli plug-in development guide] (https://cli.vuejs.org/zh/dev-guide/plugin-dev.html), we put the Vue Cli plug-in implementation is divided into two cases: - the first one: Prompts (' prompts. Js' + 'generator.js') - Second: The plug-in is installed and executed when the system starts (' index.js') #### the first installation process compared to Babel's manual installation and adding plug-ins, Vue CLI plug-in system provides a command line installation mode is very convenient. Let's take a look at the Vue Cli plug-in system how to implement a one-line command to add plug-ins function. The installation process is as follows: - Step 1: parse the plug-in name from the command line parameters, and then run the NPM I vue-cli-plugin-xxx command to install the plug-in. [@vue/cli/lib/add.js](https://github.com/vuejs/vue-cli/blob/aee9e178e7c9598f317ceeed2940c94ac82e8340/packages/%40vue/cli Prompts /lib/add.js#L52) -- take ('vue-cli-plugin-xxx/prompts') and take a prompt. [@vue/cli/lib/invoke.js](https://github.com/vuejs/vue-cli/blob/aee9e178e7c9598f317ceeed2940c94ac82e8340/packages/%40vue/ Cli/lib/invoke. Js# L69) ` ` ` javascript / / code streamlining const prompt = inquirer. CreatePromptModule () const pluginPrompts = require(`vue-cli-plugin-xxx/prompts`) const pluginOptions = await prompt(pluginPrompts)Copy the code
  • Step 3: UsepluginNameandpluginOptionsAs a parameterGeneratorObject instance
// The code has been simplified
  constPlugin = {id:'vue-cli-plugin-xxx'.options: {
      ...pluginOptions
    }
  }
 const generator = new Generator(context, {
  plugins: [plugin],
  invoking: true
})
Copy the code
  • Step 4: Execute the generator.generate method. This involves three key steps:

    1) require(vue-cli-plugin-xxx/generator), obtain the plugin’s execution function;

    2) build the GeneratorAPI (pluginAPI);

    3) Call the generator.js export function.

      // The code has been simplified
      const apply = require(`vue-cli-plugin-xxx/generator`)
      const pluginApi = new GeneratorAPI(id, pluginOptions)
      await apply(pluginApi, pluginOptions) // pluginOptions get option results for the second step
    Copy the code

Detailed code: @vue/cli/lib/ generator.js

  • Step 5: Add the plug-in name and plug-in parameters to the vue.config.js file.

The second operational process

The plug-in running flow is defined by the @vue/ CLI-service plug-in system. There are two types of plug-in calls:

  • The first built-in plug-in (the commands of @vue/ CLI-service are related to configuration, and the functions of the system plug-in are divided into multiple built-in plug-ins, which are called by default in the plug-in system);
  • The second project plug-in, (package.jsonNPM package names defined in the plugin naming convention.

The plug-in running logic is simple:

The pluginAPI for the two processes is different. – Installation process: @vue/cli/lib/GeneratorAPI – Running process: @vue/cli-service/lib/ pluginapi.js

Summarize from the three elements:

1) Installation process

  • PluginCore: Prompts. Js to get the user’s installation option result. Then, use the option result and generator.js as parameters to construct the generator. The generator.js function is executed in a call to generator.generate.

  • PluginAPI: GeneratorAPI, which provides source code modification, NPM package management, template file generation and other functions;

  • Prompts plugin: Prompts. Js and generator.js, which address a dependency to be dealt with when an ability is implanted into a project.

2) Operation process

  • PluginCore: @vue/cli-service, after obtaining the project plug-in from package.json, merge it with the system built-in plug-in, and finally execute it successively;

  • PluginAPI: pluginAPI, provides webPack configuration modification and command management capabilities;

  • Plugin: index.js file to work under different commands.

A plug-in system can contain a variety of plug-in implementation, and plug-in system through the command installation plug-in implementation, it is very convenient for users to use the plug-in system.

Third, the application of plug-in architecture

3.1 Application Scenarios

Using the examples above, summarize the application scenarios for dealing with the plug-in architecture.

  • Rich pluginAPI scenarios: code runs in multiple scenarios that need to be smoothen out (jQuery);

  • Second: rich Plugin scenario, plug-in system, which can be expected to increase demand, suitable for more plug-ins to simplify the system code (Babel)

  • The third: Rich pluginCore and pluginAPI scenarios, the system itself is very complex, high requirements for developers, at this time, use plug-in architecture, put its complex work into pluginCore and pluginAPI implementation, the rest of the simple coding work left to plug-in side implementation. PluginAPI can also be used to quickly complete business development (Vue CLI)

3.2 Development direction

Through the establishment of a plugin standard, the research and development ability of precipitation process plug-in programming, and the whole company by using a plug-in system (China), it means that we don’t have to repeat business wheel, teams and companies can continue to accumulate their own plugin ecological, software development can like automobile industries such as manufacturing, to create a standardized assembly line.

(The next part will explain how to use plug-ins in combination with other architectures and how to tune the three elements. Please pay attention to it next time.)