4/12/2009

继承 Web 站点,第 2 部分: 优化您的 Web 站点

Brett McLaughlin (brett@newInstance.com), 作者兼编辑, O'Reilly Media

2008 年 6 月 12 日

让 Web 站点易于维护(请参阅 第 1 部分)之后,速度、可访问性和组织成为关键问题。学习如何分析站点的页面并改进其效率和布局。

假设您有一个 Web 站点,而且它仍然可以使用。您使用过此站点,它通过了验证,并且您确定它是易于维护的(有关如何实现这些目标,请参阅本系列的 前一篇文章)。即使它是可维护的,您的站点也可能运行和加载的速度很慢,它可能涉及文件系统上的所有文件 —— 或者,通常更糟的是,服务器端将所有的 HTML、CSS、JavaScript 都包含在了单个文件中 —— 而且几乎无法导航。那么,您如何接手一个可维护的 Web 站点并将其变成对于 Web 漫游者和 Web 设计者来说都很轻便、顺畅的目的地呢?

正如您可以使用标准工具和技术来让您的站点变得可维护(验证、分离 CSS 和 HTML,组织样式表)一样,您也可以执行一些常规任务来进一步改进站点的组织和使用。其中一些任务将提高设计者和程序员维护站点的能力;一些任务将让站点变得可以让您更快地访问;一些将让站点变得让人更容易而且更乐于使用它。这一切都很重要,而且有必要将它们摆到正确的位置。

再次讨论维护和组织


在上一篇文章中,您花费了大量时间来验证站点的 HTML 和 CSS。此外,您应该已经将页面的样式组件从 HTML 本身分离出来。现在,应该重审这些决定了,对页面的特定部分进行进一步分离,并确保 JavaScript 像 CSS 一样干净地从页面中分离出来。

HTML 和 CSS 不混合

无需将这一主题讲透(实在太多了),值得再提一次的是您需要 将 CSS 从主要 Web 页面中提取出来。CSS 应该是在外部样式表中,通常命名为类似
[page-name].css[category/group-name].css 的名称。因此,您可能具有一个名为 homepage.html 的页面,而其 CSS 样式表名为

homepage.css,或者甚至是 default.css。您还会看到许多名称类似 mobile.cssprint.css 的 CSS 文件,旨在分别格式化呈现给移动设备或打印机的文档。这些名称不仅仅是为了方便;它们使得查明站点上正在发生的事情变得非常直观,而这意味着搜索硬盘驱动器将花费更少的时间 —— 从而发生更少的失败。您和您的合作者快速无阻地工作时获得的效率提升实际上是不可测量的,但任何有经验的开发人员或管理者都会告诉您这十分显著。

曾经有一种争论认为,浏览器查找和加载附加 CSS 文件(以及 Web 页面 HTML 文件)所花费的时间是为了避免外部 CSS。甚至把我们活在一个 DSL、电缆调制解调器、T1 甚至 T3 世界的现实抛到一边,这种争论实在太愚蠢了(这已经够仁慈了)。除二进制算术之外,几乎就没有比计算机查找文件(假设文件位于相同的物理驱动器上)并在文件正好是文本的时候将其加载为纯文本更快 的操作了。事实上,浏览器将 Web 页面转换为 DOM 树(类似于树的节点组织)并可视化地呈现这些节点所花费的时间要比加载外部文本文件所花费的时间多得多。所以使用外部 CSS 吧,让自己轻松一些。


将 JavaScript 移动到外部文件中


不是针对 JavaScript

如果选择 VBScript 或 ASP.NET,或 Microsoft® 的 Ajax 风格,则在此讨论的与 JavaScript 有关的所有原则仍然适用。只需在内心中将选择的客户端技术 “JavaScript” 替换掉就行了。


将 HTML 与 CSS 分离的所有原因都是通用的;换句话说,并非因为 CSS 和 HTML 不能在一起,而是因为一般的最佳实践就是将结构(HTML)和内容(HTML 中的文本或数据)分开表示。这在松散意义上称为关注点分离。在编程领域,您甚至将看到一种类似的技术,称为单一职责原则,即将一个软件工件(类、模块等)仅做一件事情视为好的形式。将此原则应用到 Web 空间:HTML 文件代表数据;CSS 样式表代表特定结构化数据集的样式。因为这是两个不同的任务,所以它们归入两个不同的文件。


对于 Web 页面中的逻辑(通常用 JavaScript 表示),这同样适用。正如您通常会在 HTML 文档的头部(head 元素中)内看到 CSS 嵌套在

style 标记中一样,您还会在 HTML 文档的头部看到
JavaScript 嵌套在 script 标记中,如下:

<![CDATA[
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script language="JavaScript" type="text/javascript">
/* Create a new XMLHttpRequest object to talk to the Web server */
var request = false;
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
try {
  request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
  try {
    request = new ActiveXObject("Microsoft.XMLHTTP");
  } catch (e2) {
    request = false;
  }
}
@end @*/

if (!request && typeof XMLHttpRequest != 'undefined') {
  request = new XMLHttpRequest();
}

function getImageDetails() {
 var url = "lookupImage.php?image=" + escape(getImageName(this.src));
 request.open("GET", url, true);
 request.onreadystatechange = showImageDetails;
 request.send(null);
}

function showImageDetails() {
 if (request.readyState == 4) {
  if (request.status == 200) {
    var response = request.responseText;
    var splitResponse = response.split('|');
    var title = splitResponse[1];
    var date = splitResponse[2];
    var description = splitResponse[3];

    var titleElement = document.getElementById("info-title");
    var dateElement = document.getElementById("info-date");
    var descriptionElement = document.getElementById("info-text");

    replaceText(titleElement, title);
    replaceText(dateElement, date);
    replaceText(descriptionElement, description);
  }
 }
}


<!-- and so on... there's a LOT more JavaScript here -->
</script><title>Hoverbox Image Gallery</title>

<style type="text/css">
*
{
 border: 0;
 margin: 0;
 padding: 0;
}

/* =Basic HTML, Non-essential
----------------------------------------------------------------------*/

a
{
 text-decoration: none;
}

body
{
 background: #fff;
 color: #777;
 padding: 50px;
}

#page {
  position: relative;
}

#images {
  float: left;
  width: 400px;
}

#details {
  color: #000;
}

h1
{
 background: inherit;
 border-bottom: 1px dashed #ccc;
 color: #933;
 font: 32px Georgia, serif;
        font-weight: bold;
 margin: 0 0 20px;
 padding: 0 0 15px;
 text-align: center;
}
<!-- and so on... there's even more CSS that would follow -->
</style></head>
]]>



当然,这是两个 领域的最糟的部分:HTML CSS 位于头部。但所有这些可以提取出来并放置在外部文件中;假设它们称为 hoverbox.css
hoverbox-ajax.js。那么,上面糟糕的代码就可以更改为:

<!CDATA[

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />]]><b>
<script language="JavaScript" src="hoverbox-ajax.js"> </script></b>
<!CDATA[<title>Hoverbox Image Gallery</title>]]><b>

<link rel="stylesheet" href='css/hoverbox.css'
 type="text/css" media="screen, projection" />
<!--[if IE]><link rel="stylesheet" href='css/ie_fixes.css' type="text/css" media="screen,
 projection" /></b><!CDATA[<!endif]--></head>
]]>




注意:在这种情况下,实际上有两个 CSS 样式表:
hoverbox.css(对于所有浏览器),和 ie_fixes.css 中特定于 Internet Explorer 的 CSS 的修正集合。有关此特定示例代码的详细信息,请参阅本文结尾的 参考资料 部分。


一切都非常明显:第二个示例(其中 CSS 和 JavaScript 位于单独的文件中)令人难以置信地容易处理和管理。进一步,可维护性和可用性对于设计人员和开发人员都显著增加了。JavaScript 程序员和脚本设计人员在一个文件中工作,而不会弄乱核心 HTML。CSS 设计人员可以在 CSS 中进行修补,甚至为 Internet Explorer 或其他浏览器添加附加修复,而不必更改文档的结构。负责更改 HTML 的设计人员可以执行此任务,而无需担心滚动上百行脚本和样式,或更改某些东西,从而不仅弄乱页面的结构,还弄乱其操作和显示。

如果已经安排了 QA(质量保证)和测试过程,那么此处的价值就会成指数级增长。在这些情况下(至少作为一般规则),已更改的任何文件都必须重新测试。在此设置(关注点分离)中,可以更改 JavaScript 并进行测试,而无需 重新测试样式和结构。这是巨大的增值,尤其是在考虑用于测试 CSS 和 HTML 的工具和技术与用于测试交互的 JavaScript 驱动的 Web 页面的工具和技术完全不同时。

使用编程方式附加事件处理程序


JavaScript 在页面中的显示方式与 CSS 在页面中的显示方式之间有一个重要的区别。使用 CSS 时,规则应用于元素,所以将所有 CSS 放置到外部文件中相当容易。但是,使用 JavaScript 时,事情就不总是那么明朗;JavaScript 通常不应用于元素,而是应用于元素 特定的事件。所以您将看到如下的 HTML:

<!CDATA[
<li><a href="#"><img src="img/photo01.jpg" alt="description" ]]>
<b>onmouseover="javascript:getImageDetails();"</b><!CDATA[ />

<img src="img/photo01.jpg" alt="description" class="preview" /></a></li>
<li><a href="#"><img src="img/photo02.jpg" alt="description" ]]>
<b>onmouseover="javascript:getImageDetails();"</b><!CDATA[ />
<img src="img/photo02.jpg" alt="description" class="preview" /></a></li>

<li><a href="#"><img src="img/photo03.jpg" alt="description" ]]>
<b>onmouseover="javascript:getImageDetails();"</b><!CDATA[ />
<img src="img/photo03.jpg" alt="description" class="preview" /></a></li>
<li><a href="#"><img src="img/photo04.jpg" alt="description" ]]>

<b>onmouseover="javascript:getImageDetails();"</b><!CDATA[ />
<img src="img/photo04.jpg" alt="description" class="preview" /></a></li>
<li><a href="#"><img src="img/photo05.jpg" alt="description" ]]>
<b>onmouseover="javascript:getImageDetails();"</b><!CDATA[ />

<img src="img/photo05.jpg" alt="description" class="preview" /></a></li>]]>


这可以一直延续。在这个例子中,图库中有 20 个图像,每个都有 onmouseover 事件处理程序。乍看起来,这似乎是不可避免的 — 是将 JavaScript 附加到事件中必不可少的一部分。而且,如果您已经获得需要调用单个 JavaScript 函数的单个事件,这可能行得通(例如,单击特定的图像将调用 JavaScript 函数 returnToHomePage() 等)。但是,在类似上述情形(大量图像或对象都附加到同一个 JavaScript 函数中)的情况下,您最终将真正弄乱您的 HTML 页面。


但是,避免这种情况并不是那么困难,您只需要再多使用一些
JavaScript。当然,这意味着您可以将额外的 JavaScript 放在外部文件中,并忘记它 —— 还是关注点分离。您真正需要知道的是如何使要附加事件处理程序的元素变得惟一。例如,在上述代码中,假设我想要进行更改,并将所有这些鼠标事件处理程序附加到 “preview” CSS 类的图像中,并将事件从 onmouseover 更改为 onClick(这实际上是本文最后对此代码执行的操作)。在这种情况下,我需要如下的 JavaScript 函数:

function addImageHandlers() {
 var imagesDiv = document.getElementById("images");
 var imageElements = imagesDiv.getElementsByTagName("img");
 for (var i=0; i<imageElements.length; i++) {
  var class = imageElements[i].className;
  if (class == 'preview') {
   imageElements[i].onclick = getImageDetails;
  }
 }
}



其中大部分详细信息是特定于页面的,而且本文的重点不在于介绍如何使用编程方式分配事件处理程序。但是在 nutshell 中,这抓取了特定 div 中的所有 img 元素,命名为 “images”,然后在 “preview” CSS 类中查找其中所有的图像。最后,我使用 onclick 处理程序并将其分配给我想在此事件上运行的方法:getImageDetails()。结果是我可以在 img 标记中挖掘所有这些事件处理程序。


惟一的缺点是您需要在页面的 bodyonLoad 事件上运行此方法。但是,您已经将所有这些图像处理程序逐个抽取出来了。而且,最重要(但不是最明显)的是,您可以在 JavaScript 文件中更改分配了事件的元素,而不用 使用 HTML。假设您想要再次使用 onmouseover 事件或不同的元素;只需更改 addImageHandlers() 函数,您将永远不会打开 HTML。这是一个优点。


事实上,您可能想要一开始就让页面始终运行在实用程序函数中,比如 initPage()。然后,您可以将所需的一切放入此 JavaScript 函数中,添加、删除或更改事件处理程序,而且永远不用打开 HTML。关注点的进一步分离,对页面的未更改组件的测试更少,对 Web 页面的更新更容易。





回页首


服务器端包含:决定性因素


假设您从 HTML 提取出 CSS,将 JavaScript 放入外部文件中,您甚至设法将所有的事件处理程序任务在 onLoad 事件中合并为页面的 body 标记上的单个方法。您已经完成了页面的编程部分,对吗?不幸的是,尚未完成。您仍然要担心一件事:

服务器端包含(通常称为 SSI 或 SSIs)。它们通常如下所示:

<!--#config timefmt="%A %B %d, %Y" -->Today is <!--#echo var="DATE_LOCAL" -->


这将以指定格式打印出当天的日期。


服务器端包含在目前似乎是一次性事物;您可以大量使用,也可能完全不用。换句话说,您将发现到处存在这些包含的页面(用于时间戳、上次修改日期、计数器、页眉、页脚等等……好了,这个列表太长了)或者您在页面中根本 找不到服务器包含。事实上,您将很少发现一个站点使用 1 到 20 个 SSI;大多数站点或者完全避免使用 SSI(一个都不会用),或者疯狂使用 SSI。

SSI 是坏还是邪恶?

SSI 根本没有错。大多数程序员目前偏爱较新的脚本语言,比如 PHP、JSP 或 ASP(取决于所选的供应商或技术),但 SSI 通常是一种无需太多工作就可以在 Web 页面中添加少许功能的简单方法。

但是,SSI 在被过度使用时将会变成一个问题。例如,使用标准页脚时,可能具有一个颜色栏,一组联系链接和一个版权信息。可以将此页脚包括在站点的每个页面底部,如下所示:


<!--#include virtual="/footer.html" -->


但这相当方便,所以您也可以构造一个通用的页眉,并一开始就将其包括在文档中。导航通常也是通用的,所以 SSI 也可能适用。(虽然导航将基于当前页面有稍微的改变,但是带有导航链接的包含页面也可以使用一些 SSI 来实现一些显示逻辑。)而且您可能想列出最新的几篇博客,或者最近更新的几个页面,或者可能只运行另一个脚本来加载 cookie 并提供个性化问候。SSI 均适用。
of this.

问题在于每个步骤都会引发开销。您跟踪越来越多的小文件(这还没什么)但还要构建一组未归档的假设。每个页面必须将页眉和页脚包含在特定目录中(或对 SSI 包含进行更改以指向包含文件所在的正确目录)。它很快就变成了一个重要角色。而且,可能最重要的是,您将 SSI 作为脚本语言对待以构建动态页面。这不是 SSI 最擅长的;相反,SSI 擅长一些非常细微的功能。


缩减 SSI


如果试图让站点变得更好、(更)可维护、对于设计人员和开发人员更友好,则可以考虑尽可能合理地提取出尽可能多的 SSI。如果需要在当前日期中使用一个,那就没什么。如果您的页面仅仅是尖括号注释(<!--
-->)之间的 SSI 包含和逻辑的组合,则可以考虑简化您的站点,或转向使用功能完全的 Web 脚本语言,比如 JSP 或 PHP。

此外,如果您已经 在使用 PHP 或 ASP.NET 或 JSP,那么您应该将页面中所有的 SSI 都提取出来。一旦您已经投入(在时间和专业技术上)到一个脚本语言中,那么就坚持下来。您将具有相同数量的开销,但您只需使用较少的技术。HTML、CSS、JavaScript 和您的脚本语言已经远远足够了,无需将 SSI 添加到此混合物中。






回页首


组织站点的文件

如果已经跟上来,尤其是您已经具有一个在页面中组合了 HTML、CSS 和 JavaScript 的站点,您就已经解决了许多问题 —— 并潜在地创造了一些新问题。一旦开始将结构(HTML)从显示(CSS)、操作和行为(JavaScript)分离出来,您就具有许多散播在各处的单独文件(HTML、CSS 和 JavaScript)。

当然,还有大多数 Web 站点具有的正常文件都包含:10 个、100 个甚至 500 个不同页面的 HTML 页面,潜在的页眉和页脚导航支持文件,图像,文本文件,更多图像(相信您已经明白了)。而且,将一个文件向下加载三个目录和向上加载一个目录不会耗费太多时间,查找 此文件却需要很多时间。再一次重申,组织是关键,如果您想让站点长期保留(也就是说,在漫长的周末睡上两次,并忘记一切),花一些时间对其进行组织是十分关键的。


按种类和类型全局组织文件

最常用的布局选项(除了仅仅将一切统统塞进一个目录之外)是从站点的根目录开始,按类型组织所有文件。例如,您已经具有一个 images/ 目录,一个 css/ 目录,可能还有一个 js/scripts/ 目录,然后所有的 HTML 文件都在根目录下。因此,您的所有文档都具有类似 images/header.gif

css/default.css 的路径。

这是重要的第一步,因为您立即知道图像在哪里 —— 在自己的目录中。而且您的 CSS 也在自己的目录中。脚本呢?完全一样。对于您或新的设计人员和程序员来说,组织事情十分容易,而且完全是自文档化的。无需解释或冗长的文档就能够弄明白一切。


如果您按照十分常见的实践将 HTML 文件存储在子目录中,则还有另一个小小的优点。假设您已经使用 blogs/ 目录存放所有的 HTML 博客帖子,使用 music/ 目录存放对 CD 和音乐会的所有评论。如果将所有的图像存放在站点根目录下的 images/ 目录中,则在 img 标记中不必使用相对路径 src="local-image.jpg",而是使用 src="/images/image-name.jpg"


更重要的是,在您开始包含库模板中的页眉和页脚时,您已经在 blogs/ 目录中获得了一个文件来包含站点根目录(或者,甚至更好的是
templates/ 目录)中的页脚,您必须导航令人混淆的路径结构。所有路径都从站点根目录(/)开始并前进。这避免了跟踪您所在的目录,而且甚至更好的是,避免了类似 src="../../images/logo.png" 的冗长路径。


但是,此方法的缺点在于您的所有图像都位于一个位置;您的所有脚本位于一个位置;您的所有 CSS 位于一个位置。是的,这也是优点,但单个位置本质上意味着您可能具有名称冲突。如果您将
full-moon.jpg 存储在 /images/ 中以配合您令人深思的有关纽约的午夜博客帖子,然后在六个月之后,您在一个兄弟晚会上捕捉到您小舅子令人尴尬的一瞬,于是将
full-moon.jpg 存储在 /images/ 中,那时会发生什么?好了,就这样说吧,如果有人看到您写的有关曼哈顿美妙一夜的博客帖子时,他将会惊异于所看到的画面。


这是名称冲突 的典型情况:两个文件在同一目录中尝试共享一个名称。如果按种类组织一切,您将必须切记不要用具有相同名称的新文件覆盖旧文件。否则,您将发生冲突问题,没人想这样。

按站点分组的嵌套组织

第二种方法比全局分类稍微好一些,是利用分类的总体概念并将其递归应用。假设您的站点具有四个部分:产品、文档、支持和 FAQ。您已经在服务器的根目录下创建了四个目录,可能是 products/docs/support/

faq/。然后,您要将分类方法应用于其中每个 目录。

因此在 products/ 中,您将具有 images/ 目录、css/ 目录和 scripts/ 目录等等。产品的所有 CSS、JavaScript、图像和资源将位于此子目录中。本质上,您已经创建了产品的迷你站点。然后,同样的规则适用于其他三个目录。每个目录都将具有图像和脚本等等,所有内容都存储在此子站点的目录中。


在组织上,这比较容易处理。如果您在操作产品页面,则所有资源都在此目录中。而且可能更重要的是,其他页面的所有资源都 在产品迷你站点中。所以您只需处理少数文件,其中所有文件都与您操作的内容相关。

最后,这使得名称冲突更容易避免。您将处理更小的文件集合,使用相同名称的几率将减少。这并不意味着它们不会发生,但因为文件比较少,所以甚至可以在替换或覆盖具有相同名称的文件之前手动进行检查。

当然,这种方法的缺点在于您很容易重复一些资源。如果所有的 “迷你站点” 上的所有页面都使用相同的页眉、页脚或徽标怎么办?突然,迷你站点崩溃了,或者至少变得复杂了。当然,答案并不深奥;相当的显而易见。


使用全局和区段分组之间的混合

实践证明,为站点组合一个合理的物理布局,您实际上需要两个级别的分类。首先,您需要弄清楚站点中的一切全局 内容:跨越两个或多个迷你站点。所以如果您已经具有整个站点的页脚,或一个通用的徽标,或所有页面使用的 CSS 样式表,这些都是全局资源。它们不应该嵌套在子目录中,因为您的所有页面都需要它们。

然后,一旦您弄清楚全局资源之后,就将其放置在 Web 服务器根目录下的分类子目录中。所以,您已经将全局样式表放置在 /css/ 中,并将所有页面需要的脚本放置在

/scripts/ 中。然后,应用如前所述的迷你方法:分离到子目录中,并将只有特定迷你站点使用的资源放到相关子目录中(仍然是 images/
scripts/ 或任何适当的位置)。

如果采用此方法(而且它是 Web 专家最常使用的方法),您实际获得了两个世界的最高境界。您通过隔离迷你站点减少了一个位置的文件数,并合理地避免了名称冲突。但您还降低了资源的重复,所以共享文件的迷你站点只使用此文件一次。


但是,您必须做出一个选择:是否、何时引用全局文件,使用 “../” 表示法(比如
../images/logo.jpg),或绝对路径表示法(比如
/images/logo.jpg)。您最好避免所有的 ../ 类型(这将迅速转化为 ../../ 甚至 ../../other-section/images)。如果您已经使用了此混合法,那么您将在类似 images/blog-header.gif 的路径中获得特定于本地迷你站点的图像。这十分便于查看和欣赏,而且仍然尽量保持自文档化。而且,结果里外都是易于理解的 HTML 页面。这对于您、您的团队成员以及使用您页面的任何人来说都是一件好事。


避免大桶方法


不管使用什么方法,实际上只有一个方法是明显错误的:大桶方法,即将一切抛到一个目录中。您通常可以通过 public_html 目录标识这些设置,此目录通常由启动站点或 GUI 工具使用。不管是启动站点还是 GUI 工具都需要您这样组织内容(也就是说, 组织内容),对于一些人来说,通常更简单的是将一切都保留为 GUI 编辑器或 FTP 工具所布置的那样。


此方法最明显的问题是,与页面 数相比,系统中的文件数通常按指数级增长。所以一个页面通常引用两个或三个其他页面,五个或十个图像,可能一个 CSS 样式表和一些 JavaScript —— 甚至是类似通用样式表或 JavaScript 文件的共享资源,事情迅速变得不可控制,而且,事实上,您将费大力气去逐个组织您的 HTML 页面,那么为什么不将相同的原则应用于站点的物理布局呢?


在按类别全局分组一切那一节中也有相同的问题:名称冲突。您将图像放入具有数百个其他图像(更不用说数百个所有 类型的文件)的目录中,您将不得不认真思虑一番了。当然,您可以只执行一个简单的 ls

dir,但大多数不花时间组织其站点物理布局的人也不会花时间来采用这样的预防步骤。结果呢?覆盖的文件、图像(或
CSS 或 JavaScript)被神秘地 “更改”,页面乱七八糟地分散在站点中与您预期完全不同的部分中。





回页首


流线化 CSS

CSS 是一个可以事半功倍或事倍功半的地方之一。通过在站点或者至少是页面范围应用几个规则,就可以获得巨大的效果,但是通过应用许多特定的规则,您可以获得难以置信的详细的、极具魅力的页面。问题在于使用所有这些全局规则、伪选择器,以及仅应用于标有 “i-hardly-ever-appear” 类的段落元素中的图像的样式,您就可能开始让 CSS 膨胀起来。当您具有从未使用过的样式表规则(糟糕但不可怕)或互相冲突或覆盖的规则时,确实会让人心烦,而且是一个潜在的大问题,CSS 膨胀就会发生。要获得组织良好且可维护的站点,您的工作就是摆脱这两个问题。

挖掘未使用的规则


您应该已经通过 CSS 验证器(请参阅 参考资料 部分中 HTML
和 CSS 验证器的链接,以及本系列的上一篇文章,其中详细介绍了 CSS 验证器的使用)运行了您的页面。这将暴露其中大多数问题,即使执行的操作并不是那么清晰。首先,如果验证页面并发现几个 CSS 规则没有使用,不要立即断定您可以删除这些规则! 当您构建由多个页面甚至是整个页面使用的样式表时,您将使规则仅应用于站点页面的一个子集。您可以争论说,只有应用于站点每个页面的规则属于站点范围的 CSS,但这种逻辑只是说起来比做起来容易的典范。

接下来要进行一些必需而且乏味的工作。您将必须至少运行站点页面的大量取样来查明是否有一些规则没有在整个站点中使用。至少尝试 10 个页面,对于每个页面,尝试并记录下没有使用的规则(未使用的规则通常比使用的规则要少得多)。然后,一旦获得一个好的样本之后,就会看到哪些规则没有显示在任何页面上。尝试在样式表中将其注释掉,看看发生了什么。当然,随着 CSS 规则慢慢稳定,站点具有了 100 个或更多的页面,真正能将此测试出来的惟一方法是尝试站点上的每个页面,并看看有什么影响美观的地方没有。

不幸的是,必须指出,有时候这一优点不值得付出努力。您的页面将不会更快地加载,但是 CSS 将比较干净,而且是站点上真正发生的操作的更好的表示。在多数情况下,最好的解决方案是尝试并删除任何明显未使用的规则。但是如果在整个站点上使用了 10 个 CSS 样式表,或者单个样式表具有数百个规则,那么您可能会忙一整天。


忽略重叠规则


尝试根除互相重叠和覆盖的规则甚至是一项更加劳动密集的任务。它只关乎构建应用于页面的层次结构。在纸上,在头脑中,或者使用工具。(如果您找到了,就告诉我们!)例如,如果已经获得应用于整个页面的任何全局规则,则这位于层次结构的顶部。然后是应用于页面主体标记或可能一个 p 标记的任何规则。然后是应用于特定 p 标记或
div 的规则,等等。您最终将得到一个级联 —— 浏览器应用的相同种类的级联。顶部的规则,然后是其下的规则,都应用到页面和样式表上。更具体的规则覆盖更一般的规则。所以,应用于具有 “acoustic” 类的 “guitars” div 中链接的规则将替换并覆盖为整个页面设置的标记。


此处的问题在于构建和分析此层次结构都是非常乏味的。您已经看到应用于整个页面的规则,然后确保应用到元素上的其他任何规则都不会受最顶层规则的影响。换句话说,如果没有任何元素是由高层规则设置样式,因为更具体的规则始终有效,那么您应该摆脱那个未使用的规则。

例如,假设具有如下 CSS 规则:

body {
 background: #fff;
 color: #777;
 padding: 50px;
}



然后,假设您的站点策略(已文档化并向整个团队解释了,对吗?如果您是一位独立开发人员,您是否已经将它们应用于显示?)指明所有页面在下列四个 div 元素之一中维护其内容:一个 div 用于页眉,一个用于页脚,一个用于导航,一个用于内容。每个 div 具有一个 ID 来标识它所提供的功能。所以您可能具有一些附加规则,如下:


body {
 background: #fff;
 color: #777;
 padding: 50px;
}

<b>div#header {
 background: inherit;
 border-bottom: 1px dashed #ccc;
 color: #000;
 padding: 50px;
}

div#nav {
 color: #888;
 padding: 50px;
}

div#footer {
 clear: both;
 font: 10px Verdana, sans-serif;
 padding: 10px 0;
 text-align: center;
 color: #000
}

div#content {
 background: inherit;
 color: #777;
}
</b>


此处的问题在于如果您将所有页面放在这四个
div 的其中一个中,那么在不覆盖的情况下,body 选择器中的 color: #777; 规则永远不会得到应用。每个 div 都具有自己的 color 来覆盖 body 选择器。但您如何发现这些东西呢?通过大量的试验、错误,有时候还需要一点点运气。所以您将必须自己决定是否值得去查找并删除这些实例。


但是,这里也有一些真正的好处:通过删除规则可以应用到的最具体的元素中尽可能多的规则,意味着您的整个页面具有比较少的规则。这同样意味着,如果您添加新内容,那么您不必疑惑它为什么显示为宽字体的粉色文本。可能不会有大量规则应用于其上,因为它没有使用 ID 或 CSS 类进行标记,或插入到样式元素中。越简单越好,意味着您可以设计而非删除应用于新内容的其他规则。





回页首


动态页面是否真正动态?

到目前为止,我们已经讨论了页面中所有必要的组件。如果您的页面具有 SSI,那么您或者确保它们是稀疏的,或者可能将其转换为其他脚本语言 —— 但针对 SSI 是否提供了有用的用途没有任何真正的分析。这同样适用于 CSS、JavaScript、所有图像等等。如果它存在于您的页面上,那么它必须就在它应该的位置上,对吗?

是否真正需要时间戳?


在大多数情况下,处理任何 Web 页面时,最消耗时间的部分是处理此页面中的动态信息。是否预加载图像翻转或初始化 XmlHttpRequest 对象以用于稍后的 Ajax 请求,或分配事件处理程序,您的代码将花时间处理这些问题。浏览器在呈现 HTML 方面十分卓越,当前甚至在加载图像方面也相当不错,但任何一种服务器处理都要花费时间。每个服务器端包含都必须处理,JSP(JavaServer 页面)标记中的每个 JavaBean、VBScript 中的每个宏都必须处理和执行。这都要花时间,而且通常意味着您的页面将不会快速呈现在用户浏览器中。


这就是您为什么必须摆脱您的程序员或脚本设计人员身份,用户的角度来考虑。您页面中一切需要花时间去加载的组件(尤其是在页面加载时加载的服务器端包含和 JavaScript,通常位于 body 标记的 onload 事件上)是否让您的页面变得更好?甚至更重要的是,是否改进了用户体验?因为如果它仅仅是一个装饰,或者是向别人展示的 “实在很酷” 的项目,那么就对了。


时间戳、日期戳、最后修改的页面、页面最后修改日期和时间 —— 所有这一切都可能是 Web 页面中可以删除的不重要的组件。如果是这样 —— 如果您可以说您的用户实际上不需要这些东西 —— 那么您删除它们也可以很好地服务您的用户和您的页面。您的页面加载时间应该会缩短,您已经确保了页面只包含它们所需的内容。

还要避免热心过度

在您大刀阔斧削减用户页面的所有功能之前,必须提一句警告。是的,您将降低页面的加载时间,是的,您可以删除页面中无关紧要的脚本和交互部分,但有时候您需要一些 交互性。而且,有时候,时间戳或上次查看的时间确实 有用。


许多时间,开发人员发现他们可以通过删除所有小的 SSI 或 onclickonmouseover
事件极大地简化其页面。但是,事情还可能变得无法掌控。进行足够的删除之后,您的页面可能最终不仅仅是修剪,而是平凡单调。记住,要考虑(和回答)的问题是,“这是否改进了用户体验?”尽管答案是 “否” 的时候,删除一些东西是重要而且有用的,但在答案是 “是” 的时候,保留一些东西也同样重要。





回页首


结束语


在 Web 上的大多数时候确实是这样,简单性通常是大多数疾病的解药。如果您的站点庞大而且笨拙,您可能需要将其物理布局重新组织为比较简单的迷你站点。如果您的页面混乱得难以编辑、更新和维护,您可能需要分解您的 CSS 和 JavaScript。如果 SSI 遍布在页面的各个角落,最简单的方法通常是去掉 SSI。如果您的页面像岩石般坚固,从而似乎要花一段时间去加载,那么考虑切掉一些所谓的交互性,即那些看起来养眼或比较酷但实际上不会改进用户体验的交互性。

但是,在所有这些情况下,在执行任何操作之前,必须首先考虑它是否影响了用户的体验,然后考虑是否影响了您的工作环境。如果某件东西让您站点上的维护和工作变得容易了,但让您的页面看起来十分笨拙,或者使页面变得无法导航,那么您必须处理这项工作,从而让您的页面易于使用。但是如果您改进了用户体验,或者甚至就保持原样,但可以改进查找文件、或更改行为、或排列页面中的混乱链接的容易程度,那么您就为自己做了一件好事。

最后,目标在于查找问题、隔离问题并将其修复。然后,您的工作还没有完成;您必须查看所做的更改是改进了站点,还是没有作用,还是使站点在旁观者角度上变得难以使用。仅当所做的更改在用户角度上看起来仍然效果良好时,您才能将其应用。而且,进行小小的更改,您就会改进您的 Web 站点。而且,当您更改站点的外观或功能时,您必须坚持 这种方法。如果在您操作新内容时,您或您的开发人员或设计人员抛弃了所有这些原则,那么您的站点将继续在可维护和大麻烦之间摇摆 —— 而且您将度过许多个头疼的夜晚。



参考资料

学习

获得产品和技术

  • Head First
    HTML with CSS & XHTML
    (Elizabeth 和 Eric Freeman,O'Reilly Media,
    Inc.):了解有关标准化 HTML 和 XHTML、以及如何将 CSS 应用于 HTML 的更多信息。




  • JavaScript:
    The Definitive Guide
    (David Flanagan,O'Reilly Media, Inc.):包含有关使用 JavaScript、动态 Web 页面的详细说明,而且未来的版本将添加两章有关 Ajax 的内容。


  • 下载 IBM
    产品评估版
    ,并使用来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 中的应用程序开发工具和中间件产品。



讨论