본문 바로가기

FrontEnd

프론트엔드 지식 : Javascript Critical Rendering Path

반응형

원문 링크

정의에 따르면 Critical Rendering Path
브라우저가 HTML 페이지를 가져오는 시간과
사용자가 시각화할 수 있는 웹 페이지 구축을 시작하는 시간
사이에 경로를 만드는 단계의 모음일 뿐입니다.
이 과정에서 브라우저가 수행하는 단계를 최적화해야 합니다.
크리티컬 렌더링 패스 최적화란, css, javascript, html을 적게 가져오고, 파싱 및 실행하여,
사용자의 인터랙션까지의 시간을 단축시키는 것을 의미한다.
 

The Document Object Model

모든 웹 페이지에는 Document Object Model 또는 DOM이 있습니다.

이것은 구문 분석(parsed)된 상태에 있는 전체 HTML 페이지의 객체 표현입니다.

HTML이 구문 분석되면 DOM 트리가 생성됩니다.

DOM 트리에는 객체가 포함되어 있습니다.

 

간단한 HTML 코드로 토론을 시작하겠습니다.
아래 코드는 header, main section, footer 세 부분으로 구성되어 있습니다.
브라우저에서 렌더링할 수 있는 가장 간단한 HTML 예제 중 하나입니다.
스타일시트: "style.css"는 HTML 페이지의 서식을 지정하기 위해 로드된 외부 파일입니다.
<html>
  <head>
  <link rel="stylesheet" href="style.css">
  <title>101 Javascript Critical Rendering Path</title>
  <body>
    <header>
      <h1>The Rendering Path</h1>
      <p>Every step during the rendering of a HTML page, forms the path.</p>
    </header>
    <main>
         <h1>You need a dom tree</h1>
         <p>Have you ever come across the concepts behind a DOM Tree?</p>
    </main>
    <footer>
         <small>Thank you for reading!</small>
    </footer>
  </body> 
  </head>
</html>
위의 HTML 코드가 브라우저에 의해 DOM 트리 구조로 구문 분석되면 다음과 같이 나타납니다.

parsed dom tree structure

모든 브라우저는 위의 HTML을 구문 분석하는 데 시간이 걸립니다.

클린한 시멘틱 마크업은 브라우저가 HTML을 구문 분석하는 데 필요한 시간을 줄이는 데 도움이 됩니다.

 

CSSOM Tree

다음은 CSSOM 트리입니다.
이것은 CSS 객체 모델에 지나지 않습니다.
다시 한 번, CSS 객체 모델은 객체 기반 트리입니다.
DOM 트리와 관련하여 존재하는 스타일을 처리합니다.
일반적으로 CSS 스타일은 명시적으로 상속되거나 선언될 수 있습니다.
header{
   background-color: white;
   color: black;
}
p{
   font-weight:400;
}
h1{
   font-size:72px;
}
small{
   text-align:left
}
위 CSS 선언의 경우 CSSOM 트리가 다음과 같이 나타납니다.

 
일반적으로 CSS는 렌더링 차단(Render-Blocking) 리소스로 간주됩니다.
렌더링 차단이란 무엇입니까?
렌더링 차단 리소스는 주어진 리소스가 완전히 로드될 때까지 브라우저가 전체 DOM 트리를 렌더링하는 것을 허용하지 않는 구성 요소입니다.
CSS가 완전히 로드될 때까지 트리를 렌더링할 수 없기 때문에 CSS는 렌더링 차단 리소스입니다.
처음에는 CSS가 단일 소스에서 제공되었습니다.
이제 개발자는 CSS 파일을 분할하고 렌더링 초기 단계에서 중요한 스타일만 제공하는 다양한 기술을 생각해 냈습니다.

Executing JavaScript

계속해서 JavaScript는 문서 개체 모델을 조작하는 데 사용되는 언어입니다.
DOM과 상호 작용하는 팝업 또는 캐러셀와 같은 다양한 사용 사례에 대해 생각할 수 있습니다.
문제는 이러한 상호 작용에 시간이 걸리고 웹 사이트의 전체 로딩 시간이 감소하기 시작하는 때입니다.
이것이 JavaScript 코드가 "파서 차단(Parser Blocking)" 리소스로 알려진 이유입니다.

파서 차단이란 무엇입니까?

JavaScript 코드를 다운로드하고 실행해야 하는 경우, 브라우저는 DOM 트리 파싱 및 구성을 일시 중지합니다.

JavaScript 코드가 실행되는 순간 DOM 트리의 구성이 계속됩니다.

 

"이것이 JavaScript가 비싼 자원인 이유입니다"

Let’s work on some real-time examples

다음은 일부 텍스트와 이미지를 표시하는 간단한 HTML 코드의 데모입니다.
보시다시피, 전체 페이지가 표시되는 데 약 40ms가 걸렸습니다.
이미지가 있어도 페이지가 표시되는 데 걸리는 시간이 적습니다.
첫 번째 페인트를 칠할 때 이미지는 중요한 리소스로 취급되지 않기 때문입니다.
크리티컬 렌더링 패스는 모두 HTML, CSS 및 Javascript에 관한 것임을 기억하십시오.
우리는 가능한 한 빨리 이미지를 표시하도록 노력해야 하지만, 이미지가 초기 렌더링을 차단하지는 않습니다.
전체 페이지가 표시되는 데 약 40ms가 걸렸습니다.
이제 코드 조각에 CSS를 추가해 보겠습니다. 보시다시피 추가 요청이 발생하고 있습니다.
html 파일을 로드하는 데 걸리는 시간은 적지만 전체 페이지 처리 및 표시 시간은 거의 10배 증가했습니다.
왜죠?
  • 순수한 HTML은 가져오기 및 구문 분석을 많이 포함하지 않습니다.
  • 그러나 CSS 파일을 사용하면 CSSOM(위에서 설명한 대로)을 구성해야 합니다. HTML DOMCSS CSSOM이 모두 빌드되어야 합니다. 이것은 확실히 시간이 많이 걸리는 과정입니다.
참고: domContentLoaded는 HTML DOM이 완전히 구문 분석되고 로드될 때 시작됩니다.
이벤트는 이미지, 서브프레임 또는 스타일시트가 완전히 로드될 때까지 기다리지 않습니다.
유일한 대상은 문서를 로드하는 것입니다.
DOM이 파싱되어 로드되었는지 여부를 확인하기 위해 window 인터페이스에 이벤트를 추가할 수 있습니다.
이벤트 리스너는 다음과 같습니다.
window.addEventListener('DOMContentLoaded', (event) => {
    console.log('DOM Content Loaded Event');
});
외부 JavaScript 파일을 인라인 스크립트로 교체하도록 선택하더라도 성능은 크게 변경되지 않습니다.
CSSOM을 구성해야 하기 때문입니다.
외부 스크립트를 고려하고 있다면 제안된 솔루션이 있습니다.
그것은 "async"를 추가하는 것입니다.
이렇게 하면 파서의 차단이 해제됩니다.
async에 대한 자세한 내용은 나중에 살펴보겠습니다.
 

용어를 올바르게 정리합시다!

문제를 해결하기 전에 Critical Rendering Path를 설명하는 올바른 용어를 알아보겠습니다.
  1. Critical Resource : 페이지 렌더링을 차단할 수 있는 모든 리소스입니다.
  2. Critical Path Length : 페이지를 빌드하는 데 필요한 모든 중요한 리소스를 가져오는 데 필요한 총 왕복 횟수입니다.
  3. Critical Bytes : 페이지 빌드 완료 시 전송된 총 바이트 수입니다.
순수 HTML 스크립트를 사용하는 첫 번째 예에서 위의 필드는 다음과 같습니다.
  • 1 크리티컬 리소스 (html)
  • 1 왕복 (1 Critical Path Length)
  • 192바이트의 데이터 (Critical Byte)

순수 HTML과 외부 CSS 스크립트를 포함한 두번째 예에서는 다음과 같습니다.

  • 2 크리티컬 리소스 (css, html)
  • 2 왕복
  • 400 바이트의 데이터

모든 프레임워크 혹은 일반 HTML+CSS+Javascript 코드에서 주요 렌더링 경로를 최적화하려면

위의 측정항목을 작업하고 개선해야 합니다.

 

  • 가능한 한 적은 수의 크리티컬 리소스를 포함하는 것이 중요합니다. 이것은 CPU와 브라우저에 더 적은 작업을 부과합니다.
  • 다운로드에 걸리는 시간과 리소스 크기 간의 종속성을 조심합시다
    • . 리소스가 크면 크리티컬 패스 길이가 늘어납니다. 리소스를 가져오기 위한 더 많은 왕복 횟수가 필요합니다.
  • 크리티컬 바이트를 최소한으로 유지하기 위해, 압축을 활용하거나, 중요하지 않은 리소스로 변경할 수 있습니다.

How to Cut Down on Render-Blocking Resources CSS?

모든 웹 페이지에는 초기 스크롤 지점(폴드) 이전과 이후 콘텐츠가 있습니다.

초기 컨텐츠를 주의 깊게 선택합니다.

 

이들은 크리티컬한 스타일입니다. 나머지 스타일은 나중에 로드할 수 있습니다.

즉, 펼치기 전에 필요한 스타일만 다운로드 하고, 이후 스타일은 나중에 받아도 됩니다.

이렇게 하면 웹 페이지의 속도를 높일 수 있습니다. 또한 불필요한 렌더링 차단 스타일을 제거할 수 있습니다.

How to Cut Down on Parser Blocking Resources?

Lazy Loading

로딩의 핵심은 "Lazy Loading"일 것입니다.
아마존이나 페이스북과 같은 웹사이트에는 많은 콘텐츠가 있습니다.
 
그들은 그 리소스들을 어떻게 로드할까요?
스크롤하면 콘텐츠가 로드되고 느려지지 않습니다.
 
Lazy Loading이라는 개념을 사용하기 때문입니다.
 
모든 미디어, CSS, JavaScript, 이미지, 심지어 HTML도 지연 로드할 수 있습니다.
한 번에 페이지에 로드되는 콘텐츠의 양은 제한됩니다.
이렇게 하면 중요 렌더링 경로 점수가 향상됩니다.
  • 페이지에 오버레이가 있다고 상상해보십시오.
  • 페이지를 로드하는 동안 이 오버레이의 CSS, JavaScript 및 HTML을 로드하지 마십시오.
  • 대신 버튼에 이벤트 리스너를 추가하고 사용자가 버튼을 클릭할 때만 스크립트를 로드합니다.
  • 이 기능을 수행하려면 Webpack을 사용하십시오.
다음은 Pure JavaScript에서 Lazy Loading을 구현하는 몇 가지 기술입니다.

이미지와 iFrame부터 시작하겠습니다. 중요하지 않은 이미지를 어떻게 지연 로드합니까?
또는 사용자 상호 작용 후에만 표시되어야 하는 이미지를 로드하려면 어떻게 해야 합니까?
이러한 경우 <img> 및 <iframe> 태그와 함께 제공되는 기본 로딩 속성을 사용할 수 있습니다.
브라우저는 이 태그를 볼 때 iframe 및 이미지의 로드를 지연합니다.
이 동작을 수행하기 위한 구문은 다음과 같습니다.
<img src="image.png" loading="lazy">
<iframe src="tutorial.html" loading="lazy"></iframe>

참고: loading=lazy를 사용한 지연 로딩은 첫 번째로 보이는 뷰포트 내의 이미지에 사용하면 안 됩니다.

접힌 상태의 이미지에만 적용해야 합니다.

 

loading=lazy를 활용할 수 없는 브라우저에서는 IntersectionObserver를 사용할 수 있습니다.
Intersection Observer API를 사용할 수 있는 인터페이스입니다.
이 API는 루트를 설정하고 루트에서 모든 요소의 가시성에 대한 비율을 구성합니다.
요소가 뷰포트에 표시되면 로드됩니다.
다음은 이 API를 이해하는 데 도움이 되는 간단한 코드입니다.
  • ".lazy" 클래스가 있는 모든 요소를 ​​관찰합니다.
  • ".lazy" 클래스가 있는 요소가 뷰포트에 있으면 교차 비율이 0 이상이 됩니다.
  • 교차 비율이 0이거나 0 미만이면 대상이 시야에 없는 것입니다. 아무것도 할 필요가 없습니다.
  • 이제 이러한 요소에 대해 미리 정의된 일련의 작업이 수행됩니다.
intersection Observer를 이용한 이미지 지연 로딩
var intersectionObserver = new IntersectionObserver(function(entries) {
  if (entries[0].intersectionRatio <= 0) return;

  //intersection ratio is above zero
  console.log('Loading Lazy Items');
});
// start observing
intersectionObserver.observe(document.querySelector('.lazy));

Async, Defer, Preload

참고: Async 및 Defer는 외부 스크립트에서 사용되는 속성입니다.

Async를 사용하면 JavaScript 리소스가 다운로드되는 동안 브라우저가 다른 작업을 수행할 수 있습니다.

다운로드한 자바스크립트 리소스는 다운로드가 완료되는 즉시 실행됩니다.

  • JavaScript는 비동기적으로 다운로드됩니다.
  • 다른 모든 스크립트의 실행이 일시 중지됩니다.
  • DOM 렌더링은 동시에 발생합니다.
  • DOM 렌더링은 스크립트가 실행될 때만 일시 중지됩니다.
  • 렌더링 차단 JavaScript 문제는 ​​async 속성을 사용하여 해결할 수 있습니다.
“리소스가 중요하지 않다면 async도 쓰지 말고 완전히 생략하세요”

예시:

<p>...content before scripts...</p>

<script>
  document.addEventListener('DOMContentLoaded', () => alert("DOM ready!"));
</script>

<script async src=""></script>

<!-- will be visible after the above script is completely executed –>
<p>...content after scripts...</p>

Defer를 사용하면 HTML 렌더링이 발생하는 동안 JavaScript 리소스가 다운로드됩니다.

그러나 스크립트가 다운로드되는 즉시 실행되지 않습니다. 대신 HTML 파일이 완전히 렌더링될 때까지 기다립니다.
  • Defer는 async 이후에 실행됩니다.
  • 스크립트 실행은 렌더링이 완료된 후에만 발생합니다.
  • Defer는 JavaScript 리소스를 비 렌더링 차단 리소스로 만들 수 있습니다.

예시:

<>
<p>...content before script...</p>

<script defer src=""></script>

<!-- this content will be visible immediately -->
<p>...content after script...</p>

 

Preload : HTML 파일에는 필요하지 않지만, JavaScript 또는 CSS 파일을 렌더링하거나 구문 분석하는 동안 필요한 파일에 사용합니다.

Preload를 사용하면 브라우저가 리소스를 다운로드 하고, 우리는 리소스가 필요할 때 사용할 수 있습니다.

  • Preload를 현명하게 사용하십시오. 브라우저는 페이지에서 필요하지 않은 경우에도 파일을 다운로드합니다.
  • Preload가 너무 많으면 페이지 속도가 느려집니다.
  • Preload된 파일이 너무 많으면 Preload를 사용하는 우선 순위가 영향을 받습니다.
  • 접힌 내용 위에 필요한 파일만 Preload합니다.
    • 이렇게 하면 Google PageSpeed ​​Insight 점수가 높아집니다.
  • 다른 파일이 렌더링될 때만 사용됩니다.
    • 예를 들어 CSS 파일 내의 글꼴에 대한 링크를 추가합니다.
    • CSS 파일이 구문 분석될 때까지 새 글꼴의 필요성을 알 수 없습니다.
    • 이전에 글꼴을 다운로드했다면 사이트 속도가 향상됩니다.
  • Preload는 오직 <Link> 태그와만 사용됩니다.
<link rel="preload" href="style.css" as="style">
<link rel="preload" href="main.js" as="script">

Vanilla JS로 작성하고 타사 스크립트를 피하십시오.

 
Vanilla JS는 성능 및 접근성으로 번역됩니다.
주어진 사용 사례에 대해 타사 솔루션이 수행하는 모든 작업을 수행할 필요는 없습니다.
라이브러리는 종종 많은 문제를 해결합니다만,
단순한 문제를 해결하기 위해 무거운 라이브러리에 의존하면 코드에서 성능 저하가 발생합니다.
WebAIM 팀이 수행한 설문 조사에 따르면 거의 백만 개의 상위 웹 사이트에 심각한 접근성 문제가 있는 프레임워크가 있습니다.
사용자에 관심이 있다면 Vanilla JS로 작성하십시오.
 
프레임워크를 피하고 100% 새로운 코드를 작성하는 것이 필요한 것이 아닙니다.
헬퍼 함수와 작은 크기의 플러그인을 사용하는 것입니다.


Caching, and Expiring Content

에셋이 페이지에서 계속해서 사용되는 경우 항상 로드할 필요가 없습니다.
매번 사이트를 로드하는 것과 비슷합니다. 캐싱은 이 주기를 방지하는 데 도움이 됩니다.
헤더의 콘텐츠에 만료 데이터를 제공합니다. 캐시를 지우고 만료될 때만 다시 로드하십시오.

프론트엔드 코드의 모든 부분에서 캐싱을 달성하기 위해

브라우저는 HTTP 응답에서 네 가지 중요한 헤더 속성을 찾습니다.

  1. ETag
  2. Cache-Control
  3. Last-Modified
  4. Expires

Etag는 엔터티 태그라고도 합니다. 이것은 캐시 토큰의 유효성을 검사하는 문자열일 뿐입니다.

 
이 토큰은 요청이 캐시의 복사본으로 충족될 수 있는지 여부를 결정하기 위해 브라우저에서 사용할 수 있습니다.
리소스가 변경되지 않은 경우 서버는 동일한 해시 토큰을 반환합니다.
그리고 응답 바디는 비어있을 것입니다. 이때 응답 코드 304가 표시됩니다.
리소스가 만료된 경우 본문에 가장 최근 데이터가 채워집니다.

Cache-Control을 사용하면 애플리케이션이 주어진 요청에 대한 브라우저의 캐싱 정책을 결정할 수 있습니다.

no-cache, no-store, private or public.의 네 가지 옵션 중에서 선택할 수 있습니다.
 

Last Modified는 ETag와 매우 유사하지만 요청의 Last-Modified Header에 따라 다릅니다.

수정 날짜 및 시간은 클라이언트가 새 요청을 해야 하는지 여부를 결정하는 데 도움이 됩니다.

 

Expires는 데이터의 유효성을 결정하는 데 널리 사용되는 태그 중 하나입니다.

항상 애플리케이션은 만료되지 않은 데이터를 사용해야 합니다.
헤더 필드의 만료 날짜에 도달한 경우 리소스를 유효하지 않은 것으로 간주할 수 있습니다.
 
순수 JavaScript에서는 데이터를 로드해야 하는지 여부를 결정하기 위해 serviceworker를 자유롭게 사용할 수 있습니다.
예를 들어, styles.css와 script.js 두 개의 파일이 있습니다.
이 파일을 로드할 때, 서비스 워커를 사용하여 리소스를 새로 로드해야 하는지 또는 캐시를 사용할 수 있는지 결정할 수 있습니다.
/*Install gets executed when the user launches the single page application for *the first time
*/

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(cacheName).then(function(cache) {
      return cache.addAll(
        [
          'styles.css',
          'script.js'
        ]
      );
    })
  );
});

//When a user performs an operation
document.querySelector('.lazy').addEventListener('click', function(event) {
  event.preventDefault();
  caches.open('lazy_posts’).then(function(cache) {
    fetch('/get-article’).then(function(response) {
      return response;
    }).then(function(urls) {
      cache.addAll(urls);
    });
  });
});

//When there is a network response
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.open('lazy_posts').then(function(cache) {
      return cache.match(event.request).then(function (response) {
        return response 
      });
    })
  );
});
 

리액트의 관점에서 이야기해 봅시다.

이론이 상당히 많았지만, 여기까지 오게 되어 기쁩니다.
지금까지 읽었다면 크리티컬 렌더링 패스가 무엇인지,
코드가 웹 애플리케이션의 성능에 중요한 역할을 하는 이유를 알고 있을 것입니다.
 
다음 섹션에서는 성능을 처리하고 크리티컬 렌더링 패스를 가능한 한 짧게 만드는 방법에 대해 읽을 것입니다.
 
다음 예제에서 선택할 프레임워크는 React입니다.
최적화 기술은 두 단계로 나뉩니다.
 
하나는 애플리케이션이 로드되기 전입니다.
그리고 2단계는 응용 프로그램이 로드된 후 최적화하려는 사람들을 위한 것입니다.

Stage One

다음을 사용하여 간단한 애플리케이션을 빌드해 보겠습니다.
  1. Header
  2. Sidebar
  3. Footer

우리 애플리케이션에서는 사용자가 로그인한 경우에만 사이드바가 표시되어야 합니다.

Webpack은 코드 분할에 도움이 되는 훌륭한 도구입니다.

코드 분할을 활성화했다면 App.js 또는 Route 컴포넌트에서 바로 React Lazy 로딩을 사용할 수 있습니다.

 

그렇다면 지연 로딩은 무엇일까요?

이것은 코드를 논리적 조각으로 나누는 방법입니다.

논리적 조각은 애플리케이션이 필요할 때만 로드됩니다.

결과적으로 코드의 전체 무게는 작게 유지됩니다.

사용자가 로그인한 경우에만 사이드바 구성 요소를 로드해야 하는 경우 애플리케이션의 성능을 향상시킬 수 있는 몇 가지 방법이 있습니다. 먼저 경로에 지연 로딩 개념을 주입할 수 있습니다.
아래에서 볼 수 있듯이 코드는 세 개의 논리적 청크로 나뉩니다.
각 청크는 사용자가 특정 경로를 선택할 때만 로드됩니다.
 
즉, DOM은 초기 페인트 동안 Sidarbar 코드를 "크리티컬 바이트"의 일부로 고려할 필요가 없습니다.
마찬가지로 부모 App.js에서도 지연 로딩을 달성할 수 있습니다. 선택은 개발자와 사용 사례에 따라 다릅니다.
부모 컴포넌트에서 Lazy Loading이 어떻게 달성되는지 봅시다.
webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- /src
 |- index.js
 |- Header.js
 |- Sidebar.js
 |- Footer.js
 |- loader.js
 |- route.js
|- /node_modules
import { Switch, browserHistory, BrowserRouter as Router, Route} from 'react-router-dom';
const Header = React.lazy( () => import('Header'));
const Footer = React.lazy( () => import(Footer));
const Sidebar = React.lazy( () => import(Sidebar));

const Routes = (props) => {
return isServerAvailable ? (
<Router history={browserHistory}>
           <Switch>
             <Route path="/" exact><Redirect to='/Header’ /></Route>
             <Route path="/sidebar" exact component={props => <Sidebar {...props} />} />
             <Route path="/footer" exact component={props => <Footer {...props} />} />
          </Switch>
</Router> : <></>
}
App.js에서 조건부로 구성 요소를 렌더링할 수 있습니다.
아래 코드에서 props.user가 비어 있으면 사이드바를 로드하지 않습니다.
사이드바의 렌더링은 조건부입니다.
user.props의 값이 변경되면 React와 Webpack에 변경 사항을 알려줍니다.
따라서 필요한 스크립트 청크가 로드됩니다.
 
그러나 초기 렌더링 중에 props.user가 비어 있으면 이 코드를 렌더링할 필요가 없습니다.
사이드바가 무거운 컴포넌트인 경우 초기 로딩이 훨씬 부드럽고 빨라집니다.
 
왜요? 사용자가 페이지를 처음 방문할 때 해당 코드를 로드하지 않습니다.
조건부 렌더링은 또한 애플리케이션 전체에서 사용할 수 있습니다.
const Header = React.lazy( () => import('Header'));
const Footer = React.lazy( () => import(Footer));
const Sidebar = React.lazy( () => import(Sidebar));

function App (props) {
return(
<React.Fragment>
   <Header user = {props.user} />
   {props.user ? <Sidebar user = {props.user /> : null}
   <Footer/>
</React.Fragment>
)
}​
조건부 렌더링에 대해 더 이야기하자면,
React를 사용하면 버튼 클릭만으로 컴포넌트를 로드할 수 있습니다.
예를 들어 사용자가 헤더 구성 요소에서 로그인을 클릭하고 사이드 바를 로드해야 하는 경우 코드를 아래와 같이 수정할 수 있습니다.
//Sidebar.js
export default () => {
  console.log('You can return the Sidebar component here!');
};
import _ from 'lodash';
function buildSidebar() {
   const element = document.createElement('div');
   const button = document.createElement('button');
   button.innerHTML = 'Login';
   element.innerHTML = _.join(['Loading Sidebar', 'webpack'], ' ');
   element.appendChild(button);
   button.onclick = e => import(/* webpackChunkName: "sidebar" */ './sidebar).then(module => {
     const sidebar = module.default;
     sidebar()   
   });

   return element;
 }

document.body.appendChild(buildSidebar());
일반적으로 Suspense라는 다른 컴포넌트 내부에서 지연 로드된 모든 라우터 경로 또는 컴포넌트를 유지하는 것이 중요합니다.
Suspense의 역할은 지연 로드된 컴포넌트가 로드될 때 애플리케이션에 대체 콘텐츠를 제공하는 것입니다.
대체 콘텐츠는 로더 또는 페이지가 아직 그려지지 않은 이유를 사용자에게 알려주는 메시지와 같은 것일 수 있습니다.
Suspense를 사용하여 Routes 컴포넌트를 수정해 보겠습니다.
import React, { Suspense } from 'react';
import { Switch, browserHistory, BrowserRouter as Router, Route} from 'react-router-dom';
Import Loader from ‘./loader.js’
const Header = React.lazy( () => import('Header'));
const Footer = React.lazy( () => import(Footer));
const Sidebar = React.lazy( () => import(Sidebar));

const Routes = (props) => {
return isServerAvailable && (
<Router history={browserHistory}>
    <Suspense fallback={<Loader trigger={true} />}>
           <Switch>
             <Route path="/" exact><Redirect to='/Header’ /></Route>
             <Route path="/sidebar" exact component={props => <Sidebar {...props} />} />
             <Route path="/footer" exact component={props => <Footer {...props} />} />
      </Switch>
     </Suspense>
</Router>
}​

Stage Two

이제 애플리케이션이 안전하게 로드되었으므로 더 최적화하려면 React가 어떻게 작동하는지 알아야 합니다.
React는 호스트 트리와 호스트 인스턴스가 있는 흥미로운 프레임워크입니다.
호스트 트리는 DOM에 불과합니다. 그리고 호스트 인스턴스는 노드를 나타냅니다.
React는 호스트 환경과 애플리케이션 사이의 간격을 메우기 위한 React DOM과 함께 제공됩니다.
React DOM에서 가장 작은 항목은 javascript 객체입니다.
이러한 개체는 새 개체가 생성될 때마다 버려집니다.
왜요? 객체는 불변합니다.
변경 사항이 발생할 때마다 React는 호스트 트리를 업데이트하여 React DOM 트리와 완벽하게 일치하도록 합니다.
이 과정은 reconciliation으로 알려져 있습니다.

올바른 상태 관리 방법 사용

  • React DOM 트리가 수정될 때마다 브라우저가 강제로 리플로우됩니다. 이는 애플리케이션 성능에 심각한 영향을 미칩니다.
  • reconcilation은 라렌더링 횟수를 줄이는 데 사용됩니다.
  • 마찬가지로 React는 상태 관리를 사용하여 리렌더링을 방지합니다.
    • 예를 들어 useState() 후크가 있습니다.
    • 클래스 구성 요소를 빌드하는 경우 shouldComponentUpdate() 수명 주기 메서드를 사용합니다.
      • 항상 PureComponent를 확장하는 클래스를 생성하십시오.
      • 그리고 shouldComponentUpdate() 후크는 PureComponent에서 구현되어야 합니다.
      • 이렇게 하면 state와 props 간에 얕은 비교가 발생합니다.
      • 따라서 다시 렌더링할 가능성이 크게 줄어듭니다.

React.Memo를 활용하세요

React.Memo는 컴포넌트를 파라미터로 받아 props를 메모합니다. 컴포넌트를 다시 렌더링해야 하는 경우 얕은 비교가 수행됩니다.
이 방법은 성능상의 이유로 널리 사용됩니다.
function MyComponent(props) {
}
function areEqual(prevProps, nextProps) {
  /*
  return true if passing nextProps to render would return
  the same result as passing prevProps to render,
  otherwise return false
  */
}
export default React.memo(MyComponent, areEqual);​

 

함수 컴포넌트를 사용하는 경우 useCallback() 및 useMemo() 를 사용하십시오.

Conclusion

 

이제 크리티컬 렌더링 패스가 무엇인지 알았으니, 작성한 코드를 분석해 보세요

프로젝트에 포함된 모든 라인, 모든 리소스 및 모든 파일은 크리티컬 렌더링 패스에 추가됩니다.

또한 웹 페이지의 폴드된 내용에 대해 생각하십시오.

웹사이트 성능 향상을 위한 팁과 요령을 활용하지 않았다면, 지금이 시작하기에 가장 좋은 시기일 수 있습니다.

성능은 모든 웹 애플리케이션에서 매우 중요합니다.

복잡성과 크기가 증가함에 따라 밀리초마다 차이가 발생합니다.

그럼에도 불구하고 조기 최적화는 재앙이 될 수 있음을 기억하십시오.

항상 성능을 측정한 다음 최적화 작업을 시도하십시오.

 
반응형