抽象 jQuery
Posted
技术标签:
【中文标题】抽象 jQuery【英文标题】:Abstracting jQuery 【发布时间】:2013-04-08 14:04:12 【问题描述】:在Nicholas Zakas 和Addy Osmani 的这些谈话中,他们讨论了在构建大型 javascript 应用程序时使用外观模式作为沙箱的想法,以便将应用程序与底层基础库分离。
理论上,这种解耦可以让您切换一个基础库,而无需重写您的应用程序模块。但在实践中,这似乎更难以实施。
这个提议的架构有具体的实现,例如AuraJS。然而,从源代码来看,沙箱似乎仍然存在通过从其某些方法返回 jQuery 对象的泄漏抽象。
我并不特别关心 AuraJS,但更关心的是尝试抽象像 jQuery 这样的库而不丢失太多功能的一般概念。
例如,假设我的外观/沙盒有一个 dom 方法.find(selector)
。对于它可能返回的结果,我可以想到 3 个选项:
一个 jQuery 对象 - 这会将 jQuery 泄漏到消费模块中。
一个原始的 dom 元素 - 功能丧失,没有人真正想要使用它!没有链接。
自定义的类 jQuery 包装器 - 可能相当复杂,但似乎是理想的解决方案。
所以我的问题是,如何在不丢失太多功能的情况下抽象出一个像 jQuery 这样的库,以便在将来的某个时候以最小的努力替换它?
【问题讨论】:
jQuery 内部相当复杂,创建一个可以被任何(甚至一个)其他库替换的抽象似乎非常困难。有什么意义呢?如果您认为将来可能要交换库,也许您一开始就不应该使用您正在使用的库。 @RobG 我同意这似乎很难,这就是我问这个问题的原因。不管比我聪明得多的人提出这些建议并为它们提供正当理由(请参阅提供的会谈链接)。 在 Nicholas 的架构中,如果您更改基础库,您还必须重新编写应用程序核心。一旦你超越了核心,你就不再编写使用该库的代码了。如果您使用 jQuery,编写模块的人不会编写 any jQuery。 @RobG 我明白这一点。在核心中编写方法来抽象基础库是直截了当的。从这些抽象中返回什么并通过沙箱公开是问题所在。例如如果核心中有一个 dom 模块,如果不是 jQuery 对象,应该从 .find() 返回什么? .deferred() 之类的呢? 模块应该只处理native objects和核心提供的方法。你可能有var x = new Widget(); container.add(x);
,然后是x.display('hellow world')
。小部件对象可能封装了一个主机(例如 DOM)对象,但您永远不会直接引用它,也不会指定它如何添加到容器中(它可能会被附加或替换某些东西)。您也不会通过设置 innerhtml、innerText、textContent 或添加文本节点或任何其他方式来显示文本。这一切都由核心处理。
【参考方案1】:
我认为您是在询问是否要编写更多模块化代码,但 jquery 不是一个好案例。
异步模块定义 http://addyosmani.com/writing-modular-js/
【讨论】:
请查看我提供的会谈链接。这不仅仅是编写模块化代码。【参考方案2】:这是一个使用模块作为架构的非常简单的示例:
<!DOCTYPE html>
<title>Module play</title>
<body>
<script>
// myCore provides all functionality required by modules
// Could use a library in here
var myCore =
getContainer: function()
// code in here to find a suitable container in which to put widgets
// This is where different client capabilities will be tested to ensure the
// widget behaves in it's user agent context - desktop, phone, tablet, pad, etc.
// very simple shortcut
return
element: document.body,
// This function could use a general purpose library
add: function(widget)
this.element.appendChild(widget.getElement());
;
,
// This function could use a general purpose library
getNewWidget: function()
var element = document.createElement('div');
return
getElement: function()
return element;
,
display: function(text)
// Tightly couple to itself or not?
this.getElement().innerHTML = '<em>' + text + '</em>';
// or
element.innerHTML = '<em>' + text + '</em>';
;
// Missing sandbox layer...
// Add a module - only uses myCore API (access should be controlled by
// the sandbox), does not deal with underlying library or host objects
(function()
// Get a container to add a widget too
var container = myCore.getContainer();
// Create a widget
var widget = myCore.getNewWidget();
// Add the widget to the container
container.add(widget);
// Give something to the widget to display
widget.display('Hello World');
());
</script>
</body>
所以你可以看到,在模块级别,你并不关心宿主环境或底层库,你只是在编写普通的 ECMAScript。你可以采取真正的防御措施并做以下事情:
(function()
var container, widget;
if (!myCore) return;
if (myCore.getContainer) // Some would include an isCallable test too
container = myCore.getContainer();
// getWidget could be a method of container instead so that
// everything you need is either a method or property of container
// or widget
if (myCore.getWidget)
widget = myCore.getWidget();
...
等等,所以一切都经过测试和检查。我省略了错误处理,但希望这个例子就足够了。
【讨论】:
感谢您的详细回答,我知道您来自哪里。但是,您似乎本质上是将小部件的功能和行为推入核心,而将实际模块作为真正愚蠢的消费者。我认为这限制性太强,并且不认为它适用于企业应用程序。我的理解是,核心应该提供一组 功能(即 dom 操作/ajax/mediator)来隐藏基本库实现,然后通过一个沙箱公开,该沙箱充当一个一致的“胶水”层模块和核心。 参见 Nicholas 演讲的幻灯片 40/51/66。没有任何迹象表明模块无法在自己的范围内控制 dom。为了做到这一点,核心/沙箱需要某种形式的 dom 操作“服务”。有关我的意思的示例,请参见 Addy 演讲的幻灯片 105-109。本质上,沙箱应该为模块提供一组“服务”。我发现难以抽象的是那些服务调用的返回值(如果有的话)。 还可以查看AuraJS 的源代码(Addy 对 Nicholas 架构的实现)。您将看到沙箱是如何从 base.js 创建的,以及我所看到的沙箱方法(主要是 jQuery 对象)返回的内容中的泄漏抽象。顺便说一句,谢谢你的时间! 我认为您误解了这些幻灯片 - “不要访问沙箱外的 DOM 元素”并不意味着“使用 DOM 方法在沙箱中使用 DOM 元素”。允许一个 DOM 对象离开核心意味着你的模块必须进行特征检测,所以你又回到了原点。如果在我的示例中用于显示文本的元素从 DIV 更改为 textarea 会怎样?您的模块现在必须设置 value 属性,而不是 innerHTML(或等效)属性。或者也许功能检测 textContent/innerText。这就是您要避免的。 好的。所以澄清一下,每个模块都有自己的沙箱,作为模块的一个非常高级的“接口”?例如“选项卡”小部件可能在其沙箱中有一个.showTab()
方法,然后调用核心中的 dom 操作抽象?出于兴趣,您对 Aura 中的沙盒实现有何看法?以上是关于抽象 jQuery的主要内容,如果未能解决你的问题,请参考以下文章