모바일 웹에서 성능을 높이는 방법으로 GPU가속을 사용하기 위해 translateZ(0)와 같은 hack코드를 넣곤 했다. 이후에 will-change가 나와서 hack을 대신해서 사용했다. 둘 다 GPU가속을 위해 사용했는데 굳이 will-change을 사용하는 이유는  "브라우저가 효과적으로 GPU가속을 사용할 수 있기" 때문이였다.


문서에는 브라우저마다 will-change을 다르게 사용한다고 하지만, 실제로 크롬에서는 will-change가 어떤 영향을 주는지 코드로 확인하고 싶었다. 아쉽게도 당시 코드를 봤을 때 딱히 크롬이 스마트하게 판단하는지는 찾지 못했다. 그렇게 내가 아직 잘 모르는구나 하는 생각을 하고 지나갔다.


그리고 몇 일전에 will-change: scroll-position, content이 성능에 어떤 영향을 받는지 문의한 글이 있었는데 이 글을 보고 예전 생각이 나서 정리한다. 


일단 scroll-position는 스크롤의 위치가 변경될 수 있음을 알려주는 힌트이고 content는 엘리먼트가 변경될 수 있음을 알려주는 힌트다. [링크]


답변 내용은 현재까진 will-change: scroll-position은 어떤 영향을 미치지 않는다. will-change: content의 경우는  content을 가진 엘리먼트의 자식 엘리먼트가 will-change: <hint>을 가지면 변경할 가능성이 있다고 판단하여 자식 엘리먼트는 composited layer을 만들지 않는다. 


예제를 보면 이해하기 쉬운데 아래 두개만 composited layer을 만든다.

<div style="will-change: scroll-position"></div>

<div style="will-change: contents"></div>

<div style="will-change: transform"></div> // <--------- composited layer

<div style="will-change: contents">
    <div style="will-change: transform"></div>
</div>

<div style="will-change: scroll-position">
    <div style="will-change: transform"></div> // <--------- composited layer
</div>


blink코드를 보면 더 정확히 할 수 있다.

if (style.hasWillChangeCompositingHint() && !style.subtreeWillChangeContents())
        reasons |= CompositingReasonWillChangeCompositingHint;


그리고 will-change에 들어갈 수 있는 style은 [opacity, transform, webkit-transform, top, left, bottom, right]이다.


switch (rareNonInheritedData->m_willChange->m_properties[i]) {
        case CSSPropertyOpacity:
        case CSSPropertyTransform:
        case CSSPropertyAliasWebkitTransform:
        case CSSPropertyTop:
        case CSSPropertyLeft:
        case CSSPropertyBottom:
        case CSSPropertyRight:
            return true;
        default:
            break;
}


이걸 보면서, 예전에 궁금했던 점이 조금 이해됐다. 현재는 content속성에 대해서만 되어 있지만, 나중에는 scroll-position에 대해서도 최적화가 들어갈 것 같고 이후에는 W3C에 나와 있는 것 처럼 최적화가 되지 않을까 생각된다.




Posted by 전용우
,

앞의 글에서[링크] HTML import는 랜더링을 막기 때문에 지연되지 않기 위해 스크립트로 만들어서 사용했다.

근데 알고보니 HTML import는 기본적으로 랜더링을 막지 않는다.[링크] 앞에서 HTML import가 랜더링이 멈췄던 이유는 사실 스크립트 태그 때문이였다.


HTML import은 스크립트 태그를 만나기 전까지는 계속해서 랜더링을 하다가 스크립트 태그를 만나면 랜더링을 멈춘다.

예를들면 아래처럼 script태그가 나오기 전인 #ad_area까지 랜더링을 하다가 script태그를 만나면 멈추고 로딩이 끝나면 이어서 랜더링이 된다. 

<link rel="import" href="ad.html">

<div id="ad_area"> 앞 </div>

<script>
//script...
</script>

<div id="ad_area2"> 뒤 </div>

즉, 앞의 글에서 말한 HTML import는 랜더링을 막으면 안된다는 얘기는 상관이 없다.


그렇다고해서 무조건 랜더링을 막지 않는것이 좋은 것만은 아니다.

import는 Web Components을 사용할 때 사용하는 태그인데 사용하려고 하는 커스텀 엘리먼트가 import보다 먼저 랜더링되면 처음에는 모르는 태그라 넘어가다가 import가 완료가 되면 바로 적용이 된다.[링크]

이렇게 적용되면 FOCU[링크]와 같이 화면이 이상하게 보이는데 이런 문제를 해결하려면 적절하게 보이도록 만들거나[링크] 앞에서 본것처럼 아예 script을 커스텀 태그 앞에 넣어 멈추는 방법[링크]을 사용해야한다.

둘 다 매끄럽지 않기 때문에 현재 import을 적용할 때 element속성을 추가하여 element 속성에 설정한 엘리먼트들을 만나면 랜더링을 멈추는 스팩을 제안했고 여러 각도로 이슈를 해결하기 위해 토론되고 있다.[링크]

물론. 비슷한 이슈가 이미 논의가 됬고 :unresolved라는 가상 선택자[링크]를 만들어서 제공했지만, 크기를 가지는 엘리먼트의 경우 꿀렁거리는 이슈는 여전히 해결이 안된다. (참고로 angularjs의 ngCloak도 비슷한 용도이다.[링크])


대충 의미만 전달하기 위해 적었는데 좀 더 자세한 내용은 원문을 참고하길 바란다.[링크]


관련글

HTML import을 이용하여 document.write 개선하기.

Posted by 전용우
,

document.write을 사용하면 돔 랜더링이 멈추기 때문에 느리게 보인다. 그래서document.write을 대신하기 위한 다양한 방법이 나와있다. 하지만, 광고의 경우 다양한 곳에 삽입되기 때문에 대부분은 document.write을 사용한다.

이 문제를 해결하고자 최근에 웹성능의 본좌인 사우더스 아저씨가 HTML import을 활용한 방법을 제안했다.[링크] (HTML import는 본인과 크게 상관도 없고 아직 완성도가 있는 스팩인데 이런걸 활용하다니 좀 놀랐다.)

HTML import를 간단히 설명하면  Web Components을 사용하기 위한 방법으로 link태그를 이용하여 html을 로딩하는 방법이다.[링크]

자세한 내용은 원문[링크]을 참고하고 요약하면 사우더스 아저씨는 아래와 같이  import안에서 document.write를 하는 방법을 제안했다.

main.html

<link rel="import" href="ad.html">

<div id="ad_area" style="width: 480px; height: 60px; margin-left: 2em; margin-bottom: 2em;"></div>

<script>
var link = document.querySelector('link[rel=import]');
var content = link.import.querySelector('#ad');
document.getElementById('ad_area').appendChild(content.cloneNode(true));
</script>

ad.html
<div id="ad" style="background: #E99; border: 2px; font-size: 2em; text-align: center; padding: 8px;">
<script>
document.write("IMPORTED CONTENT<div style='font-size=0.8em;'>(using document.write)</div>");
</script>

근데 문제가 있다.

1. import는 동기로 로딩된다.[링크]

2. import한 html에서 document.write을 하면 main페이지에서 context로 실행되는 문제가 있다.[링크]

그래서 이를 해결하고자 아래와 같이 스크립트로 import을 삽입하고 ad.html에서 document.write을 재정의하여 현재 context로 실행하도록 했다.

main.html

<div id="ad_area" style="width: 480px; height: 60px; margin-left: 2em; margin-bottom: 2em;"></div>

<script>
  var link = document.createElement('link');
  link.rel = 'import';
  link.onload = function() {
    var link = document.querySelector('link[rel=import]');
    var content = link.import.querySelector('#ad');
    document.getElementById('ad_area').appendChild(content.cloneNode(true));
  };
  link.href = "ad.html";
  document.getElementsByTagName('head')[0].appendChild(link);
</script>

ad.html

<div id="ad" style="background: #E99; border: 2px; font-size: 2em; text-align: center; padding: 8px;">
<script>
document.write = function(msg) {
    document.currentScript.ownerDocument.write(msg);
};
document.write("IMPORTED CONTENT<div style='font-size=0.8em;'>(using document.write)</div>");
</script>


이렇게 document.write를 사용하면서 돔의 랜더링을 지연하지 않도록 개선할 수 있다.[링크]

물론 import의 안에서 실행되는 context의 이슈등 문제가 있어 당장 사용하기 힘들지만 아이디어는 좋은 것 같다.

그리고 사우더스는 HTML import가 기본으로 랜더링을 막지 말고 동작해야 한다고 제안했지만 이 문제는 쉽지 않다. 이 문제에 대해서는 다음에 알아보자. 또한 위의 방법은 오해가 있기 때문에 반드시 [링크]을 추가로 읽기 바란다.


관련글 

알고보면 랜더링을 막지 않는 import

Posted by 전용우
,

작년에는 Web Components관련해서 아무것도 없어서 가능성만 보고 발표했는데 올해는 그나마 라이브러리도 나오고 해서 좀 더 얘기할게 많았다.

Polymer랑 Brick에 대해서도 할 얘기가 많았지만 시간상 못하고 왔는데 언제 한번 자세히 정리해봐야지...


발표자료.


소스 코드는 아래서.

https://github.com/mixed/webcomponentscode/

Posted by 전용우
,