>  기사  >  웹 프론트엔드  >  Js 프론트엔드 성능 최적화 요약

Js 프론트엔드 성능 최적화 요약

小云云
小云云원래의
2018-02-28 13:42:481728검색

가장 좋은 리소스 최적화는 리소스를 로드하지 않는 것입니다. 캐싱은 가장 효과적인 최적화 방법이기도 합니다. 솔직히 말해서 클라이언트 측 캐싱은 브라우저 측에서 발생하지만 캐싱은 주로 서버에 의해 제어되며 프런트엔드와는 거의 관련이 없습니다. 그러나 여전히 이해가 필요합니다.

캐싱에는 서버측 캐싱과 클라이언트측 캐싱이 포함됩니다. 이 문서에서는 클라이언트측 캐싱에 대해서만 설명합니다. 소위 클라이언트 캐시는 주로 http 캐시입니다. HTTP 캐싱은 크게 강제 캐싱과 협상 캐싱으로 구분됩니다.

강제 캐싱

  • Expires(http1.0)

http1.0의 Expires를 사용하여 강제 캐싱을 수행하세요. Exprires 값은 서버가 반환한 데이터의 만료 시간입니다. 재요청 시 요청 시간이 반환 시간보다 짧은 경우 캐시된 데이터를 그대로 사용합니다. 그러나 서버 시간과 클라이언트 시간이 다를 수 있으므로 이로 인해 캐시 적중 오류도 발생할 수 있습니다.

  • Cache-Control

Cache-Control에는 많은 속성이 있으며 속성마다 의미가 다릅니다.

  1. private: 클라이언트가 캐시할 수 있음

  2. public: 클라이언트와 프록시 서버 모두 캐시할 수 있음

  3. max-age=t: 캐시된 콘텐츠가 t초 후에 만료됨

  4. no-cache : 캐시된 데이터를 확인하려면 협상된 캐시가 필요합니다.

  5. no-store: 모든 콘텐츠가 캐시되지 않습니다.

캐시 협상

브라우저가 처음으로 데이터를 요청하면 서버는 캐시 ID와 데이터로 클라이언트에 응답하고 클라이언트는 이를 캐시에 백업합니다. 다시 요청할 때 클라이언트는 캐시에 있는 식별자를 서버로 보내고 서버는 이 식별자를 기반으로 판단합니다. 만료되지 않은 경우 304 상태 코드가 반환됩니다. 브라우저는 이 상태 코드를 받은 후 바로 캐시된 데이터를 사용할 수 있습니다.

  • Last-Modified

서버가 요청에 응답하면 브라우저에 리소스의 마지막 수정 시간을 알려줍니다.

  • if-Modified-Since

브라우저가 서버에 요청하면 다시 한번 요청 헤더에 이 필드가 포함되고 그 뒤에 캐시에서 얻은 Last-Modified(마지막 수정 시간)가 표시됩니다. 서버가 이 요청 헤더를 수신하고 if-Modified-Since가 있으면 요청한 리소스의 마지막 수정 시간과 비교하여 요청한 리소스의 마지막 수정 시간보다 크면 304를 반환하고 브라우저는 캐시에서 리소스를 얻습니다. 요청된 리소스의 마지막 수정 시간보다 작으면 200이 반환되고 최신 리소스가 반환됩니다. 브라우저는 서버에서 최신 리소스를 얻어 캐시합니다.

  • Etag

서버에서 생성된 각 리소스의 고유 식별 문자열

  • If-None-Match

서버가 다시 요청되면 브라우저의 요청 메시지 헤더에 이 필드가 포함됩니다. 다음 값은 캐시에서 얻은 식별자입니다. 메시지를 받은 후 서버는 If-None-Match를 찾아 요청한 리소스의 고유 식별자와 비교합니다. 동일하다면 리소스가 수정되지 않았다는 의미로 304를 반환하고, 브라우저가 캐시에서 리소스를 획득했다는 의미이며, 다르다면 리소스가 수정되었다는 의미이며 200을 반환하고, 최신 리소스. 브라우저는 서버에서 최신 리소스를 가져와 캐시합니다.

Last-Modified와 ETag를 함께 사용할 수 있습니다. 서버는 ETag를 먼저 확인하고 일치하는 경우 계속해서 Last-Modified를 비교한 후 최종적으로 304를 반환할지 결정합니다.

프런트엔드 패키징 도구를 사용하면 패키징할 때 파일에 버전 번호나 해시 값을 추가할 수 있고, 리소스가 만료되었는지 여부도 구분할 수 있습니다.

http 요청 감소

  • CDN을 사용하여 정적 리소스 호스팅

  • gulp, webpack 및 기타 패키징 도구를 사용하여 js, CSS 및 기타 파일을 병합하고 압축할 수 있습니다.

  • 이미지 지연 로딩, 주문형 로딩, 스크롤할 때 이미지는 이미지의 보이는 영역에만 로드됩니다.

  • 작은 이미지와 기본적으로 변경되지 않은 이미지는 base64 인코딩을 사용하여 전송됩니다. base64를 남용하지 마십시오. 작은 그림이라도 base64 인코딩 후에는 긴 문자열이 생성됩니다. base64를 남용하면 비생산적입니다. 이미지를 수정하면 전체 스프라이트 이미지가 발생하기 때문에 기본적으로 변경되지 않는 그림에 스프라이트 그림이 사용됩니다. 무분별하게 사용하면 역효과를 낳게 됩니다.

  • http 요청 리소스 크기 줄이기

webpack, gulp 및 기타 도구를 사용하여 리소스 압축

  • 서버에서 gzip 압축 활성화(압축률은 매우 인상적이며 일반적으로 30% 이상)

  • 유용한 패키징 도구라면 패키징 최적화가 잘 이루어져야 하고, 공개 리소스, 타사 코드 추출, 패키징이 필요 없는 라이브러리...

  • 렌더링 최적화

  • 이전 js 실행을 읽어보신 분들 메커니즘은 브라우저에서 URL을 입력하고 페이지가 화면에 나타날 때까지 무슨 일이 일어났는지 알아야 합니다(tcp 핸드셰이크, DNS 해상도 등은 지식 범위에 속하지 않습니다).

FPS 16ms, 10ms 미만이 가장 좋습니다. Google devtool을 사용하여 프레임 속도를 확인하세요

브라우저 FPS가 60에 도달하면 더 부드럽게 표시됩니다. 대부분의 모니터의 새로 고침 빈도는 60Hz이며 브라우저는 이 빈도에서 자동으로 애니메이션을 새로 고칩니다.
FPS 60을 기준으로 계산하면 평균 프레임 시간은 1000ms/60 = 16.7ms이므로 각 렌더링 시간은 16ms를 초과할 수 없습니다. 이 시간을 초과하면 프레임 손실 및 지연이 발생합니다.

Chrome 브라우저 개발자 도구의 타임라인에서 새로 고침 빈도를 확인할 수 있으며, 모든 프레임 속도의 시간 소모 및 특정 프레임의 실행을 확인할 수 있습니다. 타임라인 사용 튜토리얼: https://segmentfault.com/a/11...

일반적인 FPS를 보장하려면 일부 렌더링 성능 최적화가 여전히 필요합니다. 다음은 렌더링 최적화와 관련된 모든 전략입니다.

  • 애니메이션에 CSS3를 사용해 보세요

우리 모두 알고 있듯이 CSS의 성능은 js보다 빠르므로 CSS를 사용할 수 있으며 이를 달성하기 위해 js를 사용하지 마십시오

  • setTimeout 사용을 피하거나 setInterval을 사용하려면 requestAnimationFrame 애니메이션이나 빈도가 높은 Dom 작업을 사용해 보세요.

setTimeout과 setInterval은 콜백 함수의 실행 시점을 보장할 수 없기 때문에 프레임 끝에서 실행되어 프레임 손실이 발생할 가능성이 높지만, requestAnimationFrame은 콜백 함수가 시작 부분에서 실행되는 것을 보장할 수 있습니다. 애니메이션의 각 프레임
requestAnimationFrame의 중국어 MDN 주소 : https://developer.mozilla.org...

  • 복잡한 계산 작업은 Web Workers를 사용합니다

순회 및 합산과 같은 복잡한 데이터 작업이 필요한 경우 요소가 배열된 경우 Web Worker가 더 적합할 수 없습니다.

Web Workers는 JavaScript 스크립트가 백그라운드 스레드에서 실행되도록 허용할 수 있으며(하위 스레드 생성과 유사) 백그라운드 스레드는 기본 스레드의 페이지에 영향을 미치지 않습니다. 그러나 Web Worker를 사용하여 생성된 스레드는 DOM 트리를 작동할 수 없습니다.
Web Workers에 대한 자세한 내용은 MDN 자세한 설명을 참조하세요: https://developer.mozilla.org...

  • css는 헤드에 배치되고 js는 테일에 배치됩니다.

이전 js 실행 메커니즘을 읽어본 분들은 페이지 렌더링 과정을 아실 것이므로 자세한 내용은 다루지 않겠습니다. CSS를 헤드에 배치하면 html 트리를 생성한 후 다시 레이아웃되는 스플래시 화면 현상을 피할 수 있습니다. js는 일반적으로 페이지에 더 큰 영향을 미치며 일반적으로 마지막 실행을 위해 끝에 배치됩니다.

  • 이벤트 디바운스 및 제한

빈도가 높은 이벤트(마우스 이동, 스크롤) 및 기타 이벤트의 경우 제어하지 않으면 짧은 시간 내에 많은 이벤트가 트리거됩니다.

흔들림 방지 기능은 자주 실행될 때 코드가 충분한 여유 시간이 있을 때 한 번만 실행된다는 것을 의미합니다. 시나리오: 등록 중 이메일 입력 상자에 사용자가 입력하면 이메일 형식이 올바른지 실시간으로 판단됩니다. 첫 번째 입력 이벤트가 트리거되면 타이밍을 설정하십시오. 800ms 후에 확인하십시오. 100ms만 경과하고 마지막 타이머가 실행되지 않은 경우 타이머를 지우고 800ms로 다시 시간을 설정합니다. 가장 최근의 입력까지는 인접한 입력이 없고, 가장 최근에 입력된 타이밍이 종료되어 최종적으로 체크코드가 실행된다.

const filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;  
$("#email").on("keyup",checkEmail());  
function checkEmail(){  
    let timer=null;  
    return function (){  
        clearTimeout(timer);  
        timer=setTimeout(function(){  
            console.log('执行检查');  
        },800);  
    }  
}

함수 제한은 js 메서드가 특정 기간 내에 한 번만 실행된다는 의미입니다. 즉, 원래 초당 100번 실행되던 것이 초당 10번이 되는 것입니다.
시나리오: 기능 조절 애플리케이션의 실제 시나리오. 대부분 페이지 요소의 스크롤 이벤트를 모니터링할 때 사용됩니다.

var canRun = true;
document.getElementById("throttle").onscroll = function(){
    if(!canRun){
        // 判断是否已空闲,如果在执行中,则直接return
        return;
    }

    canRun = false;
    setTimeout(function(){
        console.log("函数节流");
        canRun = true;
    }, 300);
};
  • Dom 작업

프런트엔드 개발자는 모두 Do 작업이 시간이 많이 걸린다는 것을 알고 있습니다. (저는 개인적으로 스타일을 추가하기 위해 30*30 테이블 순회를 테스트했습니다.) 따라서 빈번한 Dom 작업을 피하십시오. 피할 수 없다면 DOM 작업을 최적화하십시오.

1.:缓存Dom查询,比如通过getElementByTagName('p')获取Dom集,而不是逐个获取。

2: 合并Dom操作,使用createDocumentFragment()
    var frag = document.createDocumentFragment()
    for (i<10) {
        var li = document.createElement(&#39;li&#39;)
        frag.appendChild(li)
    }
    document.body.appendChild(frag)
 3: 使用React、Vue等框架的虚拟dom(原理目前还不明白),可以更快的实现dom操作。
  • 다시 그리기와 리플로우를 피하세요

js를 사용하여 요소의 색상이나 배경색을 수정하면 다시 그리는 비용이 여전히 상대적으로 비쌉니다. 특정 DOM 요소의 시각적 효과가 변경되면 해당 DOM 요소 내의 모든 노드를 확인하세요.

요소의 크기와 위치가 수정되면 리플로우가 발생합니다. 리플로우는 특정 DOM 요소의 위치가 변경된 후에 트리거되며 모든 요소의 위치와 차지하는 영역을 다시 계산합니다. 페이지의 특정 부분 또는 전체 페이지가 다시 렌더링됩니다.

  • css3 하드웨어 가속

브라우저가 렌더링되면 일반 레이어와 복합 레이어의 두 레이어로 나뉩니다.

일반 문서 흐름은 복합 레이어로 이해될 수 있습니다. 절대 및 고정 레이아웃은 일반 문서 흐름과 분리될 수 있지만 여전히 일반 레이어에 속하며 하드웨어 가속이 활성화되지 않습니다. 위에서 언급한 리페인트(repaint)와 리플로우(reflow)는 일반 레이어에서의 다시 그리기(reraw)와 리플로우(reflow)를 의미합니다.

복합 레이어를 사용하면 하드웨어 가속이 가능해집니다. 일반 레이어와 같은 레이어에 있지 않기 때문에 합성 레이어가 일반 레이어에 영향을 주지 않습니다. 이렇게 하면 렌더링 성능이 향상됩니다.

하드웨어 가속을 시작하는 방법:

1.translate3d를 사용하고 Z를 번역하세요

webkit-transform: translateZ(0);
-moz-transform: translateZ(0);
-ms-transform: translateZ(0);
-o-transform: translateZ(0);
transform: translateZ(0);

webkit-transform: translate3d(0,0,0);
-moz-transform: translate3d(0,0,0);
-ms-transform: translate3d(0,0,0);
-o-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);

2.使用opacity
需要动画执行的过程中才会创建合成层,动画没有开始或结束后元素还会回到之前的状态

3.使用will-chang属性
这个属性比较不常用,一般配合opacity与translate使用

针对webkit浏览器,启用硬件加速有些时候可能会导致浏览器频繁闪烁或抖动,可以使用下面方法消除:

-webkit-backface-visibility:hidden;
-webkit-perspective:1000;
如果使用硬件加速,请使用z-index配合使用, 因为如果这个元素添加了硬件加速,并且index层级比较低, 那么在这个元素的后面其它元素(层级比这个元素高的,或者相同的,并且releative或absolute属性相同的), 会默认变为复合层渲染,如果处理不当会极大的影响性能
  • 避免强制同步布局和布局抖动

浏览器渲染过程为:js/css(javascript) > 计算样式(style) > 布局(layout) > 绘制(paint) > 渲染合并图层(Composite)

JavaScript:JavaScript实现动画效果,DOM元素操作等。
Style(计算样式):确定每个DOM元素应该应用什么CSS规则。
Layout(布局):计算每个DOM元素在最终屏幕上显示的大小和位置。
Paint(绘制):在多个层上绘制DOM元素的的文字、颜色、图像、边框和阴影等。
Composite(渲染层合并):按照合理的顺序合并图层然后显示到屏幕上。

在js中如果读取style属性的某些值就会让浏览器强行进行一次布局、计算,然后再返回值,比如:

offsetTop, offsetLeft, offsetWidth, offsetHeight

scrollTop/Left/Width/Height

clientTop/Left/Width/Height

width,height

请求了getComputedStyle(), 或者 IE的 currentStyle

所以,如果强制浏览器在执行JavaScript脚本之前先执行布局过程,这就是所谓的强制同步布局。
比如下面代码:

requestAnimationFrame(logBoxHeight);

// 先写后读,触发强制布局
function logBoxHeight() {
    // 更新box样式
    box.classList.add('super-big');

    // 为了返回box的offersetHeight值
    // 浏览器必须先应用属性修改,接着执行布局过程
    console.log(box.offsetHeight);
}

// 先读后写,避免强制布局
function logBoxHeight() {
    // 获取box.offsetHeight
    console.log(box.offsetHeight);

    // 更新box样式
    box.classList.add('super-big');
}

在JavaScript脚本运行的时候,它能获取到的元素样式属性值都是上一帧画面的,都是旧的值。因此,如果你在当前帧获取属性之前又对元素节点有改动,那就会导致浏览器必须先应用属性修改,结果执行布局过程,最后再执行JavaScript逻辑。

如果连续多次强制同步布局,就会导致布局抖动
比如下面代码:

function resizeAllParagraphsToMatchBlockWidth() {
  for (var i = 0; i < paragraphs.length; i++) {
    paragraphs[i].style.width = box.offsetWidth + &#39;px&#39;;
  }
}

作者:SylvanasSun
链接:https://juejin.im/post/59da456951882525ed2b706d
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

我们知道浏览器是一帧一帧的刷新页面的,对于每一帧,上一帧的布局信息都是已知的。
强制布局就是使用js强制浏览器提前布局,比如下面代码:

// bed  每次循环都要去获取left ,就会发生一次回流
function logBoxHeight() {
  box.style.left += 10
  console.log(box.style.left)
}

// goog 
var width = box.offsetWidth;

function resizeAllParagraphsToMatchBlockWidth() {
  for (var i = 0; i < paragraphs.length; i++) {
    // Now write.
    paragraphs[i].style.width = width + &#39;px&#39;;
  }
}
  • DOMContentLoaded与Load

DOMContentLoaded 事件触发时,仅当DOM加载完成才触发DOMContentLoaded,此时样式表,图片,外部引入资源都还没加载。而load是等所有的资源加载完毕才会触发。

1. 解析HTML结构。
2. 加载外部脚本和样式表文件。
3. 解析并执行脚本代码。
4. DOM树构建完成。//DOMContentLoaded
5. 加载图片等外部文件。
页面加载完毕。//load
  • 视觉优化

等待加载时间可以合理使用loading gif动图一定程度上消除用户等待时间的烦躁感

代码性能

代码对性能的影响可大可小,但是养成一个良好的写代码习惯和高质量的代码,会潜移默化的提高性能,同时也能提高自己的水平。废话不多说,直接看我总结的部分要点(因为这一部分知识点太多,需要大家写代码的时候多多总结)。

  • 避免全局查找

访问局部变量会比访问全局变量快,因为js查找变量的时候现在局部作用局查找,找不到在逐级向上找。

// bad
function f () {
    for (...){
        console.log(window.location.href)
    }
}

//good
function f () {
    var href = window.location.href
    for (...){
        console.log(href)
    }
}
  • 循环技巧

// bed 
for(var i = 0; i < array.length; i++){
    ....
}
// good
for(var i = 0, len = array.length; i < len; i++){
    ....
}
// 不用每次查询长度
  • 不要使用for in 遍历数组

for in是最慢的,其他的都差不多,其中直接使用for循环是最快的。for in只是适合用来遍历对象。

  • 使用+''代替String()吧变量转化为字符串

var a = 12
//bad
a = String(a)

// good
var a = 12
a = a + &#39;&#39;

这个还有很多类似的,比如使用*1代替parseInt()等都是利用js的弱类型,其实这样对性能提升不是很大,网上有人测试过,进行十几万次变量转换,才快了零点几秒。

  • 删除dom

删除dom元素要删除注册在该节点上的事件,否则就会产生无法回收的内存,在选择removeChild和innerHTML=''二者之间尽量选择后者,据说removeChild有时候无法有效的释放节点(具体原因不明)

  • 使用事件代理处理事件

任何可以冒泡的事件都可以在节点的祖先节点上处理,这样对于子节点需要绑定相同事件的情况就不用分别给每个子节点添加事件监听,而是都提升到祖先节点处理。

  • 通过js生成的dom对象必须append到页面中

在IE下,js创建的额dom如果没有添加到页面,这部分内存是不会被回收的

  • 避免与null比较

可以使用下面方法替换与null比较
1.如果该值为引用类型,则使用instanceof检查其构造函数
2.如果该值为基本类型,使用typeof检查类型

  • 尽量使用三目运算符代替if else

if(a>b){num = a}
else{num = b}

// 可以替换为
num = a > b ? a : b
  • 当判断条件大于3中情况时,使用switch代替if

因为switch的执行速度比if要快,也别是在IE下,速度大约是if的两倍。

相关推荐:

CSS解读前端性能优化的具体分析

在HTML5中如何提高网站前端性能的示例代码分析

web前端性能优化方法


위 내용은 Js 프론트엔드 성능 최적화 요약의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.