렌더 트리에서 다음과 같은 속성을 가지고 있으면 Graphics Layer로 넘어간다.
<video>
또는 <canvas>
요소CPU에만 의존해서 렌더링하는 것 보다 비디오나 3D 같이 무거운 부분들은 GPU를 사용해 렌더링하면 성능향상의 이점을 얻을 수 있다.
하드웨어 가속을 이용하면 분명 성능이점을 얻을 수 있지만 페이지의 모든 컨텐츠를 Graphics Layer로 만드는 짓은 하지말자.
구체적으로 다음과 같은 사항들을 유의해서 사용하면된다.
특정 요소에 의미없는 CSS 3D 속성( transform: translate3d(0, 0, 0) )을 주면 하드웨어 가속을 줄 수 있는데 이러한 방법은 무분별하게 Graphics Layer를 만들고 이 과정의 비용이 크기 때문에 되려 성능 저하를 가져올 수 있다.
따라서 will-change
옵션을 사용하면 되는데 이름만 보면 알 수 있듯이 미래에 바뀔 요소를 브라우저에게 미리 알려줘서 최적화를 준비할 수 있게 해주는 옵션이다.
해당 요소에 앞으로 어떤 스타일 변화가 있을 수 있는지 명시해주면 된다.
1
will-change: transform, opacity;
1
2
3
4
5
*,
*::before,
*::after {
will-change: all;
}
모든 요소에 will-change
를 페이지를 최적화하겠다는 생각이 기특한 코드이지만 전혀 이점을 가지지 못한다.
브라우저는 이미 가능한 최적화를 시행하고 있으므로 우선 순위가 없는 최적화 코드를 집어넣어버려 오히려 리소스만 사용하는 나쁜 케이스다.
will-change
는 브라우저에게 변화가 있는 요소에 힌트를 줘서 최적화의 준비 시간을 준다고 했다.
다음과 같이 호버 이벤트가 발생했을 때 will-change
로 힌트를 주고 바로 애니메이션을 발생시키면 브라우저는 준비할 수가 없다…
1
2
3
4
5
.element:hover {
will-change: transform;
transition: transform 2s;
transform: rotate(30deg) scale(1.5);
}
호버 후 클릭 이벤트로 애니메이션을 발생시킨다면?
그정도면 브라우저에겐 충분한 시간이다.
1
2
3
4
5
6
7
8
9
.element {
transition: transform 1s ease-out;
}
.element:hover { // 마우스를 올리면 힌트를 주고
will-change: transform;
}
.element:active { // 마우스를 클릭 할시 애니메이션 발생
transform: rotateY(180deg);
}
will-change
가 요소에 선언되어있으면 그 자체로 리소스를 잡아먹는다. 따라서 해당 요소가 한 번의 변화만 일으키는 일회성 변화를 가지고 있는 요소라면 변화가 일어난 후 자바스크립트로 will-change`속성을 부여하고/지워줄 것을 추천하고 있다.
처음부터 유저와 잦은 상호작용으로 많은 변화가 일어나는 요소라면 그렇게 할 필요가 없이 css 스타일 시트에서 선언해도 좋다.
렌더링 과정을 공부해본 사람은 알겠지만 리렌더링 과정에서 가장 리소스를 적게 먹는 과정은 합성이다.
위에서 살펴본 각각의 레이어들에서 페인트 과정을 거친 뒤, 컴포지터 스레드라고 하는 별도의 스레드에서 사용자에게 보여줄 웹 페이지를 합쳐 보여주는 것을 합성이라고 한다. 메인 스레드의 개입 없이 수행되기 때문에 이 과정은 스타일 계산 혹은 자바스크립트 실행을 기다릴 필요가 없어 부드러운 성능을 보여줄 수 있다.
유저가 브라우저에서 스크롤을 내리게되면 컴포지터 스레드는 이미 페인트가 끝난 레이어들의 위치만 옮겨서 재합성한 뒤 다시 보여주면된다.
컴포지터 스레드는 분명 자바스크립트가 실행되는 메인 스레드와 다른 스레드다. 그럼 합성된 페이지 내에서 자바스크립트 이벤트가 발생하는 것을 컴포지터 스레드가 어떻게 감지할 수 있을까?
결론부터 말하면 컴포지터 스레드는 이벤트가 달려있는 요소에 대해서 고속 스크롤 불가 영역(non-fast scrollable region)’으로 표시하고 이벤트가 발생하면 해당 이벤트에 대한 정보를 메인 스레드로 보내준다.
고속 스크롤 불가 영역 외에서 이벤트가 발생하면 메인 스레드에 정보를 보낼 필요가 없기 때문에 컴포지터 스레드는 합성 과정을 진행한다.
하지만 고속 스크롤 불가 영역 내에서 이벤트가 발생하면 메인 스레드에 정보를 보내주어야하기 때문에 컴포지터 스레드는 메인 스레드에서 이벤트 처리가 끝날 때 까지 기다려야하는데 이벤트 위임과 이 상황이 겹치면 다음과 같은 일이 벌어진다.
1
2
3
4
5
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
페이지의 모든 영역이 고속 스크롤 불가 영역이 되어버려 부드러운 합성을 할 수 없는 상태가 되어버리는 것
이를 방지하기 위해 메인 스레드의 응답을 기다리지 말고 새 프레임을 만들어도 된다고 명시해주는 옵션이 passive: true
다.
1
2
3
4
5
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault()
}
}, {passive: true});
https://d2.naver.com/helloworld/2061385
https://d2.naver.com/helloworld/6204533
https://dev.opera.com/articles/ko/css-will-change-property/