本项目是网站性能优化项目,来自优达学城Udacity的网站性能优化课程。项目的目的是运用优化关键渲染路径并使网页尽可能快的渲染。
1.1 下载本项目的未优化版本到本地电脑。
1.2 在Github上创建一个新的远程仓库,
1.3 使用git命令行将本项目本项目(本地仓库)与Github上的新的远程仓库关联,并同步到远程仓库。
-
优化 index.html 的 PageSpeed Insights 得分,index.html 在移动设备和桌面上的 PageSpeed 分数至少为90分。
-
对 views/js/main.js 进行的优化可使 views/pizza.html 在滚动时保持 60fps 的帧速。
-
对 views/js/main.js 进行的优化可使pizza.html 页面上的 pizza 尺寸滑块调整 pizza 大小的时间小于5毫秒,大小的调整时间在浏览器开发工具中显示。
3.1 在PageSpeed Insights上测得的分数为:72分/移动端,85分/桌面端。此结果是使用 ngrok 搭建本地服务器,通过PageSpeed Insights远程访问得到的。也可以使用GitHub上的项目页面来测试。
3.2 根据PageSpeed Insights的推荐优化策略,我们采用以下优化策略:
-
清除首屏内容中阻止呈现的 JavaScript 和 CSS
-
优化图片
-
启用压缩
3.3 具体方法:
-
删除https://fonts.googleapis.com/css?family=Open+Sans:400,700。使用谷歌字体会网页性能
-
在所有<script>标记里都加上async,避免脚本阻止DOM的构建
-
print.css和style.css较小,将print.css和style.css的内容内嵌在index.html中。
-
使用 图片优化工具,压缩图片。
-
使用 Grunt压缩perfmatters.js。扩展阅读:前端js和css的压缩合并之grunt
3.4 优化结果:
-
移动端:93分
-
桌面端:95分
4.1 使pizza.html 在滚动时的帧速达到60帧/秒
在开发者工具的console上可以看到,滚动时背景滑窗披萨移动的每10帧的平均帧率大概在30-40ms间
我们使用chrome开发者工具的Performance对页面事件(滚动页面)进行记录,记录结果如下:

从上图可以看出,页面出现了强制同步布局。是由main.js中的函数updatePositions()中代码导致的。为避免文章篇幅过长,具体原因就不详细分析,直接列出修改结果。
function updatePositions() {
frame++;
window.performance.mark("mark_start_frame");
var items = document.querySelectorAll('.mover');
for (var i = 0; i < items.length; i++) { //这里导致强制同步布局
var phase = Math.sin((document.body.scrollTop / 1250) + (i % 5));
items[i].style.left = items[i].basicLeft + 100 * phase + 'px';
}
// 以下代码省略
...
}
}function updatePositions() {
frame++;
window.performance.mark("mark_start_frame");
function render(){
var items = document.querySelectorAll('.mover');
var top = document.body.scrollTop / 1250;
for (var i = 0; i < items.length; i++) {
var phase = Math.sin(top+ (i % 5));
//items[i].style.left = items[i].basicLeft + 100 * phase + 'px';
var left = -items[i].basicLeft + 1000 * phase + 'px';
items[i].style.transform = "translateX("+left+") translateZ(0)";
}
}
window.requestAnimationFrame(render);
//以下代码省略
...
}
}在开发者工具的console上可以看到,滚动时背景滑窗披萨移动的每10帧的平均帧率保持在0.01ms到0.03ms之间,明显快了很多。
4.2 使pizza.html 页面上的 pizza 尺寸滑块调整 pizza 大小的时间小于5毫秒
在优化前,console记录结果如下:
在优化后,console记录结果如下:
将.randomPizzaContainer节点保存在一个变量randomPizzas里,不用每次都查询DOM。因为调整披萨大小不同的选项对应的披萨尺寸是确定的,直接将调整后的值赋予所有randomPizzaContainer元素的width。不需要循环读和写randomPizzaContainer的width属性,这样会导致强制同步布局。
function determineDx (elem, size) {
var oldWidth = elem.offsetWidth;
var windowWidth = document.querySelector("#randomPizzas").offsetWidth;
var oldSize = oldWidth / windowWidth;
function sizeSwitcher (size) {
switch(size) {
case "1":
return 0.25;
case "2":
return 0.3333;
case "3":
return 0.5;
default:
console.log("bug in sizeSwitcher");
}
}
var newSize = sizeSwitcher(size);
var dx = (newSize - oldSize) * windowWidth;
return dx;
}
function changePizzaSizes(size) {
for (var i = 0; i < document.querySelectorAll(".randomPizzaContainer").length; i++) {
var dx = determineDx(document.querySelectorAll(".randomPizzaContainer")[i], size);
var newwidth = (document.querySelectorAll(".randomPizzaContainer")[i].offsetWidth + dx) + 'px';
document.querySelectorAll(".randomPizzaContainer")[i].style.width = newwidth;
}
}function changePizzaSizes(size) {
switch(size){
case "1":
newWidth = 25;
break;
case "2":
newWidth = 33.3;
break;
case "3":
newWidth = 50;
break;
default:
console.log('bug in sizeSwitcher');
}
var randomPizzas = document.querySelectorAll(".randomPizzaContainer");
for (var i = 0; i < randomPizzas.length; i++) {
randomPizzas[i].style.width = newWidth + "%";
}
}使用requestAnimationFrame()刷新动画
function updatePositions() {
frame++;
window.performance.mark("mark_start_frame");
var items = document.querySelectorAll('.mover');
for (var i = 0; i < items.length; i++) {
var phase = Math.sin((document.body.scrollTop / 1250) + (i % 5));
items[i].style.left = items[i].basicLeft + 100 * phase + 'px';
}
window.addEventListener('scroll', updatePositions);function updatePositions() {
frame++;
window.performance.mark("mark_start_frame");
function render(){
var items = document.querySelectorAll('.mover');
var top = document.body.scrollTop / 1250;
for (var i = 0; i < items.length; i++) {
var phase = Math.sin(top+ (i % 5));
//items[i].style.left = items[i].basicLeft + 100 * phase + 'px';
var left = -items[i].basicLeft + 1000 * phase + 'px';
items[i].style.transform = "translateX("+left+") translateZ(0)";
}
}
window.requestAnimationFrame(render);
window.addEventListener('scroll', updatePositions);减少披萨背景图标的数量
document.addEventListener('DOMContentLoaded', function() {
var cols = 8;
var s = 256;
for (var i = 0; i < 200; i++) {
var elem = document.createElement('img');
elem.className = 'mover';
elem.src = "images/pizza.png";
elem.style.height = "100px";
elem.style.width = "73.333px";
elem.basicLeft = (i % cols) * s;
elem.style.top = (Math.floor(i / cols) * s) + 'px';
document.querySelector("#movingPizzas1").appendChild(elem);
}
updatePositions();
});document.addEventListener('DOMContentLoaded', function() {
var cols = 8;
var s = 256;
for (var i = 0; i < 31; i++) {
var elem = document.createElement('img');
elem.className = 'mover';
elem.src = "images/pizza.png";
elem.style.height = "100px";
elem.style.width = "73.333px";
elem.basicLeft = (i % cols) * s;
elem.style.top = (Math.floor(i / cols) * s) + 'px';
document.getElementById("movingPizzas1").appendChild(elem);
}
updatePositions();
});



