Context API를 이용하면 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있다.
한계
다시 렌더링 할지 여부를 정할 때 참조를 확인하기 때문에 Provider의 부모가 렌더링 될 때마다 불필요하게 하위 컴포넌트가 다시 렌더링되는 문제가 발생할 수 있다. 예를 들어 아래의 코드는 value가 바뀔 때 마다 매번 새로운 객체가 생성되므로 Provider가 렌더링 될 때마다 하위에서 구독하고 있는 컴포넌트 모두가 렌더링 된다.
js에서 요청/호출되는 일부 속성은 브라우저가 스타일과 레이아웃을 동기적으로 계산하도록 한다.
layout trashing
웹 브라우저는 레이아웃 변경을 즉시 처리하지 않고 비슷한 Style의 수정을 모아서 하게 된다. 즉 화면에서 레이아웃에 대한 정보를 실제로 알아야 할 때까지 레이아웃 계산을 지연시킨다.
layout trashing이 발생하는 경우
DOM이 변경되지 않았음을 보장할 수 있는 경우엔 레이아웃 캐시(이전 계산된 값)에서 값을 가져온다.
하지만 offsetHeight과 같은 최신으로 동기화된 레이아웃에 대한 정보에 접근하는 속성같은 경우 강제로 레이아웃 계산(리플로우)를 발생시켜 동기화 후 값을 가져오게 된다. 즉 아래와 같이 코드를 작성하게 되면 불필요한 레이아웃 계산을 과정이 요구된다.
1 2 3 4
elementA.className = "a-style"; var heightA = elementA.offsetHeight; // layout is needed elementB.className = "b-style"; // invalidates the layout var heightB = elementB.offsetHeight; // layout is needed again
layout trashing이 발생하지 않는 경우
위 코드는 다음과 같이 수정할 수 있다.
1 2 3 4
elementA.className = "a-style"; elementB.className = "b-style"; var heightA = elementA.offsetHeight; // layout is needed and calculated var heightB = elementB.offsetHeight; // layout is up-to-date (no work)
똑같은 작업을 하는 코드지만 위의 코드에 비해 레이아웃 계산의 수가 줄어들게 된다. 레이아웃 계산을 강제로 발생시키는 속성은 여기에서 확인할 수 있다.
연속된 layout trashing 최적화하기
paragraph의 너비를 box의 너비와 같도록 하는 코드를 작성한다고 생각해보자
1 2 3 4 5 6
functionresizeAllParagraphsToMatchBlockWidth() { // Puts the browser into a read-write-read-write cycle. for (var i = 0; i < paragraphs.length; i++) { paragraphs[i].style.width = box.offsetWidth + "px"; } }
위와같이 작성한다면 box의 offsetWidth에 접근한 뒤 paragraphs[i]의 너비로 설정하게 된다.
offsetWidth 속성에 접근할 때마다 강제로 레이아웃 계산을 시도하게 되므로 단일 paragraph마다 layout 계산이 일어나게 된다.
이는 다음과 같이 수정할 수 있다.
1 2 3 4 5 6 7
functionresizeAllParagraphsToMatchBlockWidth() { let width = box.offsetWidth; for (var i = 0; i < paragraphs.length; i++) { // Now write. paragraphs[i].style.width = width + "px"; } }
reflow를 강제로 발생시켜 애니메이션 실행하기
브라우저는 비슷한 style변화를 모아서 반영하기 때문에 원하는대로 애니메이션이 작동하지 않을 때가 있다. 이럴 때는 강제로 reflow를 발생시켜 해결할 수 있다.
FOUC는 외부의 css를 불러오기 전에 스타일이 적용되지 않은 웹페이지가 나타나는 현상이다. 즉 브라우저의 CRP에서 Render Tree가 노출된 후 css와 js로 인해 DOM이 변경되면 변경사항을 적용하기 전 화면이 노출되는 현상이다. IE에서 주로 발생한다.(IE11에서도 여전히 발생한다고 한다.)
@Import를 사용한 css
IE를 제외한 브라우저는 @import된 스타일이 적용될 때까지 화면에 표시하지 않는다. 하지만 IE는 화면에 노출된 상태로 스타일을 적용하여 FOUC를 유발한다.
다음과 같은 해결방법이 있다.
Preload 사용하기
preload 속성을 사용하여 중요한 css를 렌더링 이전에 사용할 수 있도록 한다.
css 링크 옮기기
head 요소안에 css를 링크하는 방식으로도 해결할 수 있다.
렌더링 지연시키기
render block 요소인 <link rel="stylesheet">을 사용하여 렌더링을 고의로 지연시켜 해결할 수 있다.
Proper caching 적용하기
브라우저 캐시 혹은 cdn을 사용하여 정적 리소스를 받아오는 시간을 줄인다.
웹 폰트의 사용
@import를 사용하여 스타일링을 할 때와 같은 원리로 FOUC가 발생한다. IE는 웹폰트를 사용할 경우 기본 폰트를 불러들여 화면에 노출시키고 이후 사용된 웹 폰트로 다시 변경하기 때문에 발생한다.
다음과 같은 해결방법이 있다.
물론 위에서 언급한 preload와 같은 해결방법도 유효하다.
Font Loading API 사용하기
Font Face Observer 라이브러리는 웹 폰트의 로딩 상태를 추적할 수 있는 폰트 로더로, 파일 크기가 작고 실행속도가 빠르다는 장점이 있다.
다음과 같이 사용할 수 있다.
웹 폰트가 적용되지 않은 상태와 적용한 상태의 css를 적어둔다. 그 후 적용되지 않은 상태의 css가 먼저 적용되도록 한다.
1 2 3 4 5 6 7
body { font-family: "Apple SD Gothic Neo", sans-serif; }