资源请求与缓存
资源请求与缓存
数据压缩与流式加载
HTTP 缓存
CDN
HTTP/2 服务端推送
使用HTTPS 安全传输
Font | 字体
Custom Web Fonts
我们首先回顾下浏览器是如何使用自定义字体的,当浏览器识别到用户在@font-size
定义的字体时,会尝试下载该字体文件。而在下载的过程中,浏览器是不会展示该字体所属的文本内容,最终导致了所谓的Flash of Invisible Text
现象。现在很多的网站都存在这个问题,这也是导致用户体验差的一个重要原因,即会影响用户最主要的内容浏览这一操作。而我们的优化点即在于首先将字体设置为默认字体,而后在自定义的

首先,我们会为需要使用到的
html {font-family: Georgia, serif;}
html.fonts-loaded {font-family: Noto, Georgia, serif;}
不过现在font-display
属性也原生提供了我们这种替换功能,更多详情可见font-display属性。
服务端与缓存
高性能的前端离不开服务端的支持,在我们的实践中也发现不同的服务端配置同样会影响到前端的性能。目前我们主要使用
Configuration
我们首先对于合适的服务端配置做了些调研,这里推荐是使用H5BP Boilerplate Apache Configuration作为配置模板,它是个不错的兼顾了性能与安全性的配置建议。同样地它也提供了面向其他服务端环境的配置。我们对于大部分的
HTTPS
使用
- 设置
HTTP Strict Transport Security 请求头可以让服务端告诉浏览器其只允许通过HTTPS 进行交互,这就避免了浏览器从HTTP 再重定向到HTTPS 的时间消耗。 - 设置
TLS false start 允许客户端在第一轮TLS 中就能够立刻传递加密数据。握手协议余下的操作,譬如确认没有人进行中间人监听可以同步进行,这一点也能节约部分时间。 - 设置
TLS Session Resumption ,当浏览器与服务端曾经通过TLS 进行过通信,那么浏览器会自动记录下Session Identifier ,当下次需要重新建立连接的时候,其可以复用该Identifier ,从而解决了一轮的时间。
这里推荐扩展阅读下Mythbusting HTTPS: Squashing security’s urban legends by Emily Stark。
Cookies
我们并没有使用某个服务端框架,而是直接使用了静态的
<!-- #if expr="($HTTP_COOKIE!=/css-loaded/) || ($HTTP_COOKIE=/.*css-loaded=([^;]+);?.*/ && ${1} != '0d82f.css' )"-->
<noscript><link rel="stylesheet" href="0d82f.css"></noscript>
<script>
(function() {
function loadCSS(url) {...}
function onloadCSS(stylesheet, callback) {...}
function setCookie(name, value, expInDays) {...}
var stylesheet = loadCSS('0d82f.css');
onloadCSS(stylesheet, function() {
setCookie('css-loaded', '0d82f', 100);
});
}());
</script>
<style>/* Critical CSS here */</style>
<!-- #else -->
<link rel="stylesheet" href="0d82f.css">
<!-- #endif -->
这里<!-- #
,其主要包含以下步骤
$HTTP_COOKIE!=/css-loaded/
检测是否有设置过CSS 缓存相关的Cookie $HTTP_COOKIE=/.*css-loaded=([^;]+);?.*/ && ${1} != '0d82f.css'
检测缓存的CSS 版本是否为当前版本If <!-- #if expr="..." -->
值为 true
我们便能假设该用户是第一次访问该站点- 如果用户是首次浏览,我们添加了一个
<noscript>
标签,里面还包含了一个阻塞型的<link rel="stylesheet">
标签。添加该标签的意义在于我们在下面是使用JavaScript 来异步加载CSS 文件,而在用户禁止JavaScript 的情况下也能保证可以通过该标签来正常加载CSS 文件。 <!-- #else -->
表达式在用户二次访问该页面时,我们可以认为CSS 文件已经被加载过了,因此可以直接从本地缓存中加载而不需要重复请求。
上述策略同样可以应用于

File Level Caching
在上文可以发现,我们严重依赖于浏览器缓存来处理用户重复访问时资源加载的问题,理想情况下我们肯定希望能够永久地缓存https://www.voorhoede.nl/assets/css/main.css?v=1.0.4
形式,即在请求路径上加上版本号的方式进行缓存。不过这种方式的缺陷在于如果我们更换了资源文件的存放地址,那么所有的缓存也就自然失效了。这里我们使用了gulp-rev以及gulp-rev-replace来为文件添加
Result
上面我们介绍了很多的优化手段,这里我们以实验的形式来对优化的结果与效果进行分析。我们可以用类似于PageSpeed Insights或者WebPagetest来进行性能测试或者网络分析。我觉得最好的测试你站点渲染性能的方式就是在限流的情况下观察页面的呈现效果,



Roadmap
优化之路漫漫,永无止境,我们在未来也会关注以下几个方面:
HTTP/2: 我们目前已经开始尝试使用HTTP/2 ,而本篇文章中提到的很多的优化的要点都是面向HTTP/1.1 的。简言之,HTTP/1.1 诞生之初还是处于Table 布局与行内样式流行的时代,它并没有考虑到现在所面对的2.6MB 大小,包含200 多个网络请求的页面。为了弥合这老的协议的缺陷,我们不得不连接JS 与CSS 文件、使用行内样式、对于小图片使用Data URL 等等。这些操作都是为了节约请求次数,而HTTP/2 中允许在同一个TCP 请求中进行多个并发的请求,这样就会允许我们不需要再去进行大量的文件合并操作。Service Workers: 这是现代浏览器提供的后台工作线程,可以允许我们为网站添加譬如离线支持、推送消息、后台同步等等很多复杂的操作。CDN: 目前我们是自己维护网站,而在真实的应用场景下可以考虑使用CDN 服务来减少服务端与客户端之间的物理距离,从而减少传输时延。
- 最多三秒钟渲染完成单屏或者使用
Loading - 基于
3G/4G 移动网络下,每屏幕资源不超过1024KB
加载优化
- 合并
CSS 、JavaScript - 合并小图片、使用雪碧图
- 缓存一切可以缓存的资源,部分资源
css 、js 使用src="abc.css?cacheVersion=1"
来控制版本 - 使用长
Cache - 压缩
HTML 、CSS、JS - 启用
GZip
- 使用首屏加载
- 使用按需加载
- 使用滚屏加载
- 增加进度指示器
- 减少
Cookie
- 避免重定向
- 异步加载第三方资源
CSS 优化
CSS 写在头部,JS 写到尾部或者异步- 避免图片和
iFrame
等的SRC 为空 - 尽量避免重设图片大小
- 图片尽量避免使用
DataURL
- 尽量避免在
HTML 标签中写Style - 避免
CSS 表达式 - 移除空的
CSS 规则 - 正确使用
Display 的属性 - 不滥用
Float
- 不滥用
Web 字体 - 不声明过多的
Font-size
- 值为
0 时候不需要任何单位 - 标准化各种浏览器的前缀
- 避免让选择符看起来像正则表达式
图片优化
- 使用
CSS3、SVG、IconFont
代替图片 - 使用
Srcset WebP 优于JPG PNG8 优于GIF - 首次加载不大于
1024KB 单页 - 图片不宽于
640
脚本优化
- 减少重绘
- 缓存
Dom 选择与计算 - 缓存列表的长度
- 尽量使用事件代理,避免批量绑定事件
- 尽量使用
ID 选择器 - 使用
touch 代理click
渲染优化
HTML 使用ViewPort - 减少
Dom 节点 - 尽量使用
CSS3 动画 - 合理使用
requestAnimationFrame
动画代替setTimeout - 适当使用
Canvas 动画 touchmove,scroll
事件会导致多次渲染- 使用
CSS3-transitions、CSS3-3D、Opacity、Canvas、WebGL、Video
来触发GPU 渲染
本文从属于笔者的
Web 前端入门与最佳实践中前端性能优化系列,同时也归纳于笔者的我的校招准备之路: 从Web 前端到服务端应用架构这篇综述。
前端优化的根本目的是为了有一个更好地用户体验的同时尽可能减少后端负载压力。即保证更少的加载时间、更快的首屏渲染、更流畅的用户交互。在笔者自己的知识体系内,当我们想为用户呈现更好的视觉效果与用户体验时,我们往往会从性能评测与监控、资源与请求优化、加载策略、首页与关键路径、渲染优化这几个方面进行考虑。
- 让
web app 更快的HTML5 最佳实践 - 高性能
JavaScript DOM 编程 - 移动
Web 开发规范摘录 - 百度移动端首页前端速度那些事儿
- 京东凹凸实验室
: 前端优化实践总结 - Google Developers:优化性能
编码与压缩
Image Optimization: 图片使用与显示优化
- 前端图片引入方式神演算
- http://www.creativebloq.com/features/10-ways-to-optimise-images-for-better-performance
- you-need-to-stop-making-these-6-mistakes-with-your-img-s
- responsive-images-css
WebP
HTTP Cache
静态网站非常简单,它就是通过一个
CDN
多域名资源存放
- 静态内容和动态内容分服务器存放,使用不同的服务器处理请求。处理动态内容的只处理动态内容,不处理别的,提高效率,这样使得
CDN( 内容分发网络) 缓存更方便
2、突破浏览器并发限制
3、跨域不会传
关于多域名,也不是越多越好,虽然服务器端可以做泛解释,浏览器做
Static Content: 网站静态化
动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路。由此可见,网站静态化处理的核心就是动静分离和缓存两大方面,上篇我简单讲述了动静整合的基础知识,本篇将会讲述两大核心之一的动静分离策略,只有把动静分离策略做好了,缓存才能发挥出它应有的效果。
请求优化
Throttling: 限流
function ajaxThrottle(url, params, timeout) {
// 这里的缓存暂时挂载在 window 命名空间下
if (!window.ajaxThrottleContext) {
window.ajaxThrottleContext = {};
}
let token = url + JSON.stringify(params);
let lastAjaxTime = window.ajaxThrottleContext[token];
// 判断是否已经过时
if (lastAjaxTime && Date.now() - lastAjaxTime < timeout) {
// console.log(`Request Abort: ${token}`);
return false;
}
window.ajaxThrottleContext[token] = Date.now();
return true;
}
资源预抓取
譬如 instant.page 能够利用即时预加载
const prefetcher = document.createElement("link");
// ...
function preload(url) {
prefetcher.href = url;
}
function touchstartListener(event) {
const linkElement = event.target.closest("a");
// ...
linkElement.addEventListener("touchcancel", touchendAndTouchcancelListener, {
passive: true,
});
linkElement.addEventListener("touchend", touchendAndTouchcancelListener, {
passive: true,
});
urlToPreload = linkElement.href;
preload(linkElement.href);
}