HTML 组件框架






4.50/5 (4投票s)
用于创建和使用 HTML 组件的框架。
引言
本文演示了 HTML 组件框架 的一些功能。它基于框架托管在 Google 代码网站上的 Wiki 中的“Hello World”示例(http://code.google.com/p/htmlcomponentframework/)。该示例展示了如何将组件内联放置在 HTML 页面中,向其触发一个事件,并接收它引发的一个事件。
背景
在开发 Web 应用程序时,我经常面临以下两个问题:
- HTML 文件会变得非常大,因为它必须在一个单一的整体文件中包含用户界面控件。
- 我对有用的通用用户界面控件无法获得任何重用。
为了解决这两个问题,我意识到我需要能够将用户界面控件或控件组单独定义,我称之为“HTML 组件”。
我以前写过 ASP.NET 服务器控件,并使用过 JSF,但不喜欢在服务器端代码中混合 HTML 和 JavaScript;它很混乱且难以阅读。我还发现这些技术破坏了设计-开发周期:来自设计团队的精心制作的 HTML 需要修改才能适应服务器所需的格式;然后,对原始 HTML 的任何设计更改都需要重复此过程。显然,用一种技术编写的组件不容易移植到另一种技术,并且无法在不运行服务器的情况下进行测试。事实上,我认为这些技术使 Web 应用程序开发变得不必要地复杂,唯一的优势是自动管理 AJAX 调用(尽管使用其他框架手动完成这一点并不难,例如:jQuery/YUI AJAX 用于客户端;Jayrock,Jabsorb 用于 .NET 和 Java 服务器端)。
我也使用过来自 jQuery UI 和 YUI 的客户端控件,并发现尽管它们很有用,但由于它们的 HTML 被包裹在 JavaScript 中,因此扩展或修改它们并不优雅——同样,它们像上面提到的服务器端对应项一样破坏了设计-开发周期。
出于这些原因,我开发了一个框架,允许我创建单独的组件,将 HTML 保存在 .html 文件中,将 JavaScript 保存在 .js 文件中。这些组件随后只需使用浏览器即可进行测试(无需编译或服务器),可用于不同的应用程序,从而有助于简化 Web 应用程序开发。
“Hello”组件示例
尽管此示例非常简单且人为,但其目的是演示 HTML 组件框架的主要功能。有关该示例的图示说明,请参见下图。

主要的“hello”网页包含标签“Name to greet:”,一个用于输入姓名的文本框,以及一个“greet”按钮。它还包含一个“hello”组件,用于显示问候语。“hello”组件包含问候语和一个“hide greeting”按钮。最初,“hello”组件内的问候语是隐藏的。
其逻辑步骤如下:
- 按下“greet”按钮后,“Name to greet:”标签、文本框和按钮将被隐藏,并向“hello”组件触发一个
"greet"
事件,携带要问候的姓名。 - 收到事件后,组件会将事件中的姓名复制到问候语中,并显示问候语。
- 按下“hide greeting”按钮后,问候语将再次隐藏,并向主网页引发一个
"greetingHidden"
事件。 - 收到事件后,标签、文本框和按钮将被重新显示,以便可以重复该过程。
Using the Code
代码包含四个文件:
- HelloComponent.html - “hello”组件的 HTML
- HelloComponent.js - “hello”组件的 JavaScript
- Hello.html - “hello”网页的 HTML
- Hello.js - “hello”网页的 JavaScript
双击文件“Hello.html”可在默认浏览器中启动示例。
“Hello”网页
Hello.html
<head>
节点包含对 HTML 组件框架 CSS 和 JavaScript 以及“Hello”网页 JavaScript 的引用。它还包含两个供框架使用的 meta 标签。
- 第一个 meta 标签描述了不兼容 HTML5 Web Messaging 的浏览器将使用的事件处理代码的位置。
name
属性表示该标签包含事件处理代码 URL,content
属性包含 URL;这里使用的是 Google 代码服务器托管的事件处理代码。<meta name="eventUrl" content="http://htmlcomponentframework.googlecode.com/svn/examples/Event.html"/>
- 第二个 meta 标签描述了将在网页中内联使用的组件。
scheme
属性将此标签的内容定义为 JSON 格式。name
属性表示要包含一个组件供页面使用。content
属性包含描述该组件的 JSON;它由以下属性组成:name
- 用于引用组件类的名称。url
- 组件 HTML 的位置。
<meta scheme="JSON" name="includeComponent" content='{"name":"HelloComponent","url":"HelloComponent.html"}' />
组件的一个实例以如下方式内联放置在网页中:
<div id="helloComponentId" title="HelloComponent" class="cf_component_marker">
<input type="hidden" name="loadedCallback" value="helloComponentLoaded" />
</div>
id
属性为此组件实例分配了一个唯一的标识符,JavaScript 将使用它来识别此实例。title
属性通过使用其类引用名称指示正在放置哪个组件;在这种情况下,它是上面 meta 标签中包含的“HelloComponent
”。class
属性用于通过使用 cf_component_marker
标记类向框架指示这是一个组件(注意:还可以使用其他样式类来根据需要设置 <div>
元素的样式)。
在 <div>
元素内,包含了一个隐藏的 <input>
字段,以指示框架在组件加载完成后调用一个函数。这是必要的,以便可以执行额外的设置,例如向组件附加事件处理程序。name
属性表示该值为组件加载后要调用的函数。value
属性包含将在下面解释的 helloComponentLoaded
函数名称。
Hello.js
此 JavaScript 使用 jQuery 来确保一旦网页加载完毕,HTML 组件框架就会启动,并且“greet
”按钮将附加一个“click
”事件处理程序。
jQuery(window).load(function()
{
ComponentFramework.loadAndRender();
jQuery("#greetButton").click(greet);
});
如上所述,框架在组件加载后通过隐藏的 <input>
字段中引用它来调用以下函数。它使用 HTML 中定义的标识符获取组件的引用,并将 greetingHidden
函数与组件可以引发的 "greetingHidden"
事件关联起来。
function helloComponentLoaded()
{
ComponentFramework.getComponent("helloComponentId").addEventListener
("greetingHidden", greetingHidden);
}
下面定义了 greetingHidden
函数。它使用 jQuery 来显示问候语输入区域。
function greetingHidden()
{
jQuery("#greetingInput").show();
}
以下函数在按下“greet”按钮时调用(它已在 JavaScript 的最前面几行中附加)。它使用 jQuery 来隐藏问候语输入区域,获取组件的引用,并向其触发 "greet"
事件。它包含一个具有 name
属性的对象,该属性的值是从输入文本框读取的。
function greet()
{
jQuery("#greetingInput").hide();
ComponentFramework.getComponent("helloComponentId").fireEvent
("greet", {name: jQuery("#name").val()});
}
“Hello”组件
HelloComponent.html
“Hello”组件的 <head>
节点与“Hello”网页一样,包含对 HTML 组件框架 CSS 和 JavaScript 以及“Hello”组件 JavaScript 的引用。它还使用 meta 标签指向不兼容 HTML5 Web Messaging 的浏览器将使用的事件处理代码的位置。此外,它还有一个 meta 标签,用于指示框架它是一个 HTML 组件。
<meta name="HTMLComponent" />
该组件可以接收和引发的事件使用 meta 标签进行定义。这确保了严格的事件定义合同,以提高安全性,并确保传递给事件的任何数据都符合特定模式。尝试触发或引发未定义的事件或发送与模式不匹配的数据会导致框架忽略这些事件。该组件有两个事件:"greet"
和 "greetingHidden"
。
- 以下定义了组件可以接受的
"greet"
事件。scheme
属性表示此标签的内容将是部分 JSON schema(有关 JSON schema 的更多信息,请参阅:http://groups.google.com/group/json-schema/web/json-schema-proposal-working-draft 和 http://tools.ietf.org/html/draft-zyp-json-schema-02)。name
属性表示这是一个组件可以接受的事件。部分 JSON schema 是一个对象,该对象有一个以事件名(在此例中为"greet"
)命名的属性,并且该属性的值是一个 JSON schema。这里,JSON schema 定义了一个对象,该对象必须由一个具有单个属性"name"
的事件携带,该属性的值必须是string
类型。<meta scheme="PartialJSONSchema" name="acceptsEvent" content='{"greet": {"type": "object","properties": {"name": {"type": "string"}}}}' />
- 以下定义了组件可以引发的
"greetingHidden"
事件。其定义方式与可以接受的事件完全相同,不同之处在于name
属性表示这是一个由组件引发的事件。在这种情况下,事件被定义为不携带任何对象。<meta scheme="PartialJSONSchema" name="raisesEvent" content='{"greetingHidden": {"type": "null"}}' />
HTML 的其余部分按照普通网页的方式编写。对于此组件,它定义了一个用于写入问候语的 region
和一个用于隐藏问候语的按钮。
HelloComponent.js
组件 JavaScript 定义了一个与 HTML 同名的类(不包含文件扩展名,即 "HelloComponent"
)。HTML 组件框架将在组件加载时自动创建一个该对象的实例。
以下是“Hello”组件的完整代码。构造函数将 hideGreeting
函数分配给“hide greeting”按钮的“click
”事件处理程序。然后,它定义了一个名为 greet
的特权函数;这匹配组件可以接受的 "greet"
事件的名称——收到此事件后,框架将调用此函数。请注意,事件携带对象的最顶层属性被拆分为函数的参数,在本例中是 name string
参数。greet
函数将“Hello”+name
写入 HTML 并显示问候语。最后一个 private
函数是 hideGreeting
,它被分配给“hide greeting”按钮的 click
事件。它会隐藏问候语,并向组件的容器(“Hello”网页)引发 greetingHidden
事件。
function HelloComponent()
{
jQuery("#hideGreetingButton").click(hideGreeting);
this.greet = function(name)
{
jQuery("#helloContainer").text("Hello " + name);
jQuery("#greetingPanel").show();
}
function hideGreeting()
{
jQuery("#greetingPanel").hide();
ComponentFramework.raiseEvent("greetingHidden");
}
}
关注点
我从使用 HTML 组件框架的经验中发现,它有助于将 Web 应用程序的功能分解为可管理且易于理解的组件。这种分离使我能够专注于应用程序的特定部分,从而以模块化的方式进行开发。直接在浏览器中测试组件的能力已被证明是无价的——通过模拟 AJAX 调用,可以编写一个完全在客户端运行的 Web 应用程序。这意味着,如果就 AJAX 接口达成一致,客户端和服务器端开发就可以独立进行。
该框架可用于包装和扩展其他库(例如 jQuery UI 或 YUI)的用户控件,以帮助应用程序的开发更加一致;但是,我最近一直在研究模板框架以帮助处理组件的动态内容。在查看了 Closure Tools 后,我发现它的编译阶段破坏了设计-开发周期,因此我开始使用 jQuery Templates,它们被证明是很有前景的,并且与 HTML 组件框架的理念相辅相成。
我对为框架编写的 HTML5 之前的事件处理感到满意,并且应该感谢网络上描述如何在跨域 iframe 之间发送消息的许多资源。在支持的浏览器中使用 HTML5 Web Messaging 来实现这一点很有趣。
该框架特别有趣的一点是能够使用来自不同域的组件并在它们之间引发事件。这意味着,如果有人托管他们自己的组件,那么其他人就可以使用它,而无需复制代码。因此,所有者可以通过仅更改其主机上的代码来立即推出修复和更新。