CSS와 JS가 DOM 구문 분석 및 렌더링을 차단하는 방법에 대해 이야기해 보겠습니다.

青灯夜游
풀어 주다: 2021-05-27 09:58:16
앞으로
2726명이 탐색했습니다.

이 글에서는 CSS와 JS가 DOM 파싱과 렌더링을 차단하는 원리를 소개합니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다.

CSS와 JS가 DOM 구문 분석 및 렌더링을 차단하는 방법에 대해 이야기해 보겠습니다.

안녕하세요~ 독자 여러분, 안녕하세요. CSS를 머리 부분에, JS를 아래쪽에 배치하면 페이지 성능이 향상된다는 이야기를 다들 들어보셨을 것입니다. 그러나 왜? 고려해 보셨나요? 오랫동안 알고 있었지만 왜인지는 몰랐습니다. 물론 시험을 위해 외워도 괜찮았지만, 실제 적용에서는 엉망이 되더군요. 그래서 씻어(왕) 심장(양) 가죽(부) 얼굴(라오)을 정리하고 최근 결과를 정리합니다. CSS放头部,JS放底部,这样可以提高页面的性能。然而,为什么呢?大家有考虑过么?很长一段时间,我都是知其然而不知其所以然,强行背下来应付考核当然可以,但实际应用中必然一塌糊涂。因此洗(wang)心(yang)革(bu)面(lao),小结一下最近玩出来的成果。

友情提示,本文也是小白向为主,如果直接想看结论可以拉到最下面看的~


由于关系到文件的读取,那是肯定需要服务器的,我会把全部的文件放在github上,给我点个 star 我会开心!

node端唯一需要解释一下的是这个函数:

function sleep(time) {
  return new Promise(function(res) {
    setTimeout(() => {
      res()
    }, time);
  })
}
로그인 후 복사

嗯!其实就延时啦。如果CSS或者JS文件名有sleep3000之类的前缀时,意思就是延迟3000毫秒才会返回这文件。

下文使用的HTML文件是长这样的:

    <!DOCTYPE html>
    <html>
    <head>
     <meta charset="UTF-8">
     <title>Title</title>
     <style>
     div {
     width: 100px;
     height: 100px;
     background: lightgreen;
     }
     </style>
    </head>
    <body>
     <div></div>
    </body>
    </html>
로그인 후 복사

我会在其中插入不同的JSCSS

而使用的common.css,不论有没有前缀,内容都是这样的:

div {
  background: lightblue;
}
로그인 후 복사

好了,话不多数,开始正文!

CSS

关于CSS,大家肯定都知道的是<link>标签放在头部性能会高一点,少一点人知道如果<script><link>同时在头部的话,<script>在上可能会更好。这是为什么呢?下面我们一起来看一下CSSDOM的影响是什么。

CSS 不会阻塞 DOM 的解析

注意哦!这里说的是DOM 解析,证明的例子如下,首先在头部插入<script defer src="/js/logDiv.js"></script>JS文件的内容是:

const div = document.querySelector(&#39;div&#39;);
console.log(div);
로그인 후 복사

defer属性相信大家也很熟悉了,MDN对此的描述是用来通知浏览器该脚本将在文档完成解析后,触发 DOMContentLoaded 事件前执行。设置这个属性,能保证DOM解析后马上打印出div

之后将<link rel="stylesheet" href="/css/sleep3000-common.css">插入HTML文件的任一位置,打开浏览器,可以看到是首先打印出div这个DOM节点,过3s左右之后才渲染出一个浅蓝色的div。这就证明了CSS 是不会阻塞 DOM 的解析的,尽管CSS下载需要3s,但这个过程中,浏览器不会傻等着CSS下载完,而是会解析DOM的。

这里简单说一下,浏览器是解析DOM生成DOM Tree,结合CSS生成的CSS Tree,最终组成render tree,再渲染页面。由此可见,在此过程中CSS完全无法影响DOM Tree,因而无需阻塞DOM解析。然而,DOM TreeCSS Tree会组合成render tree,那CSS会不会页面阻塞渲染呢?

CSS 阻塞页面渲染

其实这一点,刚才的例子已经说明了,如果CSS 不会阻塞页面阻塞渲染,那么CSS文件下载之前,浏览器就会渲染出一个浅绿色的div,之后再变成浅蓝色。浏览器的这个策略其实很明智的,想象一下,如果没有这个策略,页面首先会呈现出一个原始的模样,待CSS下载完之后又突然变了一个模样。用户体验可谓极差,而且渲染是有成本的。

因此,基于性能与用户体验的考虑,浏览器会尽量减少渲染的次数,CSS顺理成章地阻塞页面渲染。

然而,事情总有奇怪的,请看这例子,HTML

주의 사항 이 글은 주로 초보들이 쓴 글입니다. 결론을 직접 보고 싶으시면 맨 아래로 스크롤 하시면 됩니다~


파일 읽기와 관련된 글이므로 반드시 서버가 필요합니다. , 모든 파일은 github, 별표를 주시면 기뻐하겠습니다! 🎜🎜node 측에서 설명이 필요한 유일한 것은 이 함수입니다: 🎜
<header>
    <link rel="stylesheet" href="/css/sleep3000-common.css">
    <script src="/js/logDiv.js"></script>
</header>
로그인 후 복사
🎜흠! 사실은 지연됐어요. CSS 또는 JS 파일 이름에 sleep3000과 같은 접두사가 있으면 파일이 3000밀리초 지연 후에 반환된다는 의미입니다. 🎜🎜아래에 사용된 HTML 파일은 다음과 같습니다. 🎜
const arr = [];
for (let i = 0; i < 10000000; i++) {
  arr.push(i);
  arr.splice(i % 3, i % 7, i % 5);
}
const div = document.querySelector(&#39;div&#39;);
console.log(div);
로그인 후 복사
로그인 후 복사
🎜여기에 다른 JSCSS를 삽입하겠습니다. 🎜🎜사용된 common.css는 접두어 유무에 관계없이 내용은 다음과 같습니다. 🎜
<body>
	<div></div>
	<script src="/js/sleep3000-logDiv.js"></script>
	<style>
		div {
			background: lightgrey;
		}
	</style>
	<script src="/js/sleep5000-logDiv.js"></script>
	<link rel="stylesheet" href="/css/common.css">
</body>
로그인 후 복사
로그인 후 복사
🎜자, 긴 말 없이 텍스트를 시작하겠습니다! 🎜

CSS

🎜CSS에 대해 모두가 알아야 할 <link>< 성능 헤더에 /code> 태그를 배치하는 경우 <script><link>가 동시에 헤더에 있다는 사실을 아는 사람은 거의 없습니다. , <script>가 더 나을 수도 있습니다. 왜 이런가요? DOMCSS가 미치는 영향을 살펴보겠습니다. 🎜

CSSDOM

의 구문 분석을 차단하지 않습니다.🎜주의! 여기서 말하는 것은 DOM 파싱입니다. 먼저 <script defer src="/js/logDiv.js"></script&gt를 삽입합니다. ;</ code>, <code>JS 파일의 내용은 다음과 같습니다. 🎜rrreee🎜defer 속성 모두가 익숙할 것이라고 생각합니다.
MDN의 설명은 브라우저에 스크립트가 DOMContentLoaded 이벤트를 트리거하기 전에 문서 구문 분석을 완료합니다. 이 속성을 설정하면 DOM이 구문 분석된 후 div가 즉시 인쇄됩니다. 🎜🎜그런 다음 <link rel="stylesheet" href="/css/sleep3000-common.css">HTML 파일의 임의 위치에 삽입하고 browser 브라우저에서 divDOM 노드가 먼저 인쇄된 다음 약 3 후에 연한 파란색 div가 렌더링되는 것을 볼 수 있습니다. 초. 이는 CSSDOM의 구문 분석을 차단하지 않는다는 것을 증명합니다. CSS를 다운로드하는 데 3초가 걸리지만 브라우저는 까지 기다리지 않습니다. >CSS가 다운로드되고 대신 DOM을 구문 분석합니다. 🎜🎜간단히 말하면 브라우저는 DOM을 구문 분석하여 DOM 트리를 생성하고 CSS를 결합하여 CSS 트리를 생성합니다. > , 마지막으로 렌더링 트리를 구성한 다음 페이지를 렌더링합니다. 이 과정에서 CSSDOM Tree에 전혀 영향을 미칠 수 없으므로 DOM 구문 분석을 차단할 필요가 없음을 알 수 있습니다. 그러나 DOM 트리CSS 트리렌더링 트리로 결합됩니다. CSS는 페이지 렌더링을 차단합니다. ? 🎜

CSS는 페이지 렌더링을 차단합니다

🎜사실 방금 예시에서는 이미 < code >CSS
는 페이지와 렌더링을 차단하지 않으므로 CSS 파일이 다운로드되기 전에 브라우저는 연한 녹색 div를 렌더링한 다음 연한 녹색이 됩니다. 이 브라우저 전략은 실제로 매우 현명합니다. 이 전략이 없으면 페이지가 먼저 원래 모양을 표시한 다음 CSS가 다운로드된 후 갑자기 모양이 바뀔 것이라고 상상해 보십시오. 사용자 경험은 매우 열악하고 렌더링 비용이 많이 듭니다. 🎜🎜따라서 성능 및 사용자 경험을 고려하여 브라우저는 렌더링 수를 줄이려고 시도하며 CSS는 자연스럽게 페이지 렌더링을 차단합니다. 🎜🎜그러나 상황은 항상 이상합니다. 이 예를 보세요. HTML 헤더 구조는 다음과 같습니다. 🎜rrreee🎜그런데 무슨 일이 일어날지 생각해 보세요. 🎜

答案是浏览器会转圈圈三秒,但此过程中不会打印任何东西,之后呈现出一个浅蓝色的div,再打印出null。结果好像是CSS不单阻塞了页面渲染,还阻塞了DOM 的解析啊!稍等,在你打算掀桌子疯狂吐槽我之前,请先思考一下是什么阻塞了DOM 的解析,刚才已经证明了CSS是不会阻塞的,那么阻塞了页面解析其实是JS!但明明JS的代码如此简单,肯定不会阻塞这么久,那就是JS在等待CSS的下载,这是为什么呢?

仔细思考一下,其实这样做是有道理的,如果脚本的内容是获取元素的样式,宽高等CSS控制的属性,浏览器是需要计算的,也就是依赖于CSS。浏览器也无法感知脚本内容到底是什么,为避免样式获取,因而只好等前面所有的样式下载完后,再执行JS。因而造成了之前例子的情况。

所以,看官大人明白为何<script><link>同时在头部的话,<script>在上可能会更好了么?之所以是可能,是因为如果<link>的内容下载更快的话,是没影响的,但反过来的话,JS就要等待了,然而这些等待的时间是完全不必要的。

JS

JS,也就是<script>标签,估计大家都很熟悉了,不就是阻塞DOM解析和渲染么。然而,其中其实还是有一点细节可以考究一下的,我们一起来好好看看。

JS 阻塞 DOM 解析

首先我们需要一个新的JS文件名为blok.js,内容如下:

const arr = [];
for (let i = 0; i < 10000000; i++) {
  arr.push(i);
  arr.splice(i % 3, i % 7, i % 5);
}
const div = document.querySelector(&#39;div&#39;);
console.log(div);
로그인 후 복사
로그인 후 복사

其实那个数组操作时没意义的,只是为了让这个JS文件多花执行时间而已。之后把这个文件插入头部,浏览器跑一下。

结果估计大家也能想象得到,浏览器转圈圈一会,这过程中不会有任何东西出现。之后打印出null,再出现一个浅绿色的div。现象就足以说明JS 阻塞 DOM 解析了。其实原因也很好理解,浏览器并不知道脚本的内容是什么,如果先行解析下面的DOM,万一脚本内全删了后面的DOM,浏览器就白干活了。更别谈丧心病狂的document.write。浏览器无法预估里面的内容,那就干脆全部停住,等脚本执行完再干活就好了。

对此的优化其实也很显而易见,具体分为两类。如果JS文件体积太大,同时你确定没必要阻塞DOM解析的话,不妨按需要加上defer或者async属性,此时脚本下载的过程中是不会阻塞DOM解析的。

而如果是文件执行时间太长,不妨分拆一下代码,不用立即执行的代码,可以使用一下以前的黑科技:setTimeout()。当然,现代的浏览器很聪明,它会“偷看”之后的DOM内容,碰到如<link><script><img>等标签时,它会帮助我们先行下载里面的资源,不会傻等到解析到那里时才下载。

浏览器遇到 <script> 标签时,会触发页面渲染

这个细节可能不少看官大人并不清楚,其实这才是解释上面为何JS执行会等待CSS下载的原因。先上例子,HTMLbody的结构如下:

<body>
	<div></div>
	<script src="/js/sleep3000-logDiv.js"></script>
	<style>
		div {
			background: lightgrey;
		}
	</style>
	<script src="/js/sleep5000-logDiv.js"></script>
	<link rel="stylesheet" href="/css/common.css">
</body>
로그인 후 복사
로그인 후 복사

这个例子也是很极端的例子,但不妨碍它透露给我们很多重要的信息。想象一下,页面会怎样呢?

答案是先浅绿色,再浅灰色,最后浅蓝色。由此可见,每次碰到<script>标签时,浏览器都会渲染一次页面。这是基于同样的理由,浏览器不知道脚本的内容,因而碰到脚本时,只好先渲染页面,确保脚本能获取到最新的DOM元素信息,尽管脚本可能不需要这些信息。

小结

综上所述,我们得出这样的结论:

  • CSSDOM 구문 분석을 차단하지 않지만 DOM 렌더링은 차단합니다.
  • CSS 不会阻塞 DOM 的解析,但会阻塞 DOM 渲染。
  • JS 阻塞 DOM 解析,但浏览器会"偷看"DOM,预先下载相关资源。
  • 浏览器遇到 <script>且没有deferasync属性的 标签时,会触发页面渲染,因而如果前面CSS资源尚未加载完毕时,浏览器会等待它加载完毕在执行脚本。

所以,你现在明白为何<script>最好放底部,<link>最好放头部,如果头部同时有<script><link>的情况下,最好将<script>放在<link>JSDOM 구문 분석을 차단하지만 브라우저는 DOM을 "관찰"하고 관련 리소스를 미리 다운로드합니다.

브라우저가 <script>를 발견하고 defer 또는 async 속성이 없으면 페이지 렌더링을 트리거합니다. 따라서 이전 CSS 리소스가 로드되지 않은 경우 브라우저는 스크립트를 실행하기 전에 해당 리소스가 로드될 때까지 기다립니다.

이제 <script>가 맨 아래에 배치되고 <link>가 맨 아래에 배치되는 것이 가장 좋은 이유를 이제 이해하셨을 것입니다. head에 <script><link>가 모두 있는 경우에는 <script>를 넣는 것이 가장 좋습니다. &lt ;link>위인가요?

이 글을 읽어주셔서 감사합니다. 이 글이 여러분에게 다른 의견이나 더 나은 의견이 있으면 알려주세요. 감사합니다~

원본주소 : https://juejin.cn/post/6844903497599549453

더 많은 프로그래밍 관련 지식을 보시려면

프로그래밍 영상🎜을 방문해 주세요! ! 🎜

위 내용은 CSS와 JS가 DOM 구문 분석 및 렌더링을 차단하는 방법에 대해 이야기해 보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:juejin.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!