领先技术

CSS 编程:LESS 助您事半功倍

Dino Esposito

在本文中,我将介绍使用 LESS Framework 动态生成 CSS 内容的 Web 开发。

毫无疑问,CSS 代表一大飞跃,它有望将内容与网页显示完全分离,而且在很大程度上实现了这一点。尽管 CSS 属于(或应该属于?)设计人员关注的范畴,它率先提出了关注点分离原则,几乎每个开发人员都对此非常敏感。因此,CSS 很快获得采用,它在 Web 开发过程中如此根深蒂固,以致于有时难于满足最新网站的需求。

问题并不是 CSS 不足以满足样式最新、图形丰富并且外观吸引人的网站需求,而是纯粹的声明语言并非始终适合表现复杂的互联样式声明。好在只要编写正确,CSS 对于浏览器仍然合理,但对人来说也是如此吗?

Web 开发中一个相对较新的方向是围绕 CSS 构建基础结构,使开发人员和设计人员能够以可持续性更高的方式生成相同的 CSS。最终的浏览器样式表并无变化,但其生成方式应有所不同,更易读也更容易管理。

这一 Web 开发领域出现于几年前,现在日渐成熟,有几个可用的框架可以帮助您进行动态 CSS 内容生成。我将提供其中一个框架,也就是 LESS Framework 的执行概要,并演示它如何与 ASP.NET MVC 解决方案集成。

为何采用 LESS?

开发人员借助 LESS 解决的最大问题之一,是信息的重复。作为软件开发人员,您可能了解“切勿重复”(DRY) 原则并且每天都在应用。DRY 的主要优点在于,它减少了相同信息的存储位置,因而减少了应更新该信息的位置的数量。

对于普通 CSS,您根本不会遇到 DRY 问题。例如,在某些其他情况下,如果多个类中使用某种颜色,而您需要对其进行更改,可能除了逐一更新之外没有更好的办法了。使用 CSS 类可以定义特定元素的外观,并针对样式相关的元素以相同的方式跨页面重复使用。尽管 CSS 类确实可以减少重复,但有时在其他方面显得不足。

CSS 类的一个问题是它们在语义 HTML 元素的级别运行。在构建各种 CSS 类时,经常需要重复颜色或宽度等细节信息。对这些可重复细节中的每一个使用同一个类并不方便。即使设法用一个 CSS 类表示几乎所有可重复样式,如颜色和宽度,当开始对语义元素(如容器)应用样式时,还是应该连用多个 CSS 类以便实现所需的效果。

如果您曾用 Bootstrap 等框架设计过网页,就会明白我的意思。例如:

 

<a class="btn btn-primary" ... />

首先将锚点设置为一个按钮(类 btn),然后设置为特定风格的按钮(类 btn-primary)。 这种方法有效,但可能需要进行大量工作提前计划所需的类。 这会增加 Web 项目的工作量,而项目往往时间紧迫。

LESS 这样的动态样式表语言表示一种横向思维。 您不必花时间尝试使普通 CSS 变得更加智能;只需使用不同的工具(一般是语言)进行生成即可。 因此,LESS 是一种框架,它向 CSS 编码添加了编程人员友好的概念,如变量、块和函数。

与动态 CSS 生成严格相关的,是将其处理为普通 CSS 供浏览器使用的问题。 客户端可以通过临时 JavaScript 代码处理 LESS 代码,也可以在服务器上对其进行预处理,使浏览器只接收最终的 CSS。

在 ASP.NET MVC 中设置 LESS

我将演示从 ASP.NET MVC 应用程序中使用 LESS 所需的操作。 首先,我将重点介绍 LESS 代码的客户端处理。 在布局文件的 HEAD 部分添加以下内容:

<link rel="stylesheet/less"
  type="text/css"
 href="@Url.Content("~/content/less/mysite.less")" />
<script type="text/javascript"
 src="@Url.Content("~/content/scripts/less-1.3.3.min.js")"></script>

这里假设您已在项目中创建了 Content/Less 文件夹以包含所有 LESS 文件。 您需要一个 JavaScript 文件,以便在浏览器中进行实际的 LESS 到 CSS 处理。 您可以从lesscss.org 获取该脚本文件。 我将回顾一些可证明 LESS 有用的场景。

运行中的 LESS:变量

观察 CSS 渐变是了解 LESS 变量作用的好方法。 多年以来,设计人员使用小的 GIF 文件绘制 HTML 容器的渐变背景。 最近,浏览器增加了 CSS 渐变支持。 这也是官方 CSS3 标准的一部分,通过线性渐变语法及其变体实现。 很遗憾,如果要确保尽量多的浏览器采用这种渐变,必须借助一些东西,如图 1 中的代码。

图 1 中的代码几乎不可读。 更糟的是,在需要渐变的任何地方都必须重复使用这些代码。 此外,如果要稍微更改一下渐变颜色(或只更改饱和度或淡化效果),唯一的办法是在所有位置进行手动编辑。 直截了当地说,这非常困难。 但是,这是对于普通 CSS 唯一可行的方法。

图 1 在多种浏览器中显示渐变的综合代码

/* Old browsers fallback */
background-color: #ff0000;
background: url(images/red_gradient.png);
background-repeat: repeat-x;
/* Browser specific syntax */
background: -moz-linear-gradient(  left,  #fceabb 0%, 
  #fccd4d 50%, #f8b500 51%, #fbdf93 100%);
background: -Webkit-linear-gradient(  left, #fceabb 0%,
  #fccd4d 50%,#f8b500 51%,#fbdf93 100%);
background: -o-linear-gradient(  left, #fceabb 0%,
  #fccd4d 50%,#f8b500 51%,#fbdf93 100%);
background: -ms-linear-gradient(  left, #fceabb 0%,
  #fccd4d 50%,#f8b500 51%,#fbdf93 100%);
/* Standard syntax */
background: linear-gradient(  to right, #fceabb 0%,
  #fccd4d 50%,#f8b500 51%,#fbdf93 100%);

为找到更好的解决方案,您需要跳出 CSS,试试 LESS。 在 LESS 中,您只需定义一次渐变 CSS,然后在适当的位置按名称引用即可。 例如:

.background-gradient-orange { background: #fceabb; ... }
.container { .background-gradient-orange; }

名为 background-gradient-orange 的类按名称在适当的位置嵌入到类容器和任意其他类中。 而渐变定义保存在一个位置。

从开发人员的角度来看,这并不是什么革命性的改变。 不过,它使用了变量功能,这是 CSS 中没有的。 事实上,如果将文件保存为普通样式表并引用的话,前面的语法并不可行。 需要一些代码将扩展语法转换为普通 CSS。 这正是 LESS JavaScript 分析器的功能,它将变量扩展为其实际 CSS 内容。

变量还适用于颜色或大小等标量值。 请考虑以下 LESS 代码:

@black: #111;
#main {  color: @black; }
.header { background-color: @black; }

分析器将 @black 变量扩展为分配的值并进行全文件替换。 最终效果是,在一处更改实际颜色,整个文件中的相应内容会自动更改。

运行中的 LESS:Imports

如果需要,您可以将 LESS 代码拆分到多个文件、引用文件和包含的类中。 例如,假设您创建一个 gradients.less 文件,内容如下:

.background-gradient-orange { background: #fceabb; ... }

在另一个 LESS 文件中,如 main.less,您可以通过导入该文件引用任何渐变:

@import "gradients";
.container { .background-gradient-orange; }

如果 gradients.less(扩展名不是严格必需的)位于其他文件夹中,则应在调用中指示路径信息以便导入。

LESS Mixin

我调用该 LESS 项目进行变量渐变。 严格来说,这并不完全正确。 在 LESS 中,一个变量包含一个单一值。 一个 CSS 类容器被称为一个 mixin。 这类似于函数,但不包含任何自定义逻辑。 就像函数一样,LESS 可以接受和处理参数。 请看看图 2 中演示 mixin 的代码。

图 2 中,一个名为 shadow 的 mixin 定义了框阴影样式,并将颜色作为外部参数公开。 与之类似,text-box mixin 定义输入字段的基本外观。 它导入阴影定义并保持宽度为参数化形式。 通过这种方式,为不同大小(mini、normal 和 large)的输入字段定义三个类就非常简单了。 更重要的是,它只需进行少量编辑工作,更新也非常简单(请参阅图 3)。

图 2 LESS Framework 中的 Mixin

/*  Mixins  */
.shadow(@color) {
  box-shadow: 3px 3px 2px @color;
}
.text-box(@width) {
  .shadow(#555);
  border: solid 1px #000;
  background-color: #dddd00;
  padding: 2px;
  width: @width;
}
/*  CSS classes  */
.text-box-mini {
  .text-box(50px);
}
.text-box-normal {
  .text-box(100px);
}
.text-box-large {
  .text-box(200px);
}

LESS Mixins in Action
图 3 运行中的 LESS Mixin

Mixin 可以接受多个参数或可变数量的参数。 此外,每个参数都支持默认值:

.mixin(@color: #ff0) { ... }

LESS 并不是富编程语言表示,它的设计缺少命令来指示条件或循环。 但 mixin 的行为仍然可以根据所传递的值发生变化。 假设您要对一个较大的按钮使用较粗的边框和字体。 您定义一个名为 button 的参数化 mixin,使用关键字“when”将设置与条件绑定。 条件必须基于一个参数:

.button (@size) when (@size < 100px) {
 padding: 3px;
 font-size: 0.7em;
 width: @size *2;
}
.button (@size) when (@size >= 100px) {
  padding: 10px;
  font-size: 1.0em;
  font-weight: 700;
  background-color: red;
  width: @size *3;
}

您应用不同的设置,但也可以使用基本操作通过一个系数将大小扩大为数倍。 然后在实际 CSS 类中使用 mixin:

.push-button-large {
  .button(150px);
}
.push-button-small {
  .button(80px);
}

运行此代码的结果如图 4 所示。

Effects of Using LESS Mixins in CSS Classes
图 4 在 CSS 类中使用 LESS Mixin 的效果

LESS 附带很多可操作颜色的预定义函数。 您可以使用函数按百分比调整颜色亮度和饱和度,也可以按百分比设置颜色淡入和淡出,如下所示:

.push-button {
  background-color: fade(red, 30%);
}

有关 LESS 支持的函数的完整文档,请参阅 lesscss.org

嵌套类

我个人觉得需要重复 CSS 块指示同级样式非常讨厌。 下面是一个典型示例:

#container h1 { ... }
#container p { ... }
#container p a { ... }
#container img { ... }

在编写得很好的普通 CSS 中,实际上可以避免很多重复,但使用平面列表布局样式的方式并不是最好的。 在这种情况下,最好使用一些层次结构。 在 LESS 中,可以像这样嵌套样式规则:

.container {
  h1 {
    font-size: 0.8em;
   color: fade(#333, 30%);
   a {
     color: #345;
     &:hover {color: red;}
    }
  }
}

经过处理后,前面的 LESS 代码会产生下面的样式:

.container h1
.container h1 a
.container h1a:hover

服务器端处理

您可以原样下载 LESS 代码,在客户端通过 JavaScript 代码对其进行处理。 也可以在服务器上进行预处理,然后将它作为普通 CSS 下载到客户端。 在前一种情况下,完全像使用普通 CSS 文件一样:服务器端更改在下一次页面刷新时应用到客户端。

如果您注重性能,处理的是大型复杂 CSS 文件,则服务器端预处理可能是更好的选择。 服务器端预处理发生在您每次修改服务器上的 CSS 时。 您可以在生成过程结束时手动完成额外的步骤。 使用 LESS 编译器从命令行将 LESS 代码预处理为 CSS。 此编译器是为服务器端工作安装的 .less NuGet 软件包的一部分。

不过,在 ASP.NET MVC 4 中,可以将 LESS Framework 与捆绑机制集成,有关捆绑机制,请参阅我在 2013 年 10 月的专栏“CSS 编程:捆绑和缩小”(msdn.microsoft.com/magazine/dn451436)。 这确保只要请求 LESS 文件,就会执行 LESS 到 CSS 的转换。 还确保通过 If-Modified-Since 标头正确管理缓存。 最后,您可以将分析和缩小混合起来。 要在 ASP.NET MVC 中集成 LESS,首先需要下载并安装 .less NuGet 软件包。 然后将以下代码添加到 BundleConfig 类:

var lessBundle =
  new Bundle("~/myless").IncludeDirectory("~/content/less", "*.less");
lessBundle.Transforms.Add(new LessTransform());
lessBundle.Transforms.Add(new CssMinify());
bundles.Add(lessBundle);

捆绑将打包在指定文件夹中找到的所有 .less 文件。 LessTransform 类负责 LESS 到 CSS 的转换。 该类使用 .less API 分析 LESS 脚本。 LessTransform 的代码非常简单:

public class LessTransform : IBundleTransform
{
  public void Process(BundleContext context, BundleResponse response)
  {
    response.Content = dotless.Core.Less.Parse(response.Content);
    response.ContentType = "text/css";
  }
}

更多智能工具

LESS 不是唯一的 CSS 预处理器。 例如 Syntactically Awesome Stylesheets (Sass) 就是另一种常见的工具 (sass-lang.com)。 实际上,无论使用什么工具,CSS 预处理器都是广泛的 Web 编程需要考虑的工具。 无论是图形设计人员还是开发人员,都需要更多智能工具来管理和组织 CSS 代码。 最好同时将它们集成到 Web 平台。 最后请注意,Visual Studio 2012 和 Visual Studio 2013 通过 Web Essentials 扩展提供出色的 LESS(及相关技术)支持,Web Essentials 扩展可从 vswebessentials.com 下载。 此外,还可以从 Visual Studio 2012 Update 2 和即将发布的 Visual Studio 2013 中获得 LESS 编辑器。

Dino Esposito是《Architecting Mobile Solutions for the Enterprise》(Microsoft Press,2012 年)和 Microsoft Press 即将出版的《Programming ASP.NET MVC 5》的作者。作为 JetBrains 的 .NET 和 Android 平台的技术推广人员,Esposito 经常在全球行业活动中发表演讲,并在 software2cents.wordpress.com 上以及 twitter.com/despos 上的推文中分享他对于软件的愿景。

衷心感谢以下技术专家对本文的审阅:Mads Kristensen (Microsoft)
Mads Kristensen 是 Microsoft 的 Web 平台与工具团队程序经理,负责 Visual Studio 的 Web 开发人员体验。 他拥有十多年在 Microsoft 平台上开发 Web 应用程序的经验。 他创立的 BlogEngine.NET 开源项目成为 ASP.NET 平台上最流行的博客应用程序,在全球有 800,000 名用户。 Kristnsen 还是一些常用 Visual Studio 扩展的创建者,包括 Web Essentials、Image Optimizer 和 CssCop。