前端插件設計

【本文主旨】僅探討插件系統的實現,不針對某個具體功能的插件。
【關鍵詞】依賴與被依賴者的管理賦能、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的中間件等,這裏不作贅述。若是可以靈活處理依賴與被依賴二者的關係,有利於提升代碼的複用性,對編程思惟也是一種提高。

  • 參考

【歡迎留言】本文是否對你有幫助,亦或有所遺漏筆誤等,煩請告知。

相關文章
相關標籤/搜索