[편집자주] 사실 웹페이지 렌더링에 대한 글은 많지만 관련 정보가 흩어져 있고 논의가 완전하지 않습니다. 이 주제에 대한 일반적인 이해를 갖고 싶다면 여전히 많은 것을 배워야 합니다. 따라서 웹 개발자 Alexander Skutin은 기사를 작성하기로 결정했습니다. 그는 이 기사가 초보자에게 도움이 될 뿐만 아니라 지식 구조를 새롭게 바꾸고 싶은 고급 프런트엔드 개발자에게도 도움이 될 수 있다고 믿습니다. 원래 주소는
번역은 다음과 같습니다.
웹 페이지 렌더링은 매우 초기 단계에서 수행되어야 하며 페이지 레이아웃이 막 완성된 직후일 수 있습니다. 스타일과 스크립트는 웹 페이지 렌더링에 중요한 영향을 미치기 때문입니다. 따라서 전문 개발자는 실제로 성능 문제가 발생하지 않도록 하려면 몇 가지 기술을 알아야 합니다.
이 글에서는 브라우저 내부의 자세한 메커니즘을 연구하지는 않지만 몇 가지 일반적인 규칙을 제안합니다. 결국, 다양한 브라우저 엔진은 다르게 작동하므로 브라우저 기능에 대한 개발자의 연구는 더욱 복잡해집니다.
브라우저는 어떻게 웹페이지 렌더링을 완료하나요?
먼저 웹 페이지를 렌더링할 때 브라우저의 동작을 검토해 보겠습니다.
서버 측에서 HTML 코드를 기반으로 DOM(문서 개체 모델)을 구성합니다.
스타일을 로드하고 구문 분석하여 CSS 개체 모델을 구성합니다.
문서 개체 모델과 CSS 개체 모델 위에 렌더링할 개체 집합으로 구성된 렌더링 트리를 만듭니다(Webkit에서는 이러한 개체를 렌더러 또는 렌더링 개체라고 하며 Gecko에서는 " 프레임").) 렌더링 트리는 문서 객체 모델의 구조를 반영하지만 태그나 display:none 속성과 같은 보이지 않는 요소를 포함하지 않습니다. 렌더 트리에서 각 텍스트 문자열은 독립적인 렌더러로 나타납니다. 각 렌더링 개체에는 해당 DOM 개체 또는 텍스트 블록과 계산된 스타일이 포함되어 있습니다. 즉, 렌더 트리는 문서 개체 모델을 시각적으로 표현한 것입니다.
렌더링 트리의 각 요소에 대해 레이아웃이라고 하는 좌표를 계산합니다. 브라우저는 흐름 접근 방식을 사용하여 한 번의 패스로 요소를 배치하지만 테이블 요소에는 여러 번의 패스가 필요합니다.
마지막으로 렌더링 트리의 요소가 브라우저에 표시됩니다. 이 프로세스를 "페인팅"이라고 합니다.
사용자가 웹페이지와 상호작용하거나 스크립트 프로그램이 웹페이지를 변경 및 수정하는 경우 웹페이지의 내부 구조가 변경되었기 때문에 위에서 언급한 작업 중 일부가 반복적으로 수행됩니다.
다시 그리기
배경색(배경색), 테두리색(테두리색) 등 웹페이지 내 요소의 위치에 영향을 주지 않는 요소의 스타일을 변경할 때 , 가시성( Visibility), 브라우저는 새 스타일로 요소를 한 번만 다시 그립니다(이것은 스타일을 다시 그리거나 재구성하는 것입니다).
리플로우
리플로우 또는 재정렬은 변경 사항이 텍스트 내용이나 구조 또는 요소 위치에 영향을 미칠 때 발생합니다. 이러한 변경은 일반적으로 다음 이벤트에 의해 실행됩니다.
DOM 작업(요소 추가, 삭제, 수정 또는 요소 순서 변경)
양식 필드 내의 텍스트 변경을 포함한 콘텐츠 변경
CSS 속성 계산 또는 변경,
스타일 시트 추가 또는 삭제,
"클래스" 속성 변경,
브라우저 창 작업(확대/축소, 스크롤 );
의사 클래스 활성화(:hover).
브라우저는 어떻게 렌더링을 최적화하나요?
브라우저에서는 변경된 요소의 영역으로 다시 그리기/재구성을 제한하기 위해 최선을 다합니다. 예를 들어 고정 또는 절대 위치가 있는 요소의 경우 크기 변경은 요소 자체와 해당 하위 요소에만 영향을 미칩니다. 그러나 정적으로 배치된 요소의 크기 변경은 모든 후속 요소의 리플로우를 트리거합니다.
또 다른 최적화 기술은 여러 JavaScript 코드 조각을 실행할 때 브라우저가 변경 사항을 캐시하도록 한 다음 코드 실행이 완료된 후 단일 패스로 변경 사항을 적용하는 것입니다. 예를 들어, 다음 코드는 리팩터링 및 다시 그리기만 트리거합니다.
var $body = $('body'); $body.css('padding', '1px'); // reflow, repaint $body.css('color', 'red'); // repaint $body.css('margin', '2px'); // reflow, repaint // only 1 reflow and repaint will actually happen
그러나 앞에서 언급한 것처럼 요소의 속성을 변경하면 강제 리플로우가 트리거됩니다. 요소의 속성에 액세스하기 위해 위의 코드 블록에 코드 줄을 추가하면 이 동작이 발생합니다.
var $body = $('body') $body.css('padding', '1px'); // 읽기 속성, 강제 리플로우 $body.css('color', 'red'); $body.css('margin', '2px');
결과적으로 리플로우가 두 번 발생합니다. 따라서 웹 페이지 성능을 최적화하려면 요소 속성에 액세스하는 작업을 그룹화해야 합니다. (JSBin에서 더 자세한 예제를 찾을 수 있습니다.)
때로는 강제 리플로우를 실행해야 하는 경우가 있습니다. 예를 들어, 동일한 속성(예: 왼쪽 여백)을 동일한 요소에 두 번 할당해야 합니다. 처음에는 애니메이션이 없는 100px로 설정되어야 합니다. 다음으로 전환 애니메이션을 통해 50px로 변경해야 합니다. 지금 당장 JSbin에서 이 예제를 따를 수 있지만 여기서는 더 자세히 다루겠습니다.
먼저 전환 효과가 있는 CSS 클래스를 만듭니다. .has-transition { -webkit-transition: margin-left 1seasy-out; -moz-transition: margin-left 1seasy-out; -transition: margin-left 1seasing-out; Transition: margin-left 1seasing-out }
그런 다음 계속: // 기본적으로 "has-transition" 클래스가 있는 요소 var $ targetElem = $('#targetElemId');
// remove the transition class $targetElem.removeClass('has-transition'); // change the property expecting the transition to be off, as the class is not there // anymore $targetElem.css('margin-left', 100); // put the transition class back $targetElem.addClass('has-transition'); // change the property $targetElem.css('margin-left', 50);
그러나 이 실행은 작동하지 않습니다. 모든 변경 사항은 캐시되어 코드 블록 끝에서만 실행됩니다. 우리에게 필요한 것은 다음과 같은 변경을 통해 달성할 수 있는 강제 재정렬입니다.
// 전환 클래스 제거 $(this).removeClass('has-transition');
// change the property $(this).css('margin-left', 100); // trigger a forced reflow, so that changes in a class/property get applied immediately $(this)[0].offsetHeight; // an example, other properties would work, too // put the transition class back $(this).addClass('has-transition'); // change the property $(this).css('margin-left', 50);
이제 코드가 예상대로 실행됩니다.
성능 최적화를 위한 실제 제안
사용 가능한 정보를 요약하여 다음과 같이 제안합니다.
유효한 HTML 및 CSS 파일을 만들고, 문서가 인코딩되었습니다. 스타일은 태그 내에 포함되어야 하며, 태그 끝에 스크립트 코드를 추가해야 합니다.
CSS 선택기를 최대한 단순화하고 최적화하고(CSS 전처리기를 사용하는 개발자는 이 최적화를 거의 균일하게 무시함) 중첩을 최소한으로 유지합니다. 다음은 CSS 선택기의 성능 순위입니다(가장 빠른 것부터 시작)
1. 识别器:#id 2. 类:.class 3. 标签:div 4. 相邻兄弟选择器:a + i 5. 父类选择器:ul> li 6. 通用选择器:* 7. 属性选择:input[type="text"] 8. 伪类和伪元素:a:hover
브라우저는 선택기를 오른쪽에서 왼쪽으로 처리하므로 가장 오른쪽 선택기가 가장 빠릅니다: #id 또는 .class: div * {...} // 나쁨 .list li {...} // 나쁨 .list-item {...} // 좋음 #list .list -item {...} // 좋음 * 1. 스크립트 코드에서 DOM 작업을 최대한 줄이세요. 재사용되는 경우 요소 속성 및 객체를 포함한 모든 것을 캐시합니다. 복잡한 작업을 수행할 때는 나중에 DOM에 추가할 수 있는 "격리된" 요소를 사용하는 것이 좋습니다(소위 "격리된" 요소는 DOM에서 분리되어 메모리에만 저장되는 요소입니다).
2. jQuery를 사용하여 요소를 선택하는 경우 jQuery 선택기 모범 사례를 따르세요.
3. 요소의 스타일을 변경하려면 "class" 속성을 수정하는 것이 효과적인 방법 중 하나입니다. 이 변경을 수행할 때 DOM 렌더링 트리의 깊이가 깊어질수록 더 좋습니다(프레젠테이션에서 로직을 분리하는 데도 도움이 됩니다).
4. 절대 위치나 고정 위치가 있는 요소에만 애니메이션 효과를 추가해 보세요.
5. 스크롤 사용 시 복잡한 호버 애니메이션을 비활성화합니다(예: 호버가 아닌 추가 클래스 추가). 독자들은 이 문제에 대한 기사를 읽을 수 있습니다.
더 자세한 내용을 알고 싶다면 다음 두 기사를 읽어보세요.
1, 브라우저는 어떻게 작동하나요?
2,렌더링: 다시 그리기, 리플로우/리레이아웃, 스타일 변경