如何最好地抽象一个JavaScript库?

8

如何将任何一个JavaScript框架(如jQuery、MooTools等)抽象出来,使其位于框架堆栈的底部?我想要的是这样一种情况:我可以轻松替换库,仅对框架的一层进行更改(例如不是每个模块),整个框架仍然可以正常运行。

因此,每个模块都应该调用一个框架函数,然后将其路由到库中。


2
似乎你的库将会非常大。 - epascarello
4个回答

11
您可能希望使用类似适配器模式的东西。创建自己的接口,公开在应用程序中要使用的方法,然后为想要支持的每个工具包(jQuery、MooTools、YUI等)创建一个适配器。然后,您自己的接口将路由方法调用到特定的适配器。
如果要支持新的工具包,您只需编写一个将自己接口的方法转换为特定工具包方法的新适配器即可。
这是Ext JS目前正在做的事情。您可以选择在框架的方法调用后使用哪个适配器(jQuery、YUI等)。例如:Ext.getCmp()在使用jQuery适配器时将使用jQuery.$()。但是,这通常不是非常容易的一对一映射。

Ext JS最初是作为Yahoo UI Javascript库的扩展而启动的。那时,Ext依赖于YUI来处理其所有低级跨浏览器代码。现在,Ext是一个独立的JavaScript库,您可以选择用其他JavaScript库(如Prototype或jQuery)替代YUI。将低级别的Ext API映射到其他JavaScript库(或Ext自己的基本库)的源文件称为适配器,它们位于source/adapter子目录中。在您将Ext包含在网站中时,您可以选择要使用哪个适配器。

来源:Ext JS手册:源概述


但这真的能行吗?考虑到不同框架具有不同的方法和功能,你如何编写一个包含所有方法的超级集合呢?似乎你必须填补空缺,每个库拥有的功能另一个库没有的话...你必须添加缺失的功能。 - Nick Craver
@Nick:是的,你说得对。我并不是说这很容易...但这就是ExtJS所做的。它要求你选择一个适配器。最初它支持YUI、jQuery、Prototype IIRC,但后来他们也实现了自己的适配器。好处是,如果你正在使用他们自己的适配器,但之后你想使用依赖于jQuery的东西,你只需要切换适配器。非常巧妙,但肯定不容易实现。 - Daniel Vassallo
@Daniel - 这是我的主要观点...它并不容易,而且“不容易”的程度是一个重要因素,在这里我无法想象它比需要移植代码更少的工作量。另一个原因真正远离这个是OP正在针对处理器和连接速度较慢的移动设备,使得这个额外层次更加不吸引人。比起一开始就进行移植,更加沉重、更加需要处理器,并且可能需要更多的工作...我坚决建议完全避免这种情况,特别是对于这个特定的应用程序。 - Nick Craver
@Nick:我完全同意。我的回答只是试图回答这个问题,特别是因为我能够理解Ext JS是如何处理它的。然而,虽然我可以看到Ext的某些优点,在绝大多数应用程序中并不适用。一个主要的缺点,除了增加的复杂性之外,就是如果其他工具包没有提供相同的功能,则无法利用一个工具包的特定功能。 - Daniel Vassallo
2
只是分享我的经验。直接调用mootools getElement ($)与sandbox->adapter->mootools相比,平均为0.40ms vs. 0.95ms(在最坏的情况下进行测试;低端诺基亚设备,而Android设备似乎并不真正关心实现)。尽管执行时间的增加百分比相当高,但当你单独看这些数字时,每次调用仍然不到一毫秒,与框架现在是库无关的事实相比微不足道。 - crappish

6
如果你关心性能(为什么不关心呢?),那么编写一个针对库的库就不是一个好的选择。在各种实用工具、动画等方面,另外一种完全不同的抽象层会非常低效而且耗时。
说真的,在任何规模较大的项目中,即使需要多次移植代码,也比构建(或更重要的是维护)这样的库抽象层所需的时间要少得多。

移植的问题在于您失去了快速和/或轻松测试不同库的可能性。目前我们正在使用Mootools,并且我们已经超过60次调用库本身。而这只是一个最基本的原型。因此,当我们开始积累模块时,我希望它们是独立于库的,这样我们就不会陷入移植地狱,如果到了那个时候的话。尤其是因为我们希望尽可能灵活。 - crappish
重点是能够在不重写顶部所有代码的情况下移植底层...切换框架并不经常发生,但当它发生时,我喜欢有一个抽象掉混乱细节的代码。如果您担心性能问题,您可以始终编译/内联使用的那些函数。 - user9903
@omouse - "你可以编译/内联使用的那些函数",这意味着你正在编辑基础库以达到目的...这完全违背了本意。你可能喜欢有高度抽象的代码,我喜欢我的用户能够更快地加载页面。这样想:你是想让我的生活更轻松,还是想让Stack Overflow加载更快?至于切换,切换发生得如此之少(如果有的话),它很可能比基础库的许多版本都要领先,这意味着你的抽象层已经过时并且缺少一些东西...拥有它会带来更多痛苦 - Nick Craver
@NickCraver 我的意思是添加一个编译时阶段。不要在运行时编译/内联!这会像你所说的那样影响性能。 - user9903
@NickCraver 相信我,这并不是更少的工作量。我现在正在从MooTools转换到jQuery的一个项目上工作,因为所有的代码都依赖于MooTools,所以这需要相当长的时间。我们必须重写所有的代码。有了额外的中间层,您就不需要重写每个特定页面的JavaScript,只需要抽象出框架的中间层即可。在我的特定情况下,拥有那个中间层会减少工作量。我猜你的情况可能不同 :p - user9903
显示剩余10条评论

3

你需要为你的框架编写适配器,每个工具包/库都需要一个适配器,就像ExtJS/Sencha所做的那样(尽管他们的适配器可能不可重用)。

首先,制定适配器应该具有哪些方法,它将作为你的框架低级API。

下一步是为每个可能的工具包/库编写适配器。某些库的适配器会很难编写,例如YUI,因为它们动态加载代码。你可能需要先编译一个带有所需功能的单个.js文件。

添加新的工具包/库,根据你的映射编写一个新的适配器。


2
这可能是一个合理的想法,如果这些框架具有相同的功能。不幸的是,它们做的事情不同,并且它们以不同的方式完成。它们之间存在交叉,但你的包装器会错过很多东西,因为你只能对两者都有的东西进行包装。你最终会得到最低公共分母;两个框架中最糟糕的部分。
我的建议是花时间学习两个库 - MooTools和JQuery都是非常好的库,了解它们是很好的。即使你在项目中只使用其中一个,你至少能在开始之前做出明智的选择。

这是不正确的。有各种方法可以“填补”可能存在的空缺。在某一点上,您可以应用专有扩展的接口,在另一点上,基础库可能已经涵盖了该功能。这只是一个例子。最终,耦合会导致各种头痛,您不想处理。 - Cody

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接