JavaScript에서 DOM 작업이 느린 이유에 대한 자세한 소개
우선 DOM 객체 자체도 js 객체이므로 엄밀히 말하면 이 객체의 조작이 느린 것은 아니지만, 이 객체를 조작한 후에는 다음과 같은 일부 브라우저 동작이 트리거됩니다. 레이아웃 및 페인트. 다음은 주로 이러한 브라우저 동작을 소개하고 페이지가 최종적으로 표시되는 방법을 설명합니다. 또한 코드 관점에서 몇 가지 나쁜 사례와 일부 최적화 솔루션도 설명합니다.
브라우저가 페이지를 렌더링하는 방법브라우저에는 많은 모듈이 있으며, 그 중 렌더링 엔진 모듈이 페이지 렌더링을 담당합니다. 여기에는 WebKit 및 Gecko 등이 포함됩니다. 우리는 이 모듈의 내용만 다룰 것입니다. 먼저 이 프로세스를 텍스트로 간략하게 설명하세요. HTML을 구문 분석하고 DOM 트리를 생성- 각 스타일을 구문 분석하고 결합합니다. DOM 트리를 사용하여 렌더 트리를 생성
- 상자의 위치 및 크기와 같은 렌더 트리의 각 노드에 대한 레이아웃 정보를 계산
- 렌더 트리를 기반으로 그리기 및 브라우저의 UI 레이어 사용
- DOM 트리의 노드와 렌더 트리의 노드는 일대일 대응이 아닙니다. " " 노드는 DOM 트리에만 존재하며 렌더 트리에는 나타나지 않습니다.
display:none"
위의 노드입니다. 그림은 Webkit의 기본 프로세스이며 Gecko 측면에서는 다를 수 있습니다. Gecko의 흐름도는 다음과 같습니다. 그러나 기사의 다음 내용에서는 Webkit 용어를 일률적으로 사용합니다. 🎜>링크의 위치가 첫 번째 화면 렌더링에 영향을 미치는 등 페이지 렌더링에 영향을 미치는 요소는 많습니다. 하지만 여기서는 주로 레이아웃 관련 콘텐츠에 중점을 둡니다.
어떤 상황에서 브라우저가 레이아웃을 수행할까요
레이아웃 횟수를 최소화하는 방법을 고려하기 전에 먼저 이해해야 합니다. 브라우저가 레이아웃(리플로우)을 수행하는 경우를 일반적으로 레이아웃이라고 합니다. 이 작업은 문서에서 요소의 위치와 크기를 계산하는 데 사용됩니다. HTML을 처음 로드할 때 중요한 단계입니다. 레이아웃 외에 js 스크립트를 실행하면 브라우저에서도 레이아웃이 실행됩니다.
일반적으로 브라우저의 레이아웃은 게으르다. 즉, js 스크립트가 실행될 때 DOM은 업데이트되지 않습니다. DOM에 대한 모든 수정 사항은 일시적으로 대기열에 저장됩니다. 현재 js 실행 컨텍스트가 실행을 완료한 후 이 수정 사항을 기반으로 레이아웃이 수행됩니다.
그러나 js 코드에서 최신 DOM 노드 정보를 즉시 얻으려면 브라우저에서 미리 레이아웃을 실행해야 합니다. 이것이 DOM 성능 문제의 주요 원인입니다. >다음 작업은 규칙을 위반하고 브라우저가 레이아웃을 실행하도록 합니다.
js를 통해 계산해야 하는 DOM 속성을 가져옵니다
DOM 요소 추가 또는 삭제
브라우저 창 크기 조정
글꼴 변경
활성화 hover
-
js를 통해 DOM 요소 스타일을 수정하고 스타일에 크기 변경이 포함됩니다
직관적으로 느껴보자 예를 들어
// Read var h1 = element1.clientHeight; // Write (invalidates layout) element1.style.height = (h1 * 2) + 'px'; // Read (triggers layout) var h2 = element2.clientHeight; // Write (invalidates layout) element2.style.height = (h2 * 2) + 'px'; // Read (triggers layout) var h3 = element3.clientHeight; // Write (invalidates layout) element3.style.height = (h3 * 2) + 'px';
로그인 후 복사clientHeight, 이 속성을 계산해야 하므로 브라우저 레이아웃이 실행됩니다. Chrome(v47.0)의 개발자 도구를 사용하여 살펴보겠습니다(스크린샷의 타임라인 기록은 필터링되었으며 레이아웃만 표시됨).
속성을 읽습니다. 이전 수정으로 인해 이 속성을 정확하게 얻을 수 있도록 현재 DOM이 더티로 표시됩니다. , 브라우저는 레이아웃을 수행합니다(Chrome의 개발자 도구는 이러한 성능 문제를 양심적으로 상기시켜주었습니다).
이 코드를 최적화하는 방법은 매우 간단합니다. 필수 속성을 미리 읽고 함께 수정하면 됩니다.
// Read var h1 = element1.clientHeight; var h2 = element2.clientHeight; var h3 = element3.clientHeight; // Write (invalidates layout) element1.style.height = (h1 * 2) + 'px'; element2.style.height = (h2 * 2) + 'px'; element3.style.height = (h3 * 2) + 'px';
이번 상황을 살펴보세요.
clientHeight
这个链接里有介绍大部分需要计算的属性:http://www.php.cn/
再来看看别的情况:
面对一系列DOM操作
针对一系列DOM操作(DOM元素的增删改),可以有如下方案:
documentFragment
display: none
cloneNode
比如(仅以documentFragment为例):
var fragment = document.createDocumentFragment(); for (var i=0; i < items.length; i++){ var item = document.createElement("li"); item.appendChild(document.createTextNode("Option " + i); fragment.appendChild(item); } list.appendChild(fragment);
这类优化方案的核心思想都是相同的,就是先对一个不在Render tree上的节点进行一系列操作,再把这个节点添加回Render tree,这样无论多么复杂的DOM操作,最终都只会触发一次layout。
面对样式的修改
针对样式的改变,我们首先需要知道并不是所有样式的修改都会触发layout,因为我们知道layout的工作是计算RenderObject的尺寸和大小信息,那么我如果只是改变一个颜色,是不会触发layout的。
这里有一个网站CSS triggers,详细列出了各个CSS属性对浏览器执行layout和paint的影响。
像下面这种情况,和上面讲优化的部分是一样的,注意下读写即可。
elem.style.height = "100px"; // mark invalidated elem.style.width = "100px"; elem.style.marginRight = "10px"; elem.clientHeight // force layout here
但是要提一下动画,这边讲的是js动画,比如:
function animate (from, to) { if (from === to) return requestAnimationFrame(function () { from += 5 element1.style.height = from + "px" animate(from, to) }) } animate(100, 500)
动画的每一帧都会导致layout,这是无法避免的,但是为了减少动画带来的layout的性能损失,可以将动画元素绝对定位,这样动画元素脱离文本流,layout的计算量会减少很多。
使用requestAnimationFrame
任何可能导致重绘的操作都应该放入requestAnimationFrame
在现实项目中,代码按模块划分,很难像上例那样组织批量读写。那么这时可以把写操作放在requestAnimationFrame
的callback中,统一让写操作在下一次paint之前执行。
// Read var h1 = element1.clientHeight; // Write requestAnimationFrame(function() { element1.style.height = (h1 * 2) + 'px'; }); // Read var h2 = element2.clientHeight; // Write requestAnimationFrame(function() { element2.style.height = (h2 * 2) + 'px'; });
可以很清楚的观察到Animation Frame触发的时机,MDN上说是在paint之前触发,不过我估计是在js脚本交出控制权给浏览器进行DOM的invalidated check之前执行。
其他注意点
除了由于触发了layout而导致性能问题外,这边再列出一些其他细节:
缓存选择器的结果,减少DOM查询。这里要特别提下HTMLCollection。HTMLCollection是通过document.getElementByTagName
得到的对象类型,和数组类型很类似但是每次获取这个对象的一个属性,都相当于进行一次DOM查询:
var ps = document.getElementsByTagName("p"); for (var i = 0; i < ps.length; i++){ //infinite loop document.body.appendChild(document.createElement("p")); }
比如上面的这段代码会导致无限循环,所以处理HTMLCollection对象的时候要做些缓存。
另外,减少DOM元素的嵌套深度并优化css,去除无用的样式对减少layout的计算量有一定帮助。
在DOM查询时,querySelector
和querySelectorAll
应该是最后的选择,它们功能最强大,但执行效率很差,如果可以的话,尽量用其他方法替代。
下面两个jsperf的链接,可以对比下性能。
1)http://www.php.cn/
2)http://www.php.cn/
自己对View层的想法
上面的内容理论方面的东西偏多,从实践的角度来看,上面讨论的内容,正好是View层需要处理的事情。已经有一个库FastDOM来做这个事情,不过它的代码是这样的:
fastdom.read(function() { console.log('read'); }); fastdom.write(function() { console.log('write'); });
问题很明显,会导致callback hell
,并且也可以预见到像FastDOM这样的imperative的代码缺乏扩展性,关键在于用了requestAnimationFrame
后就变成了异步编程的问题了。要让读写状态同步,那必然需要在DOM的基础上写个Wrapper来内部控制异步读写,不过都到了这份上,感觉可以考虑直接上React了……
总之,尽量注意避免上面说到的问题,但如果用库,比如jQuery的话,layout的问题出在库本身的抽象上。像React引入自己的组件模型,用过virtual DOM来减少DOM操作,并可以在每次state改变时仅有一次layout,我不知道内部有没有用requestAnimationFrame
之类的,感觉要做好一个View层就挺有难度的,之后准备学学React的代码。希望自己一两年后会过来再看这个问题的时候,可以有些新的见解。
위 내용은 JavaScript에서 DOM 작업이 느린 이유에 대한 자세한 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

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

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

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

Clothoff.io
AI 옷 제거제

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

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

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

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











WebSocket 및 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법 소개: 지속적인 기술 개발로 음성 인식 기술은 인공 지능 분야의 중요한 부분이 되었습니다. WebSocket과 JavaScript를 기반으로 한 온라인 음성 인식 시스템은 낮은 대기 시간, 실시간, 크로스 플랫폼이라는 특징을 갖고 있으며 널리 사용되는 솔루션이 되었습니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 음성 인식 시스템을 구현하는 방법을 소개합니다.

WebSocket과 JavaScript: 실시간 모니터링 시스템 구현을 위한 핵심 기술 서론: 인터넷 기술의 급속한 발전과 함께 실시간 모니터링 시스템이 다양한 분야에서 널리 활용되고 있다. 실시간 모니터링을 구현하는 핵심 기술 중 하나는 WebSocket과 JavaScript의 조합입니다. 이 기사에서는 실시간 모니터링 시스템에서 WebSocket 및 JavaScript의 적용을 소개하고 코드 예제를 제공하며 구현 원칙을 자세히 설명합니다. 1. 웹소켓 기술

JavaScript 및 WebSocket을 사용하여 실시간 온라인 주문 시스템을 구현하는 방법 소개: 인터넷의 대중화와 기술의 발전으로 점점 더 많은 레스토랑에서 온라인 주문 서비스를 제공하기 시작했습니다. 실시간 온라인 주문 시스템을 구현하기 위해 JavaScript 및 WebSocket 기술을 사용할 수 있습니다. WebSocket은 TCP 프로토콜을 기반으로 하는 전이중 통신 프로토콜로 클라이언트와 서버 간의 실시간 양방향 통신을 실현할 수 있습니다. 실시간 온라인 주문 시스템에서는 사용자가 요리를 선택하고 주문을 하면

WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법 오늘날의 디지털 시대에는 점점 더 많은 기업과 서비스에서 온라인 예약 기능을 제공해야 합니다. 효율적인 실시간 온라인 예약 시스템을 구현하는 것이 중요합니다. 이 기사에서는 WebSocket과 JavaScript를 사용하여 온라인 예약 시스템을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 1. WebSocket이란 무엇입니까? WebSocket은 단일 TCP 연결의 전이중 방식입니다.

JavaScript 및 WebSocket: 효율적인 실시간 일기 예보 시스템 구축 소개: 오늘날 일기 예보의 정확성은 일상 생활과 의사 결정에 매우 중요합니다. 기술이 발전함에 따라 우리는 날씨 데이터를 실시간으로 획득함으로써 보다 정확하고 신뢰할 수 있는 일기예보를 제공할 수 있습니다. 이 기사에서는 JavaScript 및 WebSocket 기술을 사용하여 효율적인 실시간 일기 예보 시스템을 구축하는 방법을 알아봅니다. 이 문서에서는 특정 코드 예제를 통해 구현 프로세스를 보여줍니다. 우리

JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법, 특정 코드 예제가 필요합니다. 서문: 웹 개발에서는 서버와의 데이터 상호 작용이 종종 포함됩니다. 서버와 통신할 때 반환된 HTTP 상태 코드를 가져와서 작업의 성공 여부를 확인하고 다양한 상태 코드에 따라 해당 처리를 수행해야 하는 경우가 많습니다. 이 기사에서는 JavaScript를 사용하여 HTTP 상태 코드를 얻는 방법과 몇 가지 실용적인 코드 예제를 제공합니다. XMLHttpRequest 사용

사용법: JavaScript에서 insertBefore() 메서드는 DOM 트리에 새 노드를 삽입하는 데 사용됩니다. 이 방법에는 삽입할 새 노드와 참조 노드(즉, 새 노드가 삽입될 노드)라는 두 가지 매개 변수가 필요합니다.

JavaScript는 웹 개발에 널리 사용되는 프로그래밍 언어인 반면 WebSocket은 실시간 통신에 사용되는 네트워크 프로토콜입니다. 두 가지의 강력한 기능을 결합하면 효율적인 실시간 영상 처리 시스템을 만들 수 있습니다. 이 기사에서는 JavaScript와 WebSocket을 사용하여 이 시스템을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 첫째, 실시간 영상처리 시스템의 요구사항과 목표를 명확히 할 필요가 있다. 실시간 이미지 데이터를 수집할 수 있는 카메라 장치가 있다고 가정해 보겠습니다.
