웹 기본에서 재인쇄
객체 모델 구축
브라우저가 페이지를 렌더링하기 전에 먼저 DOM 및 CSSOM 트리를 구축해야 합니다. 따라서 우리는 HTML과 CSS가 모두 가능한 한 빨리 브라우저에 제공되도록 해야 합니다.
바이트 → 문자 → 태그 → 노드 → 객체 모델.
HTML 마크업은 DOM(문서 개체 모델)으로 변환되고, CSS 마크업은 CSSOM(CSS 개체 모델)으로 변환됩니다. DOM과 CSSOM은 독립적인 데이터 구조입니다.
Chrome DevTools Timeline은 DOM 및 CSSOM의 구성 및 처리 오버헤드를 캡처하고 검사할 수 있습니다.
Document Object Model (DOM)
<meta><link><title>Critical Path</title> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="브라우저 렌더링 페이지 방법 요약" ></div>
일부 텍스트와 이미지가 포함된 일반 HTML 페이지인데, 브라우저는 이 페이지를 어떻게 처리합니까?
HTML 파서의 트리 출력은 DOM 요소와 속성 노드로 구성됩니다. 이는 HTML 문서의 객체 설명이자 HTML 요소와 외부 세계(예: Javascript) 간의 인터페이스이기도 합니다. DOM과 태그는 거의 일대일로 대응됩니다.
변환: 브라우저는 디스크나 네트워크에서 HTML의 원시 바이트를 읽고 이를 파일의 지정된 인코딩(예: UTF-8)에 따라 개별 문자로 변환합니다.
-
Tokenizing: 브라우저는 문자열을 W3C HTML5 표준에서 지정한 다양한 토큰(예: "", "
" 및 꺾쇠 괄호 안의 기타 문자열)으로 변환합니다. 각 토큰에는 특별한 의미와 일련의 규칙이 있습니다. 어휘 분석: 방출된 토큰은 속성과 규칙을 정의하는 "객체"로 변환됩니다.
DOM 빌드: 마지막으로 HTML 태그는 서로 다른 태그 간의 관계를 정의하므로(일부 태그는 다른 태그 내에 포함됨) 생성된 객체는 원래 상위-하위 관계도 캡처하는 트리 데이터 구조 내에 연결됩니다. 마크업에 정의됨: HTML 개체는 body 개체의 부모이고, body는 paragraph 개체의 부모입니다.
전체 프로세스의 최종 출력은 페이지의 문서 객체 모델 (DOM)이며, 이는 페이지의 모든 추가 처리를 위해 브라우저에서 사용됩니다.
브라우저는 HTML 마크업을 처리할 때마다 위의 모든 단계를 완료합니다. 즉, 바이트를 문자로 변환하고, 토큰을 결정하고, 토큰을 노드로 변환한 다음 DOM 트리를 구축합니다. 이 전체 프로세스를 완료하는 데 시간이 좀 걸릴 수 있습니다. 특히 처리할 HTML이 많은 경우에는 더욱 그렇습니다.
Chrome DevTools를 열고 페이지가 로드되는 타임라인을 기록하면 이 단계를 수행하는 데 실제로 시간이 얼마나 걸리는지 확인할 수 있습니다. 위의 예에서는 HTML 바이트 묶음을 DOM 트리로 변환하는 데 약 5밀리초가 걸립니다. 페이지가 큰 경우 이 프로세스에 필요한 시간이 크게 늘어날 수 있습니다. 부드러운 애니메이션을 만들 때 브라우저가 많은 양의 HTML을 처리해야 하면 병목 현상이 쉽게 발생할 수 있습니다.
DOM 트리는 문서 마크업의 속성과 관계를 캡처하지만 렌더링 시 요소가 어떻게 보일지는 알려주지 않습니다. 그것이 CSSOM의 책임입니다.
CSSOM(CSS 개체 모델)
이 간단한 페이지의 DOM을 구축하는 과정에서 브라우저는 문서 헤드에서 외부 CSS 스타일 시트인 style.css를 참조하는 링크 태그를 발견했습니다. 페이지를 렌더링하는 데 리소스가 필요할 것으로 예상하고 즉시 리소스를 요청하고 다음을 반환합니다.
body { font-size: 16px }p { font-weight: bold }span { color: red }p span { display: none }img { float: right }
HTML 마크업(인라인) 내에서 직접 스타일을 선언할 수도 있지만 HTML과 독립적인 CSS를 사용하면 콘텐츠와 디자인을 독립적인 문제로 처리할 수 있습니다. 디자이너는 CSS를 처리하고 개발자는 HTML에 집중합니다.
HTML로 작업할 때와 마찬가지로 수신된 CSS 규칙을 브라우저가 이해하고 처리할 수 있는 것으로 변환해야 합니다. 따라서 HTML 프로세스를 반복하지만 HTML 대신 CSS의 경우:
CSS 바이트는 문자로 변환된 다음 토큰과 노드로 변환되고 마지막으로 "CSS 개체 모델"(CSSOM)이라는 항목에 연결됩니다. 트리 구조:
CSSOM에 트리 구조가 있는 이유는 무엇인가요? 페이지의 노드 개체에 대한 최종 스타일 세트를 계산할 때 브라우저는 노드에 적용되는 가장 일반적인 규칙으로 시작한 다음(예: 노드가 본문 요소의 하위인 경우 모든 본문 스타일을 적용함) 통과 계산 스타일을 반복적으로 최적화하려면 보다 구체적인 규칙을 적용합니다.
더 구체적인 설명을 위해 위의 CSSOM 트리를 예로 들어보세요. body 요소 내부의 span 태그 내에 배치된 모든 텍스트는 16픽셀의 글꼴 크기와 빨간색을 갖습니다. 글꼴 크기 지시문은 본문에서 범위까지 계단식으로 적용됩니다. 그러나 범위 태그가 단락(p) 태그의 하위 태그인 경우 해당 내용은 표시되지 않습니다.
또한 위의 트리는 완전한 CSSOM 트리가 아니며 스타일시트에서 재정의하기로 결정한 스타일만 표시합니다. 모든 브라우저는 기본 스타일 세트("사용자 에이전트 스타일"이라고도 함)를 제공합니다. 이러한 기본 스타일을 재정의하면 됩니다.
CSS 처리에 걸리는 시간을 이해하려면 DevTools에서 타임라인을 기록하고 "스타일 다시 계산" 이벤트를 찾으세요. DOM 구문 분석과 달리 타임라인에는 별도의 "CSS 구문 분석" 항목이 표시되지 않고 대신 구문 분석 및 CSSOM 트리가 캡처됩니다.
우리의 작은 스타일시트는 처리하는 데 약 0.6밀리초가 걸리며 페이지의 8개 요소에 영향을 미칩니다. 많지는 않지만 여전히 오버헤드가 발생합니다. 그런데 이 8가지 요소는 어디서 오는 걸까요? DOM과 CSSOM을 함께 묶는 것은 렌더링 트리입니다.
렌더링 트리 구성, 레이아웃 및 그리기
CSSOM 트리와 DOM 트리는 렌더링 트리로 병합된 다음 표시되는 각 요소의 레이아웃을 계산하고 이를 드로잉 프로세스로 출력하여 픽셀을 화면에 렌더링하는 데 사용됩니다. . 최적의 렌더링 성능을 달성하려면 이러한 각 단계를 최적화하는 것이 중요합니다.
브라우저는 HTML 및 CSS 입력을 기반으로 DOM 트리와 CSSOM 트리를 구축합니다. 그러나 이들은 완전히 독립적인 개체로서 문서의 다양한 측면을 포착합니다. 하나는 콘텐츠를 설명하고 다른 하나는 문서에 적용해야 하는 스타일 규칙을 설명합니다. 이 둘을 어떻게 병합하고 브라우저가 화면에 픽셀을 렌더링하도록 합니까?
DOM 트리는 CSSOM 트리와 병합되어 웹 페이지를 렌더링하는 데 필요한 노드만 포함하는 렌더링 트리를 형성합니다. 각 DOM 트리의 노드 노드를 탐색하고 CSSOM 규칙 트리에서 현재 노드의 스타일을 찾아 렌더링 트리를 생성합니다.
레이아웃은 각 개체의 정확한 위치와 크기를 계산합니다.
마지막 단계는 최종 렌더링 트리를 사용하여 픽셀을 화면에 렌더링하는 그리기입니다.
첫 번째 단계는 브라우저가 DOM과 CSSOM을 "렌더링 트리"로 병합하여 웹 페이지의 모든 visible DOM content과 모든 CSSOM 스타일 정보를 포함하도록 하는 것입니다. 각 노드의 .
렌더링 트리를 구축하기 위해 브라우저는 일반적으로 다음 작업을 완료합니다.
DOM 트리의 루트 노드부터 시작하여 표시되는 각 노드를 탐색합니다.
일부 노드는 표시되지 않으며(예: 스크립트 태그, 메타 태그 등) 렌더링된 출력에 반영되지 않기 때문에 무시됩니다.
일부 노드는 CSS를 통해 숨겨져 있으므로 렌더링 트리에서 무시됩니다. 예를 들어, "display: none" 속성은 스팬 노드에 설정되어 있으므로 렌더링 트리에 표시되지 않습니다.
遍历每个可见节点,为其找到适配的 CSSOM 规则并应用它们。从选择器的右边往左边开始匹配,也就是从CSSOM树的子节点开始往父节点匹配。
Emit visible nodes with content and their computed styles.
注: visibility: hidden 与 display: none 是不一样的。前者隐藏元素,但元素仍占据着布局空间(即将其渲染成一个空框),而后者 (display: none) 将元素从渲染树中完全移除,元素既不可见,也不是布局的组成部分。
最终输出的渲染同时包含了屏幕上的所有可见内容及其样式信息。有了渲染树,我们就可以进入“布局”阶段。
到目前为止,我们计算了哪些节点应该是可见的以及它们的计算样式,但我们尚未计算它们在设备视口内的确切位置和大小---这就是“布局”阶段,也称为“reflow”。
为弄清每个对象在网页上的确切大小和位置,浏览器从渲染树的根节点开始进行遍历。让我们考虑一个简单的实例:
<meta><title>Critial Path: Hello world!</title> <div> <div>Hello world!</div> </div>
위 웹페이지의 본문에는 두 개의 중첩된 div가 포함되어 있습니다. 첫 번째(상위) div는 노드의 표시 크기를 뷰포트 너비의 50%로 설정하고, 상위 div에 포함된 두 번째 div의 너비는 뷰포트 너비의 50%입니다. 해당 부모는 뷰포트 너비의 25%입니다.
레이아웃 프로세스의 출력은 뷰포트 내 각 요소의 정확한 위치와 크기를 정확하게 캡처하는 "상자 모델"입니다. 모든 상대적 측정값은 화면의 절대 픽셀로 변환됩니다.
마지막으로 어떤 노드가 표시되는지, 계산된 스타일 및 형상 정보가 있는지 알았으므로 이 정보를 최종 단계인 렌더 트리의 각 노드를 화면의 실제 픽셀로 변환하는 단계로 전달할 수 있습니다. 이 단계를 흔히 "페인팅" 또는 "래스터화"라고 합니다.
Chrome DevTools는 위에 언급된 세 단계 모두에서 시간 소모를 심층적으로 이해하는 데 도움이 됩니다. 원래 "hello world" 예제의 레이아웃 단계를 살펴보겠습니다.
"Layout" 이벤트는 타임라인에서 렌더 트리 구성, 위치 및 크기 계산을 캡처합니다.
레이아웃이 완료되면 브라우저 문제는 렌더링 트리를 화면의 픽셀로 변환하는 "페인트 설정" 및 "페인트" 이벤트입니다.
렌더 트리 구성, 레이아웃 및 페인팅을 수행하는 데 필요한 시간은 문서 크기, 적용된 스타일 및 문서가 실행되는 장치: 문서가 클수록 브라우저가 수행해야 하는 작업이 많아지고 스타일이 복잡할수록 그리는 데 시간이 더 걸립니다. 예를 들어 단일 색상은 그리는 데 "더 작아집니다". 그림자는 많은 것을 계산하고 렌더링하는 데 "훨씬 더 비쌉니다").
다음은 브라우저에서 수행되는 단계에 대한 간략한 개요입니다.
HTML 마크업을 처리하고 DOM 트리를 구축합니다.
CSS 마크업을 처리하고 CSSOM 트리를 구축하세요.
DOM과 CSSOM을 렌더 트리에 병합합니다.
렌더링 트리에 따라 배치하여 각 노드의 기하학적 정보를 계산합니다.
각 노드를 화면에 그립니다.
DOM 또는 CSSOM이 수정된 경우 위의 모든 단계를 다시 수행하여 화면에 다시 렌더링해야 하는 픽셀을 결정해야 합니다.
주요 렌더링 경로를 최적화하는 것은 위 순서에서 1~5단계를 수행하는 데 소요되는 총 시간을 최소화하는 프로세스입니다. 이렇게 하면 콘텐츠가 화면에 최대한 빨리 렌더링되고 그 사이의 시간도 줄어듭니다. 즉, 대화형 콘텐츠에 대해 더 높은 새로 고침 비율을 달성합니다.
렌더링 차단 CSS
기본적으로 CSS는 렌더링을 차단하는 리소스로 처리됩니다(그러나 html 구문 분석은 차단하지 않습니다). ) 이는 CSSOM이 빌드될 때까지 브라우저가 처리된 콘텐츠를 렌더링하지 않음을 의미합니다. CSS를 최소화하고, 가능한 한 빨리 제공하고, 미디어 유형과 쿼리를 활용하여 렌더링 차단을 해제하여 스크롤 없이 볼 수 있는 부분에 걸리는 시간을 줄이세요.
렌더링 트리 구성에서는 렌더링 트리를 구성하기 위해 DOM과 CSSOM이 모두 필요합니다. 이는 성능에 심각한 영향을 미칠 수 있습니다. HTML 및 CSS 은 모두 렌더링 차단 리소스입니다. DOM이 없으면 렌더링할 것이 없기 때문에 HTML은 분명히 필요하지만 CSS의 필요성은 덜 명확할 수 있습니다. CSS 렌더링을 차단하지 않고 일반 웹페이지를 렌더링하려고 하면 어떻게 되나요?
기본적으로 CSS는 렌더링 차단 리소스로 간주됩니다.
미디어 유형 및 미디어 쿼리를 통해 일부 CSS 리소스를 비렌더링 차단으로 표시할 수 있습니다.
브라우저는 차단 여부에 관계없이 모든 CSS 리소스를 다운로드합니다.
CSS가 없는 웹페이지는 사실상 사용할 수 없습니다. 따라서 브라우저는 DOM과 CSSOM이 모두 준비될 때까지 렌더링을 차단합니다.
CSS 은 렌더링 차단 리소스입니다. 첫 번째 렌더링 시간을 단축하려면 최대한 빨리 클라이언트에 다운로드해야 합니다.
웹페이지를 표시하거나 대형 모니터에 투사할 때 등 특정 조건에서만 사용되는 CSS 스타일이 있다면 어떨까요? 이러한 리소스가 렌더링을 차단하지 않는다면 좋을 것입니다.
이러한 상황은 CSS "미디어 유형" 및 "미디어 쿼리"를 통해 해결할 수 있습니다.
媒体查询由媒体类型以及零个或多个检查特定媒体特征状况的表达式组成。例如,第一个样式表声明未提供任何媒体类型或查询,因此它适用于所有情况。也就是说它始终会阻塞渲染。第二个样式表则不然,它只在打印内容时适用---或许您想重新安排布局、更改字体等等,因此在网页首次加载时,该样式表不需要阻塞渲染。最后一个样式表声明提供了由浏览器执行的“媒体查询”:符合条件时,样式表会生效,浏览器将阻塞渲染,直至样式表下载并处理完毕。
通过使用媒体查询,我们可以根据特定用例(比如显示或打印),也可以根据动态情况(比如屏幕方向变化、尺寸调整事件等)定制外观。声明样式表时,请密切注意媒体类型和查询,因为它们将严重影响关键渲染路径的性能。
让我们考虑下面这些实例:
第一个声明阻塞渲染,适用于所有情况。
第二个声明同样阻塞渲染:“all”是默认类型,和第一个声明实际上是等效的。
第三个声明具有动态媒体查询,将在网页加载时计算。根据网页加载时设备的方向,portrait.css 可能阻塞渲染,也可能不阻塞渲染。
最后一个声明只在打印网页时应用,因此网页在浏览器中加载时,不会阻塞渲染。
最后,“阻塞渲染”仅是指浏览器是否需要暂停网页的首次渲染,直至该资源准备就绪。无论媒寻是否命中,浏览器都会下载上述所有的CSS样式表,只不过不阻塞渲染的资源对当前媒体不生效罢了。
使用 JavaScript 添加交互
JavaScript 允许我们修改网页的方方面面:内容、样式以及它如何响应用户交互。不过,JavaScript 也会阻止 DOM 构建和延缓网页渲染。为了实现最佳性能,可以让 JavaScript 异步执行,并去除关键渲染路径中任何不必要的 JavaScript。
JavaScript 可以查询和修改 DOM 与 CSSOM。
JavaScript的 执行会阻止 CSSOM的构建,所以和CSSOM的构建是互斥的。
JavaScript blocks DOM construction unless explicitly declared as async.
JavaScript 是一种运行在浏览器中的动态语言,它允许对网页行为的几乎每一个方面进行修改:可以通过在 DOM 树中添加和移除元素来修改内容;可以修改每个元素的 CSSOM 属性;可以处理用户输入等等。为进行说明,让我们用一个简单的内联脚本对之前的“Hello World”示例进行扩展:
<meta><link><title>Critical Path: Script</title><style> body { font-size: 16px };p { font-weight: bold }; span { color: red };p span { display: none }; img { float: right }</style> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="브라우저 렌더링 페이지 방법 요약" ></div><script> var span = document.getElementsByTagName('span')[0]; span.textContent = 'interactive'; // change DOM text content span.style.display = 'inline'; // change CSSOM property // create a new element, style it, and append it to the DOM var loadTime = document.createElement('div'); loadTime.textContent = 'You loaded this page on: ' + new Date(); loadTime.style.color = 'blue'; document.body.appendChild(loadTime);</script>
JavaScript 允许我们进入 DOM 并获取对隐藏的 span 节点的引用 -- 该节点可能未出现在渲染树中,却仍然存在于 DOM 内。然后,在获得引用后,就可以更改其文本,并将 display 样式属性从“none”替换为“inline”。现在,页面显示“Hello interactive students!”。
JavaScript 还允许我们在 DOM 中创建、样式化、追加和移除新元素。从技术上讲,整个页面可以是一个大的 JavaScript 文件,此文件逐一创建元素并对其进行样式化。但是在实践中,使用 HTML 和 CSS 要简单得多。
尽管 JavaScript 为我们带来了许多功能,不过也在页面渲染方式和时间方面施加了更多限制。
首先,请注意上例中的内联脚本靠近网页底部。为什么呢?如果我们将脚本移至 span元素前面,就会脚本运行失败,并提示在文档中找不到对任何span 元素的引用 -- 即 getElementsByTagName(‘span') 会返回 null。这透露出一个重要事实:脚本在文档的何处插入,就在何处执行。当 HTML 解析器遇到一个 script 标记时,它会暂停构建 DOM,将控制权移交给 JavaScript 引擎;等 JavaScript 引擎运行完毕,浏览器会从中断的地方恢复 DOM 构建。
换言之,我们的脚本块在运行时找不到网页中任何靠后的元素,因为它们尚未被处理!或者说:执行内联脚本会阻止 DOM 构建,也就延缓了首次渲染。
在网页中引入脚本的另一个微妙事实是,它们不仅可以读取和修改 DOM 属性,还可以读取和修改 CSSOM 属性。实际上,示例中就是这么做的:将 span 元素的 display 属性从 none 更改为 inline。最终结果如何?我们现在遇到了race condition(资源竞争)。
如果浏览器尚未完成 CSSOM 的下载和构建,而却想在此时运行脚本,会怎样?答案很简单,对性能不利:浏览器将延迟脚本执行和 DOM 构建,直至其完成 CSSOM 的下载和构建。
简言之,JavaScript 在 DOM、CSSOM 和 JavaScript 执行之间引入了大量新的依赖关系,从而可能导致浏览器在处理以及在屏幕上渲染网页时出现大幅延迟:
脚本在文档中的位置很重要。
当浏览器遇到一个 script 标记时,DOM 构建将暂停,直至脚本完成执行。
JavaScript 可以查询和修改 DOM 与 CSSOM。
JavaScript 执行将暂停,直至 CSSOM 就绪。即CSSDOM构建的优先级更高。
“优化关键渲染路径”在很大程度上是指了解和优化 HTML、CSS 和 JavaScript 之间的依赖关系谱。
解析器阻塞与异步 JavaScript
默认情况下,JavaScript 执行会“阻塞解析器”:当浏览器遇到文档中的脚本时,它必须暂停 DOM 构建,将控制权移交给 JavaScript 运行时,让脚本执行完毕,然后再继续构建 DOM。实际上,内联脚本始终会阻止解析器,除非编写额外代码来推迟它们的执行。
通过 script 标签引入的脚本又怎样:
<meta><link><title>Critical Path: Script External</title> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="브라우저 렌더링 페이지 방법 요약" ></div><script></script>
app.js
var span = document.getElementsByTagName('span')[0]; span.textContent = 'interactive'; // change DOM text contentspan.style.display = 'inline'; // change CSSOM property// create a new element, style it, and append it to the DOMvar loadTime = document.createElement('div'); loadTime.textContent = 'You loaded this page on: ' + new Date(); loadTime.style.color = 'blue'; document.body.appendChild(loadTime);
无论我们使用 <script> 标记还是内联 JavaScript 代码段,两者能够以相同方式工作。 <strong>在两种情况下,浏览器都会先暂停并执行脚本,然后才会处理剩余文档。如果是外部<strong> JavaScript <strong>文件,浏览器必须停下来,等待从磁盘、缓存或远程服务器获取脚本,这就可能给关键渲染路径增加更长的延迟。</script>
默认情况下,所有 JavaScript 都会阻止解析器。由于浏览器不了解脚本计划在页面上执行什么操作,它会作最坏的假设并阻止解析器。向浏览器传递脚本不需要在引用位置执行的信号既可以让浏览器继续构建 DOM,也能够让脚本在就绪后执行。为此,我们可以将脚本标记为异步:
<meta> <link> <title>Critical Path: Script Async</title> <p>Hello <span>web performance</span> students!</p> <div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="브라우저 렌더링 페이지 방법 요약" ></div> <script></script>
向 script 标记添加异步关键字可以指示浏览器在等待脚本可用期间(仅指下载期间,因为所有脚本的执行都会阻塞解析器)不阻止 DOM 构建,这样可以显著提升性能。
分析关键渲染路径性能
发现和解决关键渲染路径性能瓶颈需要充分了解常见的陷阱。让我们踏上实践之旅,找出常见的性能模式,从而帮助您优化网页。
优化关键渲染路径能够让浏览器尽可能快地绘制网页:更快的网页渲染速度可以提高吸引力、增加网页浏览量以及提高转化率。为了最大程度减少访客看到空白屏幕的时间,我们需要优化加载的资源及其加载顺序。
为帮助说明这一流程,让我们先从可能的最简单情况入手,逐步构建我们的网页,使其包含更多资源、样式和应用逻辑。在此过程中,我们还会对每一种情况进行优化,以及了解可能出错的环节。
到目前为止,我们只关注了资源(CSS、JS 或 HTML 文件)可供处理后浏览器中会发生的情况,而忽略了从缓存或从网络获取资源所需的时间。我们作以下假设:
到服务器的网络往返(传播延迟时间)需要 100 毫秒。
HTML 文档的服务器响应时间为 100 毫秒,所有其他文件的服务器响应时间均为 10 毫秒。
Hello World 体验
<meta><title>Critical Path: No Style</title> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="브라우저 렌더링 페이지 방법 요약" ></div>
我们将从基本 HTML 标记和单个图像(无 CSS 或 JavaScript)开始。让我们在 Chrome DevTools 中打开 Network 时间线并检查生成的资源瀑布:
正如预期的一样,HTML 文件下载花费了大约 200 毫秒。请注意,蓝线的透明部分表示浏览器在网络上等待(即尚未收到任何响应字节)的时间,而不透明部分表示的是收到第一批响应字节后完成下载的时间。HTML 下载量很小 (
当 HTML 内容可用后,浏览器会解析字节,将它们转换成tokens,然后构建 DOM 树。请注意,为方便起见,DevTools 会在底部记录 DOMContentLoaded 事件的时间(216 毫秒),该时间同样与蓝色垂直线相符。HTML 下载结束与蓝色垂直线 (DOMContentLoaded) 之间的间隔是浏览器构建 DOM 树所花费的时间 — 在本例中仅为几毫秒。
请注意,我们的“趣照”并未阻止 domContentLoaded 事件。这证明,我们构建渲染树甚至绘制网页时无需等待页面上的每个静态资源:并非所有资源都对快速提供首次绘制具有关键作用。事实上,当我们谈论关键渲染路径时,通常谈论的是 HTML 标记、CSS 和 JavaScript。图像不会阻止页面的首次渲染,不过,我们当然也应该尽力确保系统尽快绘制图像!
That said, the load event (also known as onload), is blocked on the image: DevTools reports the onload event at 335ms. Recall that the onload event marks the point at which all resources that the page requires have been downloaded and processed; at this point (the red vertical line in the waterfall), the loading spinner can stop spinning in the browser.
结合使用 JavaScript 和 CSS
“Hello World experience”页面虽然看起来简单,但背后却需要做很多工作。在实践中,我们还需要 HTML 之外的其他资源:我们可能需要 CSS 样式表以及一个或多个用于为网页增加一定交互性的脚本。让我们将两者结合使用,看看效果如何:
<title>Critical Path: Measure Script</title><meta><link> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="브라우저 렌더링 페이지 방법 요약" ></div><script></script>
添加 JavaScript 和 CSS 之前:
添加 JavaScript 和 CSS 之后:
添加外部 CSS 和 JavaScript 文件将额外增加两个瀑布请求,浏览器差不多会同时发出这两个请求。不过,请注意,现在 domContentLoaded 事件与 onload 事件之间的时间差小多了。这是怎么回事?
与纯 HTML 示例不同,我们还需要获取并解析 CSS 文件才能构建 CSSOM,要想构建渲染树,DOM 和 CSSOM 缺一不可。
由于网页上还有一个阻塞解析器的JavaScript 文件,系统会在下载并解析 CSS 文件之前阻止 domContentLoaded事件:因为 JavaScript 可能会查询 CSSOM,必须在下载 CSS 文件之后才能执行 JavaScript。
如果我们用内联脚本替换外部脚本会怎样?即使直接将脚本内联到网页中,浏览器仍然无法在构建 CSSOM 之前执行脚本。简言之,内联 JavaScript 也会阻止解析器。
不过,尽管内联脚本会阻止 CSS,但这样做是否能加快页面渲染速度呢?让我们尝试一下,看看会发生什么。
外部 JavaScript:
内联 JavaScript:
我们减少了一个请求,但 onload 和 domContentLoaded 时间实际上没有变化。为什么呢?怎么说呢,我们知道,这与 JavaScript 是内联的还是外部的并无关系,因为只要浏览器遇到 script 标记,就会进行阻止,并等到之前的css文件的 CSSOM 构建完毕。此外,在我们的第一个示例中,浏览器是并行下载 CSS 和 JavaScript,并且差不多是同时完成。在此实例中,内联 JavaScript 代码并无多大意义。但是,我们可以通过多种策略加快网页的渲染速度。
首先回想一下,所有内联脚本都会阻止解析器,但对于外部脚本,可以添加“async”关键字来解除对解析器的阻止。让我们撤消内联,尝试一下这种方法:
<title>Critical Path: Measure Async</title><meta><link> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="브라우저 렌더링 페이지 방법 요약" ></div><script></script>
阻止解析器的(外部)JavaScript:
异步(外部)JavaScript:
效果好多了!解析 HTML 之后不久即会触发 domContentLoaded 事件;浏览器已得知不要阻止 JavaScript,并且由于没有其他阻止解析器的脚本,CSSOM 构建也可并行进行了。
或者,我们也可以同时内联 CSS 和 JavaScript:
<title>Critical Path: Measure Inlined</title><meta><style> p { font-weight: bold } span { color: red } p span { display: none } img { float: right }</style> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="브라우저 렌더링 페이지 방법 요약" ></div><script> var span = document.getElementsByTagName('span')[0]; span.textContent = 'interactive'; // change DOM text content span.style.display = 'inline'; // change CSSOM property // create a new element, style it, and append it to the DOM var loadTime = document.createElement('div'); loadTime.textContent = 'You loaded this page on: ' + new Date(); loadTime.style.color = 'blue'; document.body.appendChild(loadTime);</script>
请注意,domContentLoaded 时间与前一示例中的时间实际上相同;只不过没有将 JavaScript 标记为异步,而是同时将 CSS 和 JS 内联到网页本身。这会使 HTML 页面显著增大,但好处是浏览器无需等待获取任何外部资源,网页已经内置了所有资源。
即便是非常简单的网页,优化关键渲染路径也并非轻而易举:需要了解不同资源之间的依赖关系图,需要确定哪些资源是“关键资源”,还必须在不同策略中做出选择,找到在网页上加入这些资源的恰当方式。这一问题不是一个解决方案能够解决的,每个页面都不尽相同。您需要遵循相似的流程,自行找到最佳策略。
不过,我们可以回过头来,看看能否找出某些常规性能模式。
性能模式
最简单的网页只包括 HTML 标记;没有 CSS,没有 JavaScript,也没有其他类型的资源。要渲染此类网页,浏览器需要发起请求,等待 HTML 文档到达,对其进行解析,构建 DOM,最后将其渲染在屏幕上:
<meta><title>Critical Path: No Style</title> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="브라우저 렌더링 페이지 방법 요약" ></div>
T0 与 T1 之间的时间捕获的是网络和服务器处理时间。在最理想的情况下(如果 HTML 文件较小),我们只需一次网络往返便可获取整个文档。由于 TCP 传输协议工作方式的缘故,较大文件可能需要更多次的往返。因此,在最理想的情况下,上述网页具有单次往返(最少)关键渲染路径。
现在,我们还以同一网页为例,但这次使用外部 CSS 文件:
<meta><link> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="브라우저 렌더링 페이지 방법 요약" ></div>
我们同样需要一次网络往返来获取 HTML 文档,然后检索到的标记告诉我们还需要 CSS 文件;这意味着,浏览器需要返回服务器并获取 CSS,然后才能在屏幕上渲染网页。因此,这个页面至少需要两次往返才能显示出来。CSS 文件同样可能需要多次往返,因此重点在于“最少”。
让我们定义一下用来描述关键渲染路径的词汇:
关键资源: 可能阻止网页首次渲染的资源。
关键路径长度: 获取所有关键资源所需的往返次数或总时间。
关键字节: 实现网页首次渲染所需的总字节数,它是所有关键资源传送文件大小的总和。我们包含单个 HTML 页面的第一个示例包含一项关键资源(HTML 文档);关键路径长度也与 1 次网络往返相等(假设文件较小),而总关键字节数正好是 HTML 文档本身的传送大小。
现在,让我们将其与上面 HTML + CSS 示例的关键路径特性对比一下:
2 项关键资源
2 次或更多次往返的最短关键路径长度
9 KB 的关键字节
我们同时需要 HTML 和 CSS 来构建渲染树。所以,HTML 和 CSS 都是关键资源:CSS 仅在浏览器获取 HTML 文档后才会获取,因此关键路径长度至少为两次往返。两项资源相加共计 9KB 的关键字节。
现在,让我们向组合内额外添加一个 JavaScript 文件。
<meta><link> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="브라우저 렌더링 페이지 방법 요약" ></div><script></script>
我们添加了 app.js,它既是网页上的外部 JavaScript 静态资源,又是一种解析器阻止(即关键)资源。更糟糕的是,为了执行 JavaScript 文件,我们还需要进行阻塞并等待 CSSOM;因为JavaScript 可以查询 CSSOM,因此在下载 style.css 并构建 CSSOM 之前,浏览器将会暂停解析。
即便如此,如果我们实际查看一下该网页的“网络瀑布”,就会注意到 CSS 和 JavaScript 请求差不多是同时发起的;浏览器获取 HTML,发现两项资源并发起两个请求。因此,上述网页具有以下关键路径特性:
3 项关键资源
2 次或更多次往返的最短关键路径长度
11 KB 的关键字节
现在,我们拥有了三项关键资源,关键字节总计达 11 KB,但我们的关键路径长度仍是两次往返,因为我们可以同时传送 CSS 和 JavaScript。了解关键渲染路径的特性意味着能够确定哪些是关键资源,此外还能了解浏览器如何安排资源的获取时间。让我们继续探讨示例。
在与网站开发者交流后,我们意识到我们在网页上加入的 JavaScript 不必具有阻塞作用:网页中的一些分析代码和其他代码不需要阻止网页的渲染。了解了这一点,我们就可以向 script 标记添加“async”属性来解除对解析器的阻止:
<meta><link> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="브라우저 렌더링 페이지 방법 요약" ></div><script></script>
异步脚本具有以下几个优点:
脚本不再阻止解析器,也不再是关键渲染路径的组成部分。
由于没有其他关键脚本,CSS 也不需要阻止 domContentLoaded 事件。
domContentLoaded 事件触发得越早,其他应用逻辑开始执行的时间就越早。
因此,我们优化过的网页现在恢复到了具有两项关键资源(HTML 和 CSS),最短关键路径长度为两次往返,总关键字节数为 9 KB。
最后,如果 CSS 样式表只需用于打印,那会如何呢?
<meta><link> <p>Hello <span>web performance</span> students!</p><div><img src="/static/imghwm/default1.png" data-src="awesome-photo.jpg" class="lazy" alt="브라우저 렌더링 페이지 방법 요약" ></div><script></script>
style.css 리소스는 인쇄에만 사용되기 때문에 웹 페이지를 렌더링하기 위해 브라우저가 이를 차단할 필요가 없습니다. 따라서 DOM이 구성되면 브라우저는 웹 페이지를 렌더링하는 데 충분한 정보를 갖게 됩니다. 따라서 페이지에는 단 하나의 중요 리소스(HTML 문서)만 있고 가장 짧은 중요 렌더링 경로 길이는 1회 왕복입니다.
요약:
기본적으로 CSS는 렌더링 차단 리소스로 처리됩니다. 즉, CSSOM이 구성될 때까지 브라우저는 처리된 콘텐츠를 렌더링하지 않습니다. html과 css는 모두 렌더링을 차단하는 리소스입니다. DOM과 CSSDOM을 최대한 빨리 구축해야만 첫 번째 화면을 최대한 빨리 표시할 수 있습니다. 그러나 CSS 구문 분석과 HTML 구문 분석은 동시에 수행될 수 있습니다.
HTML 파서가 스크립트 태그를 발견하면 DOM 구축을 일시 중지하고 js 파일을 다운로드한 다음(외부/인라인/캐시에서) 제어권을 JavaScript 엔진으로 전송합니다(스크립트가 이 시간 요소에서 참조하는 경우, 참조 오류가 발생합니다.) JavaScript 엔진 실행이 끝나면 브라우저는 중단된 부분부터 DOM 구성을 다시 시작합니다. 즉, 페이지에 스크립트 태그가 있는 경우 DOMContentLoaded 이벤트는 트리거되기 전에 JS가 실행될 때까지 기다려야 합니다. 그러나 스크립트는 비동기식으로 표시될 수 있으며, 이는 js 파일을 다운로드하는 동안 DOM 구성을 차단하지 않습니다.
defer 및 async는 모두 js 파일을 비동기적으로 다운로드하지만 차이점이 있습니다.
defer 속성은 IE에서만 지원되며 이 속성이 있는 스크립트는 페이지가 구문 분석된 후에 실행되며 지연된 스크립트가 반드시 순서대로 실행되는 것은 아닙니다.
async js는 다운로드 후 바로 실행됩니다(따라서 스크립트가 실행되는 순서는 코드에 있는 스크립트의 순서가 아닙니다. 나중에 나타나는 스크립트가 먼저 로드되어 먼저 실행될 수도 있습니다).
비동기 리소스는 파서를 차단하지 않으므로 브라우저가 스크립트를 실행하기 전에 CSSOM 구성에서 차단되는 것을 방지할 수 있습니다. 일반적으로 스크립트에서 async 특성을 사용할 수 있는 경우(첫 번째 렌더링에는 필요하지 않음) 첫 번째 렌더링 후에 스크립트를 비동기식으로 로드하는 것이 좋습니다.
Race Condition
브라우저에서 다운로드가 완료되지 않았고 스크립트를 실행하려고 할 때 CSSOM을 빌드합니까? 대답은 간단하지만 그렇지 않습니다. 성능에 매우 좋음: 브라우저가 스크립트 실행 및 DOM을 지연시킵니다. 즉, 스크립트 태그의 JS는 실행 전에 CSS가 로드될 때까지 기다려야 합니다.
HTML 파서는 DOM 트리를 어떻게 구축하나요? DOM 트리와 html 태그 사이에는 일대일 대응이 있습니다. html을 위에서 아래로 구문 분석하면 구문 분석하는 동안 DOM이 구성됩니다. 외부 리소스(링크 또는 스크립트)가 발견되면 외부 리소스가 로드됩니다. 외부 리소스가 js인 경우 HTML 구문 분석은 일시 중지되고 js가 로드되어 실행될 때까지 계속됩니다. 외부 리소스가 CSS인 경우 html 구문 분석에는 영향을 미치지 않지만 첫 화면 렌더링에는 영향을 미칩니다.
domContentLoaded: CSS 파일, 이미지 및 iframe이 로드될 때까지 기다리지 않고 초기 HTML 문서가 로드되어 DOM 트리로 구문 분석될 때 트리거됩니다.
load: 페이지에 필요한 모든 리소스(이미지 포함)가 다운로드되어 처리되었을 때 동적으로 얻은 리소스는 로드 이벤트와 관련이 없습니다.
위 내용은 브라우저 렌더링 페이지 방법 요약의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

Python 및 JavaScript의 미래 추세에는 다음이 포함됩니다. 1. Python은 과학 컴퓨팅 분야에서의 위치를 통합하고 AI, 2. JavaScript는 웹 기술의 개발을 촉진하고, 3. 교차 플랫폼 개발이 핫한 주제가되고 4. 성능 최적화가 중점을 둘 것입니다. 둘 다 해당 분야에서 응용 프로그램 시나리오를 계속 확장하고 성능이 더 많은 혁신을 일으킬 것입니다.

개발 환경에서 Python과 JavaScript의 선택이 모두 중요합니다. 1) Python의 개발 환경에는 Pycharm, Jupyternotebook 및 Anaconda가 포함되어 있으며 데이터 과학 및 빠른 프로토 타이핑에 적합합니다. 2) JavaScript의 개발 환경에는 Node.js, VScode 및 Webpack이 포함되어 있으며 프론트 엔드 및 백엔드 개발에 적합합니다. 프로젝트 요구에 따라 올바른 도구를 선택하면 개발 효율성과 프로젝트 성공률이 향상 될 수 있습니다.

예, JavaScript의 엔진 코어는 C로 작성되었습니다. 1) C 언어는 효율적인 성능과 기본 제어를 제공하며, 이는 JavaScript 엔진 개발에 적합합니다. 2) V8 엔진을 예를 들어, 핵심은 C로 작성되며 C의 효율성 및 객체 지향적 특성을 결합하여 C로 작성됩니다.

JavaScript는 웹 페이지의 상호 작용과 역학을 향상시키기 때문에 현대 웹 사이트의 핵심입니다. 1) 페이지를 새로 고치지 않고 콘텐츠를 변경할 수 있습니다. 2) Domapi를 통해 웹 페이지 조작, 3) 애니메이션 및 드래그 앤 드롭과 같은 복잡한 대화식 효과를 지원합니다. 4) 성능 및 모범 사례를 최적화하여 사용자 경험을 향상시킵니다.

C 및 JavaScript는 WebAssembly를 통한 상호 운용성을 달성합니다. 1) C 코드는 WebAssembly 모듈로 컴파일되어 컴퓨팅 전력을 향상시키기 위해 JavaScript 환경에 도입됩니다. 2) 게임 개발에서 C는 물리 엔진 및 그래픽 렌더링을 처리하며 JavaScript는 게임 로직 및 사용자 인터페이스를 담당합니다.

JavaScript는 웹 사이트, 모바일 응용 프로그램, 데스크탑 응용 프로그램 및 서버 측 프로그래밍에서 널리 사용됩니다. 1) 웹 사이트 개발에서 JavaScript는 HTML 및 CSS와 함께 DOM을 운영하여 동적 효과를 달성하고 jQuery 및 React와 같은 프레임 워크를 지원합니다. 2) 반응 및 이온 성을 통해 JavaScript는 크로스 플랫폼 모바일 애플리케이션을 개발하는 데 사용됩니다. 3) 전자 프레임 워크를 사용하면 JavaScript가 데스크탑 애플리케이션을 구축 할 수 있습니다. 4) node.js는 JavaScript가 서버 측에서 실행되도록하고 동시 요청이 높은 높은 요청을 지원합니다.

Python은 데이터 과학 및 자동화에 더 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 더 적합합니다. 1. Python은 데이터 처리 및 모델링을 위해 Numpy 및 Pandas와 같은 라이브러리를 사용하여 데이터 과학 및 기계 학습에서 잘 수행됩니다. 2. 파이썬은 간결하고 자동화 및 스크립팅이 효율적입니다. 3. JavaScript는 프론트 엔드 개발에 없어서는 안될 것이며 동적 웹 페이지 및 단일 페이지 응용 프로그램을 구축하는 데 사용됩니다. 4. JavaScript는 Node.js를 통해 백엔드 개발에 역할을하며 전체 스택 개발을 지원합니다.

C와 C는 주로 통역사와 JIT 컴파일러를 구현하는 데 사용되는 JavaScript 엔진에서 중요한 역할을합니다. 1) C는 JavaScript 소스 코드를 구문 분석하고 추상 구문 트리를 생성하는 데 사용됩니다. 2) C는 바이트 코드 생성 및 실행을 담당합니다. 3) C는 JIT 컴파일러를 구현하고 런타임에 핫스팟 코드를 최적화하고 컴파일하며 JavaScript의 실행 효율을 크게 향상시킵니다.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

Atom Editor Mac 버전 다운로드
가장 인기 있는 오픈 소스 편집기

드림위버 CS6
시각적 웹 개발 도구

에디트플러스 중국어 크랙 버전
작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

Dreamweaver Mac版
시각적 웹 개발 도구

SublimeText3 영어 버전
권장 사항: Win 버전, 코드 프롬프트 지원!
