Miloer の 闲言碎语

读书笔记:高性能网站建设指南(2)

关于雅虎的14条军规。

关于个人对前端性能优化的认识。

主要记载学习笔记。

读书也并不意味着照本宣科,大部分内容都是根据书中理解然后进行知识拓展,也多聊一聊自己对前端性能的理解,所以理解难免有错,如果有错误,希望不吝指正。

规则5:将样式表放在顶部

HTML 标准要求把样式表和JS的内容都放在顶部。js内容暂且不说。
雅虎团队做了一些有趣的测试。他们为了让网页加载的更快,尝试放在文档底部。
但结果,却让用户感觉页面运行的更缓慢了。

逐步呈现

页面通常都是按顺序进行下载的,设计到底部的初衷是想让用户先看到最重要的组建内容,以及页面内容,最后在进行样式渲染。
但是将样式表放在文档底部却会导致浏览器阻止内容的逐步呈现。
当时我看到这句话,脑海中产生一副画面,一个小孩从上面扔东西,一个妇人拿个兜子去挨个接,直到没有力气拿不住了,然后把这些东西直接扔到地上。
在我想来最后的重物就是这个CSS文件了。

产生的影响
白屏

将CSS放到底部会产生短暂的白屏,因为逐步呈现被禁止了。
DEMO.
我记得前几天在segfaultment社区有人提白屏问题。但不是像这种“兜着”的白屏,我觉得他的问题是响应的内容的图片很大,还有些资源找不到。

无样式内容的闪烁

在页面中,文档为样式使用了一个CSS规则,但是却在最后的底部,又引入了一个CSS资源,有进行了页面重绘,所以造成了页面无样式内容的闪烁。
DEMO.

为什么造成这样

如果样式表仍在加载,构建呈现树就是一种浪费,因为在所有样式表加载并解析完毕之前无需绘制任何东西。否则就会遇到无样式内容闪烁的问题。 —DavidHyatt.

其实现在我们工作里,有进行页面加载进来后在利用JS进行位置重绘这样的问题,这样无形中就削弱了性能。

几个DEMO测试

可以对比一下放到底部和顶部的时间。
我这里其实相差不大的,甚至底部还要更快一些,但是白屏的时间让人体验太差了。
底部
顶部

关于import引入

about import.

@import 必须放在所有其他规则之前。
可能会出现一些样式引入了但是却没有加载进来让人挠头的问题。
使用@import会让之组建下载的无序性,因为使用@import引用的文件只有在引用它的那个css文件被下载、解析之后,浏览器才会知道还有另外一个css需要下载,这时才去下载,然后下载后开始解析、构建render tree等一系列操作。这种情况就类似于我刚说的,页面加载完后,利用JS去重绘样式,重绘位置。

规则6:将脚本放在底部

JS是一个很霸道的语言。JS运行当中,在解释器运行JS的时候,JS代码没完成之前,其他所有代码全部阻断。
在JS中有一个预处理过程,预处理完了才执行,预处理完了,JS代码从上到下的进行执行。这里涉及到一个变量提升的知识点。

文中尝试把JS放到中部,顶部,底部不同位置来测试产生的影响。

放在中部

DMEO

下载JS代码需要10S左右的时间,页面上部分(脚本以上)代码立即显示出来了,而下部分要花大约10S才能显示出来,这段时间页面在加载脚本。

逐步呈现

使用脚本时,对于所有位于脚本以下的内容,逐步呈现都被阻塞。将脚本放在页面越靠下的地方,意味着更多的内容能够逐步地呈现。

并行下载和脚本阻塞下载

并行下载意味着我们可以下载更多的组件(取决于你的宽带和CPU速度),但是下载脚本时并行下载还是被禁用了。一部分原因是因为可能你的JS代码有类似document.write的操作,另一个原因为了保证JS脚本是按特定顺序来加载到浏览器中的。(js代码中的互相依赖。)

文中还提到了defer 延时加载,但是在Firfox中延迟脚本也会阻塞呈现和并行加载。
关于defer 和async 多说两句,个人建议不要使用,就打defer而言,官方说HTML中脚本是按顺序执行,但是你可以尝试一下,在页面上使用defer 引入两个相关联的JS脚本, 第一个JS比第二个JS要大,有可能会导致顺序的错乱。

对比

顶部DEMO
底部DEMO

规则7:避免CSS表达式

CSS表达式,你用过吗?
CSS表达式是CSS属性由JS来进行设定的。

1
background-color:expression(newDate().getHours()%2?"#ccc":"#fff");

这里用expression方法来接受了一个JS表达式,CSS的属性被设置成为对JS表达式值得结果。

更新的频率过于频繁

CSS表达式的问题在于页面对其进行的操作频率非常高,当页面滚动甚至鼠标滑动时都要运行求职。影响了JS性能。

解决方式

解决该问题方式基本都使用JS来完成操作,文中还介绍了可以创建一次性表达来进行替换。

简单介绍一下一次性表达式:

1
2
3
4
5
6
7
8
9
10
11
<style>
p{
background-color:expression(BgColor(this))
}
</style>
<script>
function BgColor(elem){
elem.style.backgroundColor =(new Date()).getHours()%2?"#ccc":"#fff";
}
</script>

CSS表达式调用了BgColor函数,该函数设定了一个明确的值,并且移除了CSS表达式。
我感觉这就是一种隐性的替换。还不如直接就用JS来操作。
不过多琢磨几下,你们觉得用一次性表达式和用JS来渲染 那个对页面性能更友好呢?

规则8:使用外部Js和Css

平常开发中,如果能复用的我基本单例放一个文件里,不能的就内联写到页面里。来看看书里是怎么讲的。
其实我以前觉得有点规模公司的网站,CSS,JS应该很规范,抱着学习的角度去看看,有的还不及我写的呢~哈哈~~

内联VS 外置

来看看内联与外置的对比。

内联JS和CSS的示例.
外部JS和CSS的示例.

首次访问时,内联要快一些,二次访问浏览器加了缓存感觉2个速度相差不大。

书中也提到了内联比外部的快的原因因为需要承担多个HTTP请求,在第一条规则里已经提到了。

页面浏览量

用户访问的次数越少,内联的Js和Css更具优势。

空缓存与完整缓存

如果网站本质上能够为用户带来完整缓存率,使用外部文件的收益将更大。如果不大可能使用完整缓存,则内联是更好的选择。

组件重用

如果网站中使用了相同的样式和JS脚本,提高了组件的重用率。那么引入外部文件是一个更好的选择,因为用户在首页进行导航时,JS和CSS文件已经被缓存进来了。

两全其美

内联元素无法使用浏览器缓存,书里提供了两种方法,达到两全其美。

加载后下载

主页画面作为浏览口进行”拉客”,加载后,通过JS方法俩下载其他页面需要的CSS,JS方法。

动态内联

如果主页服务器直到一个组件是否在浏览器的缓存中,就可以在内联和外部之间做出最佳的选择。
书中通过判断cookie值的存在来做对应的选择。

1
cookie ? 加载外部组件 : 内联

我自己更倾向于第一种方式。

加载后下载的demo

动态内联demo

(未完待续)