前端插件设计

【本文主旨】仅探讨插件系统的实现,不针对某个具体功能的插件。
【关键词】依赖与被依赖者的管理赋能、vue、webpack、babelhtml

对插件最直观的认识,顾名思义,即插即用,具备很高的灵活性;另外插件彻底解耦于使用者,只要知足对接口的约定便可。 本文主要从目前使用较多的几个工具入手,简单介绍下插件的使用,以及它们对插件的约定格式。vue

  • 前言
    从简单的例子开始:webpack

    function pluginA(App) {
        App.protoType.a = 1;
    }
    function pluginB(App) {
        App.protoType.b = 1;
    }
    class App { }
    
    //对App原型进行加强
    pluginA(App);
    pluginB(App);
    复制代码

    这种写法,从语义上看,plugin是主动施与者,为了统一对外接口,较多的写法为:git

    class App { 
        use(plugins) {
            let type = Object.protoType.toString.call(plugins);
            if(type === '[object Function]') {
                plugins.call(null, this);
            }else if(type === '[object Array]') {
                plugins.forEach(plugin => {
                   pulgin.call(null, this); 
                });
            }
        }
    }
    //用法一
    App.use(pluginA);
    App.use(pluginB);
    //用法二
    App.use([pluginA, pluginB]);
    复制代码

    由使用者提供接口,语义上更友好。App:"我须要pluginA"。pluginA便为之所用。是否是有些IOC的意思?
    github

  • webpack插件
    插件向第三方开发者提供了 webpack 引擎中完整的能力。使用阶段式的构建回调,开发者能够引入它们本身的行为到 webpack 构建流程中。web

    • 插件写法编程

      function MyPlugin(options) {
      
      };
      
      // 在插件函数的 prototype 上定义一个 `apply` 方法。
      MyPlugin.prototype.apply = function(compiler) {
          // 指定一个挂载到 webpack 自身的事件钩子。
          compiler.plugin('webpacksEventHook', function(compilation, callback) {
      
              // 功能完成后调用 webpack 提供的回调。
              callback();
          });
      };
      
      module.exports = MyPlugin;
      复制代码

      顺便一提:
      compiler:表明的是不变的webpack环境,是针对webpack的;
      compilation:针对的是随时可变的项目文件,只要文件有改动,compilation就会被从新建立。redux

    • 用法bash

      var MyPlugin = require('my-plugin');
      
      var webpackConfig = {
          ...,
          plugins: [
              new MyPlugin({options: true})
          ]
      };
      
      复制代码
    • 深刻理解babel

      • 插件的初始化时机
        为了让插件对webpack构建生命周期的事件节点,作出相应的反应,在读取webpack.config.js文件后,会首先执行配置文件中插件(plugin)的实例化,为webpack事件流挂上自定义钩子。
      • apply方法的约定
        查看源码,插件的应用以下:
        if (options.plugins && Array.isArray(options.plugins)) {
        	for (const plugin of options.plugins) {
        	    //调用plugin的apply方法
            	plugin.apply(compiler);
            }
        }
        复制代码
        由此看出,webpack内部对插件的统一处理:获取插件的apply方法,将compiler注入,以完成事件钩子的注册。此处apply即是webpack处理插件的通用接口,所以编写webpack插件,必须提供apply方法。
  • Vue插件

    • 插件写法
      //1. 若插件为对象,必须提供install方法
      MyPlugin.install = function (Vue, options) { }
      export default MyPlugin;
      
      //2. 若为函数,会被做为install方法
      export default function(Vue) {
      }
      复制代码
    • 用法
      var MyPlugin = require('my-plugin');
      
      Vue.use(MyPlugin, options);
      复制代码
      use的内部处理和webpack相似:MyPlugin.install(Vue),都是使用约定的函数,将使用者注入,进行功能加强。 另外提一下,Regular中的Component也是由一个use函数来统一'使用'插件。
  • Babel插件
    Babel 虽然开箱即用,可是什么动做都不作。它基本上相似于 const babel = code => code; ,将代码解析以后再输出一样的代码。若是想要 Babel 作一些实际的工做,就须要为其添加插件。

    • 插件写法
      //babel-plugin-demo/index
      module.exports = function() {
          return {
              //访问者  
              visitor: {
                  BinaryExpression(path, state) {
                      //对AST对象进行变换操做
                  }
                  /**
                  *等价于
                  * BinaryExpression: {
                  *    enter() {
                  *        //进入节点,相应的存在exit()
                  *    }
                  * }
                  *
                  **/
              }
          }
      }
      复制代码
      上面定义了一个简单的访问者(visitor),当遍历AST树的过程当中遇到typeBinaryExpression的节点,就会调用BinaryExpression()方法。
    • 使用
      //.babelrc
      {
          ...
          "plugin": [
              [
                  "demo",  //等价于babel-plugin-demo
                  {
                      ...   //state.opts
                  }
              ]
          ]
          
      }
      复制代码
  • 总结

    经过上面的总结,能够看到不一样框架对插件的实现大同小异:不固化依赖,而是经过对外提供"插槽"的方式,由使用者灵活的按需注入。在其中,看到了依赖注入装饰者模式的影子。
    平常开发中,随处可见插件的应用,好比redux的中间件等,这里不作赘述。若是可以灵活处理依赖与被依赖二者的关系,有利于提升代码的复用性,对编程思惟也是一种提高。

  • 参考

【欢迎留言】本文是否对你有帮助,亦或有所遗漏笔误等,烦请告知。

相关文章
相关标签/搜索