性能与组织
当能够扎实的理解并编写HTML
和CSS
这门专业知识。随着网站代码量和流量的增长,另一种新技能也开始发挥作用,
这对于开发效率和用户体验都至关重要。我们在了解网站性能和组织的基础知识上还要继续努力。
代码库的组织和体系结构不仅极大的影响开发速度,还极大的影响页面呈现的速度。这两者对用户和开发人员来说都是个大问题。
我们需要花时间为代码库设计正确的结构,确定所有不同组件如何协同工作,加快开发效率并创造更好的用户体验。
此外,一些小技巧也可以改善网站的性能。网站性能类似于二八定律,其中20%的优化将带来网站80%的性能提升。
策略与结构
改善网站的性能和组织首先围绕如何构思良好的策略和结构的项目代码库结构。具体来说,就是构建良好的目录体系结构和设计模式,
并找出项目中重用的公共代码的方法。
样式的结构
什么样的组织架构才是最有效的呢? 一般来说,可以遵循以下方法。 这种做法包含集中化的样式分离,还包括根据公共基本样式、用户交互组件、业务逻辑模块。
# Base // 基本样式目录
- normalize.css // 常规的
- layout.css // 布局的
- typography.css // 排版的
# Components // 组件样式目录
- alerts.css
- buttons.css
- forms.css
- list.css
- nav.css
- tables.css
# Modules // 模块样式目录
- aside.css
- footer.css
- header.css
上面展示的体系结构包括三个目录,每个目录都具有独立的样式组。 我们的设计目标就是将网站是为一个系统而不是单个页面,
代码架构应该反映出这种形态。请注意这里没有涉及到任何页面的特定样式。
Base
目录包括了整个网站的布局和排版样式中使用的公共样式和变量。components
目录包括了特定用户界面元素的样式,
这些元素分为不同的组件文件,比如警告提示和按钮。最后,modules
目录包含页面不同部分的样式,这些样式由具体的业务需求来决定。
组件样式纯粹是接口驱动的,与网站的核心业务逻辑无关。然后,模块包括特定于业务逻辑的样式。在HTML
中标记模块时,通常在页面
使用不同的用户界面组件。例如,页面的侧边栏可能具有在组件样式中定义的列表和按钮样式,而侧边栏所需的其他样式则从模块样式继承。
样式的分离反映了程序员经过深思熟虑的设计和样式被共享和重用的能力。
以这种方式组织样式的策略并不是全新的。并且之前已经在不同的CSS
方法中提到过,包括面向对象的CSS
,OOCSS
以及用于CSS
的
可扩展性和模块化架构(SMACSS)
。这些方法对架构以及如何使用样式来说有着自己独特的方法。
面向对象的CSS
面向对象的CSS方法是由Nicole Sullivan
在为大型网站编写样式的工作中开创的。面向对象的CSS(`Object
Oriented CSS`)确定了两个基本原则,这些原则将有助于构建具有强大架构和合理代码量的可弹性网站。
这两个原则包括:
- 整体结构与皮肤的分离
- 从容器中分离内容
整体结构与皮肤的分离包括从网站的主题中抽象出元素的布局,模块的结构应该是透明的,允许继承和显示其他样式而不会发生冲突。
最常见的是这需要实体网格和布局结构,以及精心设计的模块。
将内容与容器分离涉及移除父元素嵌套子元素的依赖项。无论其父容器是什么,标题应该看起来都是一样的。为此,元素需要
继承默认样式,然后根据需要使用多个类进行扩展。
HTML
<div class="alert alert-error">
<p class="msg">......</p>
</div>
CSS
.alert {....}
.alert-error {...}
.msg {...}
面向对象的CSS
提倡构建组件库,保持灵活性,并使用网格。 这些都是很好的基本规则,它们可以帮你避免每次向网站添加新页面
和新功能时都需要添加额外的样式。
可扩展和模块化架构的CSS
与面向对象CSS相同的是Jonathan Snook开发的可伸缩的模块化CSS方法论体系结构。
CSS的可扩展和模块化架构促进将样式分成五个核心类别,包括:
- 基本
- 布局
- 模块
- 状态
- 主题
基本类别包括核心元素样式,涵盖了一般的默认值。
然后,布局类别确定不同元素的大小和网格样式,确定它们的布局。
模块样式是针对页面各个部分的更具体的样式,例如导航或特性样式。
然后,状态样式用于在模块包含备用状态(例如活动选项卡)的情况下增强或覆盖其他样式。
最后,可以添加主题类别,其中可以包括基于皮肤的样式,或者不同模块的外观和感觉。
HTML
<div class="alert is-error">
<p>...</p>
</div>
CSS
.alert {...}
.alert.is-error {...}
.alert p {...}
.alert.is-error p {}
在上面的例子中,alert类属于模块类,而is-error类属于状态类。然后根据需要来继承这些类别中的每个样式。
选择方案
选择使用那种方法(如果有的话)完全取决于你自己。对于一个给定的网站,你觉得最好的是什么。
一般来说,OOCSS和SMACSS的可靠组合工作会很好,你可以根据自己的喜好从每种方法中借鉴原则。
性能驱动的选择器
CSS中的大部分注意力都集中在属性和值上面,而CSS中的选择器经常被滥用却没有被意识到。
我们总以为将这些样式应用到正确的元素上就很完美了,但是这是一个不好的想法。
CSS中元素的选择也会影响性能,包括页面呈现的速度,以及样式再整个站点架构中的实用性和模块化程度。
保持简短的选择器
保持CSS选择器尽可能短有几个好处。 包括最小化特异性,允许更好的继承性和可移植性,以及提高效率。
长的、多度限定的选择器会降低性能,因为它们迫使浏览器从右到左呈现每个选择器类型。
更具体的说,它们还增加了所有其他选择器的负担。
/* Bad */
header nav ul li a {...}
/* Good */
.primary-link {...}
/* Bad */
button strong span {...}
button strong span .callout {....}
/* Good */
button span {...}
button .callout {...}
在上面的代码中,第一个选择器是非常具体的,不过我们可以通过使用类更快地识别和呈现。此外,
在这种情况下使用类大大减少了识别元素父元素的需要,允许元素位置随时间变化而不破坏任何样式。
第二个示例包含比第一个示例更短的选择器,但是可以通过为每个选择器提供相同级别的特异性来改进它。
避免使用过于特定的选择器,作为回报,如果元素的顺序发生变化,
它们不太可能中断。去掉一些单独的选择器单元,并赋予所有选择器相同的强度,使它们能够更好地协作。
使用短选择器的总体目标是降低特异性,创建更干净、更宽松的代码。
支持类
类非常棒,它们呈现速度很快,允许重用样式,并且已经广泛用于构建网站。但是,在使用类时,要观察一些常见的实践,
以确保它们得到了适当的利用。
因为选择器是从右到左呈现的,所以一定要注意键选择器。键选择器是最后最右边的选择器单元。键选择器非常重要,因为它标识了浏览器要查找的第一个元素。
如果键选择器不好,浏览器就会陷入徒劳。不要害怕仅仅为了性能的好处而使用一个类来变得更加独特。
此外,不要在类选择器前面加上元素前缀。这样做会阻碍将这些样式轻松应用于不同的元素,并增加选择器的总体特异性。
/* Bad */
#container header nav {...}
/* Good */
.primary-nav {...}
/* Bad */
article.feat-post {...}
/* Good */
.feat-post {...}
同样值得注意的是,尽可能远离ID选择器,因为它们过于具体,不允许任何重复。说到底,使用ID和使用!important没什么不同。
代码重用
最大的性能缺陷之一是文件大小过大和不必要的浏览器呈现。最大程度上减少CSS文件大小的快速方法就是尽可能多地重用样式。
应该组合任何重复的样式或接口模式,以允许共享代码。如果两个模块共享一个背景、圆角和一个方框阴影,则没有理由两次显式地声明相同的样式。
相反,它们可以组合在一个类中,允许只编写一次样式,然后共享。
重用代码也不需要以语义为代价。一种技术是将选择器组合在一起用逗号分隔他们,允许在两个选择器之间继承相同的样式。
在前面提到的OOCSS和SMACSS方法中经常看到的另一个方法包括将样式绑定到一个类,然后在同一个元素上使用多个类。
/* Bad */
.news {
background:#eee;
border-radius: 5px;
box-shadow:inset 0 1px 2px rgba(0, 0, 0, .25);
}
.social {
background:#eee;
border-radius: 5px;
box-shadow:inset 0 1px 2px rgba(0, 0, 0, .25);
}
.news,.social {
background:#eee;
border-radius:5px;
box-shadow:inset 0 1px 2px rgba(0, 0, 0,.25);
}
/* Even Better */
.model {
background: #eee;
border-radius:5px;
box-shawdow:inset 0 1px 2px rgba(0, 0, 0, .25);
}
只要代码是共享和重用的,并且总体文件大小减少了,那么采用哪种方法并没有太大的区别。
缩小和压缩文件
简单的删除重复和不必要的代码是减少文件大小的最佳方法,但是还有其他方法。一种方法包括缩小和压缩文件,如HTML、CSS、JavaScript文件。
此外,图像可能被压缩,删除任何不必要的注释和颜色配置文件。
gzip 压缩
一种比较流行的文件压缩类型称为gzip。gzip压缩采用常见文件,包括HTML、CSS、JavaScript等,并标识要压缩的类似字符串。识别的匹配字符串越多,
可以压缩的文件就越小,这样就可以将较小的文件从服务器发送到浏览器。
设置gzip相当简单的,HTML5模版团队已经做了很多工作来实现这一目标。要想压缩gzip文件,需要将.htaccess文件添加到web服务器的根目录中,并标记要压缩的特定文件。
文件名开头的点是正确的,因为.htaccess文件是一个隐藏文件。
在HTML5样板Apache服务器配置中,它们指示应该对哪些文件应用gzip压缩。请记住,此压缩的代码应该位于Web服务器根目录下的.htaccess文件中。
另外,值得注意的是.htaccess文件只在ApacheWeb服务器上工作,这些服务器需要启用以下模块。
- mod_setenvif.c
- mod_headers.c
- mod_deflate.c
- mod_filter.c
- mod_expires.c
- mod_rewrite.c
一般来说,这不是问题,有些Web服务器甚至可能为您设置压缩。毕竟,压缩文件也是Web服务器的最大利益所在。
测量压缩
在谷歌Chrome web浏览器中,web inspector提供了大量关于性能的数据,特别是在Network选项卡中。此外,还有一些网站可以帮助识别是否启用了gzip压缩。
“网络”选项卡标识浏览器中加载的每个文件,并显示文件大小和加载时间。请注意gzipping如何将文件大小减少了大约60%。
查看一个文件的请求头可以明确地确定浏览器支持哪种类型的压缩编码。在本例中,gzip、deflate和sdch都支持,如请求头中所述。
图像压缩
减少文本文件的大小是有帮助的,但是通过压缩图像的文件大小可以得到更好的结果。
一个网站上所有图片的总文件大小可以快速地加起来,压缩图片将大大有助于控制文件大小。
许多人害怕压缩会降低图像本身的质量,所以避免压缩图像。在大多数情况下,这是不正确的,图像可以以无损的方式压缩,允许从图像中删除不必要的颜色配置文件和注释,而不改变图像的质量。
有一些工具可以帮助压缩图像,其中最好的两个是ImageOptim for Mac和PNGGauntlet for Windows。这两种服务都压缩最常用的图像格式,特别是JPG和PNG文件。
还应该注意,通过高度和宽度属性在HTML中设置图像的尺寸确实有助于更快地呈现页面,为图像留出适当的空间。请理解,这些属性只用于确定图像的精确尺寸,而不是缩小图像。使用较大的图像,然后使用高度和宽度属性将其缩小,这是一种不好的做法,因为它加载的数据比需要的多。
<img src="ocean.jpg" height="440" width="660" alt="Oceanview">
减少HTTP请求
除了文件大小之外,网站发出的HTTP请求数量是最大的性能陷阱之一。每次向服务器发出请求,页面加载时间都会增加。有些请求必须在其他请求启动之前完成,太多的请求会使服务器膨胀。
合并类似的文件
减少HTTP请求数量的一种方法(可能也是最简单的方法)是像组合文件一样组合。具体地说,将所有CSS文件合并到一个中,并将所有JavaScript文件合并到一个中。组合这些文件,
然后对它们进行压缩,就会创建一个很小的HTTP请求。
<!-- Bad -->
<link href="css/reset.css" rel="stylesheet">
<link href="css/base.css" rel="stylesheet">
<link href="css/site.css" rel="stylesheet">
<!-- Good -->
<link href="css/styles.css" rel="stylesheet">
一般来说,网页的CSS应该加载在头文档的header开始处,而网页的javascript应该加载在结束处,就在结束body标记之前。
这些独特的位置的原因是,CSS可以在加载网站的其余部分时加载。另一方面,JavaScript一次只能呈现一个文件,
因此禁止加载任何其他文件。这里的一个要注意的是,在页面本身呈现完成之后,
JavaScript文件是异步加载的。另一个注意是,当需要JavaScript来帮助呈现页面时,比如HTML5 SHIV。
图像精灵
在CSS中拼接图像的做饭包括在多个元素之间使用一个背景图像。这里的目标是减少使用多个背景图像所做的HTTP请求的数量。
要创建一个精灵,可以将一些常用的背景图片排列成一张图片。然后使用CSS将sprite作为背景图像添加到元素中,
并使用background-position属性显示正确的背景图像。
想象一下背景图像在元素后面滑动,只是为了在给定的元素上显示正确的背景图像。例如,如果一个元素的宽度是16像素乘16像素高,
那么它只能暴露一个16像素乘16像素的背景图像,而其余的背景图像则被隐藏。
比如,我们的文本编辑器菜单的精灵,我们可以将多个图标通过ps进行拼接在一起使用。
想使用上面的图像精灵,可以使用图像精灵作为span元素的背景来创建菜单。
然后,使用类来更改图像精灵的背景位置,可以相应地显示不同的图标。
HTML
<ul>
<li><a href="#"><span class="bold">Bold Text</span></a></li>
<li><a href="#"><span class="italic">Italicize Text</span></a></li>
<li><a href="#"><span class="underline">Underline Text</span></a></li>
<li><a href="#"><span class="size">Size Text</span></a></li>
<li><a href="#"><span class="bullet">Bullet Text</span></a></li>
<li><a href="#"><span class="number">Number Text</span></a></li>
<li><a href="#"><span class="quote">Quote Text</span></a></li>
<li><a href="#"><span class="left">Left Align Text</span></a></li>
<li><a href="#"><span class="center">Center Align Text</span></a></li>
<li><a href="#"><span class="right">Right Align Text</span></a></li>
</ul>
CSS
ul {
margin: 0;
padding: 0;
}
li {
float: left;
list-style: none;
margin: 2px;
}
li a {
background: linear-gradient(#fff, #eee);
border: 1px solid #ccc;
border-radius: 3px;
display: block;
padding: 3px;
}
li a:hover {
border-color: #999;
}
li span {
background: url("sprite.png") 0 0 no-repeat;
color: transparent;
display: block;
font: 0/0 a;
height: 16px;
width: 16px;
}
.italic {
background-position: -16px 0;
}
.underline {
background-position: -32px 0;
}
.size {
background-position: -48px 0;
}
.bullet {
background-position: -64px 0;
}
.number {
background-position: -80px 0;
}
.quote {
background-position: -96px 0;
}
.left {
background-position: -112px 0;
}
.center {
background-position: -128px 0;
}
.right {
background-position: -144px 0;
}
图像数据URI
此外,可以通过数据URI直接将图像的编码数据包含在HTML和CSS中,而不需要使用spriting图像,
从而完全不需要HTTP请求。使用图像数据URI可以很好地处理小图像,这些图像可能永远不会改变,
并且HTML和CSS可以被大量缓存。然而,数据uri存在一些问题。它们很难更改和维护,因此必须生成另一种编码。
而且,它们在较老的浏览器中无法工作,特别是在Internet Explorer 7及以下的浏览器中。
如果使用数据uri有助于减少一些HTTP请求,并且HTML或CSS可以被大量缓存,那么好处往往大于风险。一些帮助生成数据uri的工具包括转换器和模式生成器。
但是要小心,一定要反复检查,以确保实际数据URI的权重小于实际图像。
HTML
<img height="100" width="660" alt="Rigged Pattern" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAAPUlEQVQYV2NkQAO6m73+X/bdxogujiIAU4RNMVwhuiQ6H6wQl3XI4oy4FMHcCJPHcDS6J2A2EqUQpJhohQAyIyYy0nBAGgAAAABJRU5ErkJggg==">
CSS
div {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAICAYAAADA+m62AAAAPUlEQVQYV2NkQAO6m73+X/bdxogujiIAU4RNMVwhuiQ6H6wQl3XI4oy4FMHcCJPHcDS6J2A2EqUQpJhohQAyIyYy0nBAGgAAAABJRU5ErkJggg==") repeat;
}
缓存公共文件
另一种帮助减少HTTP请求和更快地提供页面的方法是缓存常见文件。当页面第一次加载时,
可以缓存特定文件。现在浏览器在重复访问一段时间后不必再次请求相同的文件。
一段时间取决于您,这取决于您希望用户保留特定文件类型的时间长度。
与gzipping文件一样,可以在.htaccess文件中设置缓存文件的expires头。再次,HTML5 Boilerplate团队领先于我们。在他们的Apache Server Configs中,有一个专门用于设置expires头文件的文件。
图像,视频,Web字体和常见媒体类型通常缓存一个月,而CSS和JavaScript文件通常缓存一年。如果CSS或任何其他文件每年更改频率超过一次,则需要更改文件名,最好是版本化,以便加载。或者,可以将到期标题更改为更短的时间段。
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
将“access plus 1 year”的值更改为“access plus 1 week”更适合于每周更改但没有使用单独文件名控制版本的CSS和JavaScript文件。对于接受的expires头值,请参考mod_expires语法。