如何实现一个简单的插件扩展

为什么需要插件扩展?

任何一个易用的系统都强调易用,易维护,模块化是nodejs的核心思想(显然并不是nodejs首先提出的);nodejs有很多优秀的package都支持插件式开发:hexo(非常流行的静态博客生成工具),egg(ali团队推出的企业级开发框架),etc…


当然模块化不仅仅限于使用插件化的方式;还有node web开发中非常常见的中间件模式。


总之,无论是oop、中间件、插件化这些设计模式都是为了更加解耦、更加抽象、更加易读、易用、易维护。

好的设计模式可以做到不需要对现有的系统代码做任何修改,对现有代码无侵入的情况下做到功能拓展。插件机制就是一种很常见的设计模式,可以用最小的成本满足大部分的应用场景。

插件可以随时添加,任意添加,对已封装的核心代码没有任何改变,核心代码更加纯粹,保持最精简的状态,极大程度的避免了快速迭代过程中造成核心崩溃的情况。

如何实现插件扩展机制?

java,.net 等oop语言都有反射机制,即可以根据类名动态加载类的信息到内存或虚拟机中,完成对象的创建。这种反射机制是现在流行的主流框架的基本实现手段,它们之所以如此优秀于灵活,就是因为这种机制提供了更自由的扩展于变化;让coder更关注业务。

nodejs并没有反射机制,因为它本身是非编译型的,为了能有更好的扩展,nodejs设计了require机制,可以通过require函数自由的加载需要的模块,某种意义上说:

require 就是nodejs的反射机制!

但是nodejs在不断更新,慢慢会出现更好的特性,必然也会实现其反射机制。不过就目前来看,require已经可以满足我们实现插件化开发了。

hook即钩子,是一种在php系统中使用较为多见的概念,比如非常著名的博客系统:wordpress,就有非常多丰富的插件;而插件的基础就是hook!假设一个模型:

// code list 1:
var core = { 
 run: function(){
   console.log('hi there~');
 }
};

core.run();
// hi there~

针对这样的设计,我们无法完成任何事情,想要在run之后执行一些其它的操作,只能修改它的代码比如:

// code list 2:
var core = { 
 run: function(){
   console.log('hi there~');
   console.log('an other greeting~');
 }
};

core.run();
// hi there~
// an other greeting~

这样的设计无法满足动态的需求,本身的核心代码也无法进行封装,因为随时需要修改它。


下面进入正文:

// code list 3:
var core = {};	//初始化
core.hooks = {};	//定义钩子集合

/*添加钩子的函数,这里是重点*/
/*钩子名,钩子函数*/
/*将一个函数添加到core对象的钩子集合中*/
core.addHook = function(hookName, hookFunction){
  var hookList = core.hooks[hookName];
  if(hookList){
    hookList.push(hookFunction);
  }else{
    hookList = [ hookFunction ];
  }
  core.hooks[hookName] = hookList;
};

/*执行钩子集合中的钩子函数*/
core.runHook = function(hookName){
  var hookList = core.hooks[hookName];
  if(hookList){
    for(var i = 0 ; i < hookList.length ; i++){
      hookList[i]();
    }
  }
};

/*核心入口函数*/
core.run = function(){
  core.runHook('before_run');
  console.log('hi there~');
  core.runHook('after_run');
};

core.run();
// hi there~

/*当然此时,写了这么多代码,也只是和 code list 1 实现的功能一样*/

重点来了:

如果我们想实现 code list 2的功能,我们似乎根本不需要修改 core.run的代码:

// code list 4:

code.addHook('after_run', function(){
  console.log('an other greeting~');
});

code.run();
// hi there~
// an other greeting~

简单的添加一个hook函数就可以实现code list 2实现的功能了,当然我们还能在这个基础上实现更多。 而这个就是一个简单的插件化开发模型;在此基础上不断的提高核心代码的健壮和容错性就可以将核心代码进行封装发布,提供丰富的钩子和api供插件开发者实现更多优秀的扩展。

总结

一个nodejs模块尽量精简,保持较小的体积,可以让它更轻便,优雅,插件化开发可以让你的package被更多人使用和喜欢。