>  기사  >  PHP 웹사이트 성능 최적화 연습: 타오바오 홈페이지 로딩 속도 최적화 연습

PHP 웹사이트 성능 최적화 연습: 타오바오 홈페이지 로딩 속도 최적화 연습

PHPz
PHPz원래의
2017-03-18 10:15:375138검색

많은 분들이 Taobao 홈페이지의 새 버전을 보셨을 거라 생각합니다. 이 버전의 페이지는 고유한 개인화 요구 사항으로 인해 다양한 기술적 문제에 직면해 있습니다. :

·

여러 데이터 소스

·

모듈 렌더링을 위한 직렬 요청

·

작업 데이터 및 개인화 데이터 매칭 및 관리

·

데이터 재해 복구이번 타오바오 홈페이지 개정판은 IE6, IE7 등 낮은 버전의 앤티크 브라우저를 더 이상 지원하지 않지만, 브라우저 , 하지만 여전히 홈페이지 성능에 영향을 미치는 요소는 많습니다:

<p></p>
  • 시스템에 너무 많이 의존

    , 데이터 요청은 세 부분으로 나누어져 있습니다. 정적 리소스(예: js/css/image/iconfont 등)이고, 두 번째는 CDN에 푸시된 정적 데이터(예: 작업으로 채워진 데이터, 프런트엔드 구성 정보 등)입니다. 백엔드 인터페이스, 다양한 모듈이 다양한 비즈니스에 대응하고, 페이지에 광고 콘텐츠가 많이 있습니다. 페이지를 처음 스크롤할 때 첫 번째 화면에서 전송된 인터페이스 요청이 대략 8개 있는 것으로 추정됩니다. 하단에는 20개 이상의 요청이 발행되었습니다.

  • 첫 번째 화면의 데이터는 직접 출력할 수 없습니다

    . 첫 번째 화면의 많은 데이터는 비동기 요청을 통해 얻어집니다. 이러한 요청은 불가피합니다. 요청 횟수가 상대적으로 많아 첫 화면 시간에 큰 영향을 미칩니다.

  • 모듈이 너무 많습니다

    . 백그라운드 격리 작업 간 데이터 권한을 채우려면 모듈을 세밀하게 분할해야 합니다. , 아래 그림과 같이:
    간단한 모듈은 여러 개의 소규모 산업 모듈로 분할되어야 하며 페이지의 다른 위치에서도 마찬가지이며 이러한 분할 모듈이 반드시 표시되지 않을 수도 있습니다. , 어떤 모듈을 표시하려면 알고리즘을 프런트엔드에 알려야 합니다. PHP 웹사이트 성능 최적화 연습: 타오바오 홈페이지 로딩 속도 최적화 연습

  • 사진이 너무 많아요

    페이지를 넘기면 페이지 전체를 가득 채울 사진이 눈에 띕니다. 작업에 따라 채워지고 일부 그림은 작업에 따라 채워집니다. 개인화된 인터페이스가 제공되며 이러한 그림에는 고정된 크기가 없습니다.

  • 웹페이지 성능 측정 지표

웹페이지 성능 측정 지표는 많습니다. 핵심을 파악하고 최적화에 집중하면 성능은 자연스럽게 높아집니다.

FPS

페이지 성능을 가장 잘 반영하는 지표는 FPS(초당 프레임)입니다. 일반적으로 시스템은 페이지 요소가 애니메이션, 스크롤 또는 그라데이션일 때 화면 새로 고침 빈도를 60fps로 설정합니다. 속도가 60 미만이면 부드럽지 않고, 24 미만이면 막히며, 12 미만이면 기본적으로 막힌 것으로 간주됩니다.

한 프레임의 지속 시간은 약 16ms입니다. 시스템 컨텍스트 전환 오버헤드를 제외하면 각 프레임에서 스크립트 처리 시간이 10ms를 초과하는 경우 약 10ms만 남습니다. 처리 시간이 26ms를 초과하면 두 개의 연속 프레임이 손실된 것으로 간주될 수 있습니다. 우리는 페이지에서 5~6개의 프레임이 여러 번 연속적으로 손실되는 것을 용납할 수 없습니다. 이는 실행하는 데 80ms 이상 걸리는 코드 프로그램을 분할하는 방법을 찾아야 함을 의미합니다.

페이지가 처음 로드되면 많은 프로그램을 초기화해야 하고 시간이 많이 걸리는 DOM 작업도 많이 있을 수 있으므로 처음 1초에 필요한 작업으로 인해 프레임 속도가 매우 낮아집니다. , 무시할 수 있습니다. 물론 PC용이기 때문에 모바일 콘텐츠의 양이 적고, PC 1에 비해 DOM과 JS 스크립트의 양이 훨씬 적어 조금 길 수도 있습니다.

DOMContentLoaded 및 Load

DOMContentLoaded 이벤트는 DOM이 로드되고 구문 분석된 후에만 트리거됩니다. 소스 코드가 너무 많은 콘텐츠를 출력하는 경우 클라이언트가 DOM을 구문 분석하는 데 걸리는 시간입니다. 파싱 ​​시간도 길어집니다. DOM 수가 2000개 증가하고 중첩 수준이 깊어지면 파싱 시간도 50~200ms 정도 늘어나므로 대부분의 페이지에서는 필요하지 않습니다. 첫 번째 화면 출력과 후속 콘텐츠를 보장합니다. 후크만 유지하고 동적 렌더링에는 JS를 사용합니다.

첫 번째 화면이 로딩되는 동안 클라이언트가 받은 정보의 총량을 측정하는 데 로드 시간을 사용할 수 있습니다. 백엔드를 사용하면 그에 따라 로드 시간도 길어집니다.

Fluency

Fluency는 FPS의 시각적 피드백입니다. FPS 값이 높을수록 시각적 표현이 더 매끄러워집니다. 페이지 로딩 속도를 보장하기 위해 페이지가 열릴 때 많은 콘텐츠가 클라이언트에 완전히 로드되지 않습니다. 여기서 말하는 부드러움은 대기 과정에서의 시각적 버퍼링입니다. 다음은 Google Plus 페이지의 렌더링입니다.

<p></p>

Google Plus Item벽 내에서 Google에 액세스하는 속도는 그렇지 않습니다. 위 요소의 내용 중 많은 부분이 비동기적으로 로드되는데, 위 그림을 보면 구글이 사용자에게 기다림에 대한 불안감을 주지 않는다는 것을 알 수 있습니다.

淘宝首页的性能优化

由于平台限制,淘宝首页面临一个先天的性能缺陷,首屏的渲染需要从 7 个不同的后端取数据,这些数据请求是难以合并的,如果用户屏幕比较大,则首屏的面积也比较大,对应的后端平台数据接口就更多。数据是个性化内容或者为广告内容,故请求也不能缓存。

关键模块优先

不论用户首屏的面积有多大,保证关键模块优先加载。下面代码片段是初始化所有模块的核心部分:


$('.J_Module').each(function(mod) {  var $mod = $(mod);  var name = $mod.attr('tms');  var data = $mod.attr('tms-data');  if($mod.hasClass('tb-pass')) {
    Reporter.send({
      msg: "跳过模块 " + name
    });    return;
  }  // 保证首屏模块先加载
  if (/promo|tmall|tanx|notice|member/.test(name)) {
    window.requestNextAnimationFrame(function(){      // 最后一个参数为 Force, 强制渲染, 不懒加载处理
      new Loader($mod, data, /tanx/.test(name));
    });
  } else {    // 剩下的模块进入懒加载队列    lazyQueue.push({
      $mod: $mod,
      data: data,
      force: /fixedtool|decorations|bubble/.test(name)
    });
  }
});

TMS 输出的模块都会包含一个 .J_Module 钩子,并且会预先加载 js 和 css 文件。

对于无 JS 内容的模块,会预先打上 tb-pass 的标记,初始化的时候跳过此模块;对于首屏模块关键模块,会直接进入懒加载监控:

// $box 进入浏览器视窗后渲染// new Loader($box, data) ->datalazyload.addCallback($box, function() {
  self.loadModule($box, data);
});// $box 立即渲染// new Loader($box, data, true) ->self.loadModule($box, data);

除必须立即加载的模块外,关键模块被加到懒加载监控,原因是,部分用户进入页面就可能急速往下拖拽页面,此时,没必要渲染这些首屏模块。

非关键模块统一送到 lazyQueue 队列,没有基于将非关键模块加入到懒加载监控,这里有两个原因:

  • 一旦加入监控,程序滚动就需要对每个模块做计算判断,模块太多,这里可能存在性能损失

  • 如果关键模块还没有加载好,非关键模块进入视窗就会开始渲染,这势必会影响关键模块的渲染

那么,什么时候开始加载非关键模块呢?

var lazyLoaded = false;function runLazyQueue() {  if(lazyLoaded) {    return;
  }
  lazyLoaded = true;
  $(window).detach("mousemove scroll mousedown touchstart touchmove keydown resize onload", runLazyQueue);  var module;  while (module = lazyQueue.shift()) {    ~function(m){      // 保证在浏览器空闲时间处理 JS 程序, 保证不阻塞
      window.requestNextAnimationFrame(function() {        new Loader(m.$mod, m.data, m.force);
      });
    }(module);
  }
}
$(window).on("mousemove scroll mousedown touchstart touchmove keydown resize onload", runLazyQueue);// 担心未触发 onload 事件, 5s 之后执行懒加载队列window.requestNextAnimationFrame(function() {
  runLazyQueue();
}, 5E3);

上面的代码应该十分清晰,两种请求下会开始将非关键模块加入懒加载监控:

  • 当页面中触发 mousemove scroll mousedown touchstart touchmove keydown resize onload 这些事件的时候,说明用户开始与页面交互了,程序必须开始加载。

  • 如果用户没有交互,但是页面已经 onload 了,程序当然不能浪费这个绝佳的空档机会,趁机加载内容;经测试,部分情况下,onload 事件没有触发(原因尚不知),所以还设定了一个超时加载,5s 之后,不论页面加载情况如何,都会将剩下的非关键模块加入到懒加载监控。

懒执行,有交互才执行

如果说上面的优化叫做懒加载,那么这里的优化可以称之为懒执行。

首页上有几个模块是包含交互的,如头条区域的 tab ,便民服务的浮层和主题市场的浮层,部分用户进入页面可能根本不会使用这些功能,所以程序上并没有对这些模块做彻底的初始化,而是等到用户 hover 到这个模块上再执行全部逻辑。

更懒的执行,刷新页面才执行

首屏中有两个次要请求,一个是主题市场的 hot 标,将用户最常逛的三个类目打标;第二个是个人中心的背景,不同的城市会展示不同的背景图片,这里需要请求拿到城市信息。

这两处的渲染策略都是,在程序的 idle(空闲)时期,或者 window.onload 十秒之后去请求,然后将请求的结果缓存到本地,当用户第二次访问淘宝首页时能够看到效果。这是一种更懒的执行,用户刷新页面才看得到.这种优化是产品能够接受,也是技术上合理的优化手段。

图片尺寸的控制和懒加载

不论图片链接的来源是运营填写还是接口输出,都难以保证图片具备恰当的宽高,加上如今 retina 的屏幕越来越多,对于这种用户也要提供优质的视觉体验,图片这块的处理并不轻松。

<img  src=&#39;//g.alicdn.com/s.gif&#39; src=&#39;//g.alicdn.com/real/path/to/img.png&#39; / alt="PHP 웹사이트 성능 최적화 연습: 타오바오 홈페이지 로딩 속도 최적화 연습" >

阿里 CDN 是支持对图片尺寸做压缩处理的,如下图为 200×200 尺寸的图片:

加上 _100x100.jpg 的参数后,会变成小尺寸:

我们知道 webp 格式的图片比对应的 jpg 要小三分之一,如上图加上 _.webp 参数后:

视觉效果并没有什么折扣,但是图片体积缩小了三分之一,图片越大,节省的越明显。显然,淘宝首页的所有图片都做了如上的限制,针对坑位大小对图片做压缩处理,只是这里需要注意的是,运营填写的图片可能已经是压缩过的,如:

$img = '//g.alicdn.com/real/path/to/img.png_400x400.jpg';<img  alt="PHP 웹사이트 성능 최적화 연습: 타오바오 홈페이지 로딩 속도 최적화 연습" >

上面这种情况,图片是不会正确展示的。首页对所有的图片的懒加载都做了统一的函数处理:

src = src.replace(/\s/g, '');var arr;if (/(_\d{2,}x\d{2,}\w*?\.(?:jpg|png)){2,}/.test(src) && src.indexOf('_!!') == -1) {
  arr = src.split('_');  if (arr[arr.length - 1] == '.webp') {
    src = [arr[0], arr[arr.length - 2], arr[arr.length - 1]].join('_');
  } else {
    src = [arr[0], arr[arr.length - 1]].join('_');
  }
}if (src.indexOf('_!!') > -1) {
  src = src.replace(/((_\d{2,}x\d{2,}[\w\d]*?|_co0)\.(jpg|png))+/, '$1');
}
WebP.isSupport(function(isSupportWebp) {  // https 协议访问存在问题 IE8,去 schema
  if (/^http:/.test(src)) {
    src = src.slice(5);
  }  // 支持 webp 格式,并且 host 以 taobaocdn 和 alicdn 结尾,并且不是 s.gif 图片
  if (isSupportWebp && /(taobaocdn|alicdn)\.com/.test(src) && (src.indexOf('.jpg') ||
    src.indexOf('.png')) && !/webp/.test(src) && !ignoreWebP && !/\/s\.gif$/.test(src)) {
    src += '_.webp';
  }
  $img.attr('src', src);
});

模块去钩子,走配置

TMS 的模块在输出的时候会将数据的 id 放在钩子上:

<p></p>

如果模块是异步展示的,可以通过 tms-datakey 找到模块数据,而首页的个性化是从几十上百个模块中通过算法选出几个,如果把这些模块钩子全部输出来,虽说取数据方便了很多,却存在大量的冗余,对此的优化策略是:将数据格式相同的模块单独拿出来,新建页面作为数据页。所以可以在源码中看到好几段这样的配置信息:

<textarea>[{"backup":"false","baseid":"1","mid":"222726","name":"iFashion","per":"false","tid":"3","uid":"1000"},{"backup":"false","baseid":"3","mid":"222728","name":"美妆秀","per":"false","tid":"3","uid":"1001"},{"backup":"false","baseid":"4","mid":"222729","name":"爱逛街","per":"false","tid":"4","uid":"1002"},{"backup":"false","baseid":"2","mid":"222727","name":"全球购","per":"false","tid":"4","uid":"1003"}]</textarea>

减少了大量的源码以及对 DOM 的解析。

低频修改模块,缓存请求

有一些模块数据是很少被修改的,比如接口的兜底数据、阿里 APP 模块数据等,可以通过调整参数,设置模块的缓存时间,如:

io({
  url: URL,
  dataType: 'jsonp',
  cache: true,
  jsonpCallback: 'jsonp' + Math.floor(new Date / (1000 * 60)),
  success: function() {    //...  }
});

Math.floor(new Date / (1000 * 60)) 这个数值在一分钟内是不会发生变化的,也就是说将这个请求在本地缓存一分钟,对于低频修改模块,缓存时间可以设置为一天,即:


Math.floor(new Date / (1000 * 60 * 60 * 24))

当然,我们也可以采用本地储存的方式缓存这个模块数据:

offline.setItem('cache-moduleName', JSON.stringify(data), 1000 * 60 * 60 * 24);

缓存过期时间设置为 1 天,淘宝首页主要采用本地缓存的方式。

使用缓动效果减少等待的焦急感

这方面的优化不是很多,但是也有一点效果,很多模块的展示并不是干巴巴的 .show(),而是通过动画效果,缓动呈现,这方面的优化推荐使用 CSS3 属性去控制,性能消耗会少很多。

优化的思考角度

页面优化的切入点很多,我们不一定能够面面俱到,但是对于一个承载较大流量的页面来说,下面几条必须有效执行:

  • 首屏一定要快

  • 滚屏一定要流畅

  • 能不加载的先别加载

  • 能不执行的先别执行

  • 渐进展现、圆滑展现

当然,性能优化的切入角度不仅仅是上几个方面,对照 Chrome 的 Timeline 柱状图和折线图,我们还可以找到很多优化的点,如:

淘宝首页 Chrome Timeline

  • 在 1.0s 左右存在一次 painting 阻塞,可能因为一次性展示的模块面积过大

  • 从 FPS 的柱状图可以看出,在 1.5s-2.0s 之间,存在几次 Render 和 JavaScript 丢帧

  • 从多出的红点可以看出页面 jank 次数,也能够定位到代码堆栈

在优化的过程中需要更多地思考,如何让阻塞的脚本分批执行,如何将长时间执行的脚本均匀地分配到时间线上。这些优化都体现在代码的细节上,宏观上的处理难以有明显的效果。当然,在宏观上,淘宝首页也有一个明显的优化:\

// https://gist.github.com/miksago/3035015#file-raf-js(function() {  var lastTime = 0;  var vendors = ['ms', 'moz', 'webkit', 'o'];  for(var x = 0; x <p>这段代码基本保证每个模块的初始化都是在浏览器空闲时期,减少了很多不必要的丢帧。这个优化也可以被应用到每个模块的细节代码之中,不过优化难度会更高。</p><h2>小结</h2><p>代码的性能优化是一个精细活,如果你要在一个庞大的未经优化的页面上做性能优化,可能会面临一次重构代码。本文从php网站性能优化引出的问题出发,依实战淘宝首页速度优化提升实战为案例,从微观到宏观讲述了页面的优化实践,提出了几条可以借鉴的优化标准,希望对你有所启发。优化的细节点描述的不够完善也不够全面,但是都是值得去优化的方向。</p><p>相关文章:</p><p><a href="http://www.php.cn/php-weizijiaocheng-357241.html" target="_self">大型php网站性能和并发访问优化方案</a></p>
성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.