<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>WebFocus &#187; dom scripting</title>
	<atom:link href="http://webfocus.pl/blog/category/dom-scripting/feed/" rel="self" type="application/rss+xml" />
	<link>http://webfocus.pl/blog</link>
	<description>net pod lupą</description>
	<lastBuildDate>Sat, 09 Jan 2010 01:12:45 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Skrócenie czasu ładowania stron WWW &#8212; optymalizacja skryptów JS</title>
		<link>http://webfocus.pl/blog/2008/09/29/skrocenie-czasu-ladowania-stron-www-optymalizacja-skryptow/</link>
		<comments>http://webfocus.pl/blog/2008/09/29/skrocenie-czasu-ladowania-stron-www-optymalizacja-skryptow/#comments</comments>
		<pubDate>Sun, 28 Sep 2008 23:29:12 +0000</pubDate>
		<dc:creator>Mat</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[dom scripting]]></category>
		<category><![CDATA[firebug]]></category>
		<category><![CDATA[firefox]]></category>
		<category><![CDATA[optymalizacja]]></category>

		<guid isPermaLink="false">http://webfocus.pl/blog/?p=23</guid>
		<description><![CDATA[Ostatnio Webstop.pl rozpoczął serię artykułów związanych z internetowym know-how. Pierwszy z nich omawia różne sposoby na skrócenie czasu ładowania stron. A gra jest warta świeczki, bo dzisiejszy internauta jest bardzo niecierpliwy i szybko się zniechęca. Jeśli nie wiesz CO należy zrobić, aby strony wczytywały się szybciej to odsyłam do artykułu. Natomiast jeśli chcesz się dowiedzieć [...]]]></description>
			<content:encoded><![CDATA[<p>Ostatnio <a title="webstop.pl" href="http://webstop.pl" target="_blank">Webstop.pl</a> rozpoczął serię artykułów związanych z internetowym know-how. <a title="Optymalizacja działania stron @ webstop.pl" href="http://www.webstop.pl/2008/09/26/ile-kosztuje-cie-wole-dzialanie-strony-oraz-dlaczego-wiekszosc-ludzi-optymalizuje-nie-to-co-potrzeba/" target="_blank">Pierwszy z nich</a> omawia różne sposoby na skrócenie czasu ładowania stron. A gra jest warta świeczki, bo dzisiejszy internauta jest bardzo niecierpliwy i szybko się zniechęca. Jeśli nie wiesz <strong>CO </strong>należy zrobić, aby strony wczytywały się szybciej to <a href="http://www.webstop.pl/2008/09/26/ile-kosztuje-cie-wole-dzialanie-strony-oraz-dlaczego-wiekszosc-ludzi-optymalizuje-nie-to-co-potrzeba/" target="_blank">odsyłam do artykułu</a>. Natomiast jeśli chcesz się dowiedzieć <strong>JAK </strong>to zrobić, to dobrze trafiłeś!<span id="more-23"></span></p>
<h3>JavaScript jest wszędzie</h3>
<p>Zacznijmy od tego, że optymalizację czasu wyświetlania się strony powinniśmy zacząć po stronie użytkownika. Jeśli policzymy czas od momentu wpisania adresu i wciśnięcia ENTER aż do wyświetlenia całej strony to wyjdzie nam, że około 80-90% czasu zajmą wszystkie czynności związane z wyświetleniem frontendu, tzn. pobranie dodatkowych obrazków, uruchomienie skryptów, załadowanie dodatkowych ramek itd. Tylko 10-20% to czas w jakim serwer tworzy samą zawartość strony (np. wykonując skrypty PHP). No dobrze, ale co najbardziej wydłuża czas wczytywania się stron WWW? Zdecydowanie JavaScript. W dalszej części spróbujemy jakoś temu zaradzić.</p>
<p>W tej chwili niemal 100% stron WWW używa różnych skryptów: od javascriptowych wodotrysków w interfejsie, po trackery i zewnętrzne reklamy. Najczęściej skrypty JS używane są jako różne wklejki na stronie. Wszystko jest proste: kopiuj-wklej w kod HTML strony. Niestety z punktu widzenia wydajności jest to najczęściej spory błąd.</p>
<p>Dla przeciętnego użytkownika gotowa i załadowana strona to taka, którą już można w całości zobaczyć wraz ze wszystkimi jej składnikami (obrazki, flashe itd). Przeglądarka świetnie sobie radzi żeby zrobić to jak najszybciej: ściąga kilka elementów równolegle i wyświetla je jak tylko do niej dotrą, nawet jeszcze w trakcie ściągania. Inaczej jest jednak ze skryptami. Skrypty ściągane są w całości i zaraz po ściągnięciu uruchamiane. Co więcej, w trakcie działania skryptu przeglądarka nie wykonuje nic innego. Czyli np. nie ściąga pozostałych składników strony tylko cierpliwie czeka aż skrypt zrobi swoje.</p>
<p>Co to oznacza? Oto kilka <em>case studies</em>:</p>
<ol>
<li>Strona korzysta z zewnętrznego skryptu gemiusa umieszczonego w sekcji &#8220;head&#8221; strony. Gemius ma akurat krótką awarię i skrypt jest niedostępny. Przeglądarka zaczyna wczytywac stronę ale czeka na skrypt. Po kilku sekundach następuje timeout, ale przez ten czas wszystko stoi w miejscu a użytkownik widzi pustą stronę.</li>
<li>Nawigacja na stronie oparta jest o panele z zakładkami (<a title="tab based navigation" href="http://www.smashingmagazine.com/2007/04/18/14-tab-based-inferface-techniques/" target="_blank">tab-based interface</a>). Przełączanie paneli obsługuje skrypt JS, który został umieszczony w kodzie HTML tuż za elementami
<div>tworzącymi panele. Użytkownik widzi wczytującą się w miarę płynnie stronę, jednak w momencie pokazania się zakładek następuje wyraźne przerwanie wczytywania i przez chwilę nic się nie dzieje.</div>
</li>
<li>Strona wykorzystuje zewnętrzny widget umieszczony w panelu bocznym. Widget został wstawiony wg. instrukcji od dostawcy: wklej kod &lt;script type=”text/javascript”&gt; document.write(”….”) &lt;/script&gt; Użytkownik po wejściu na taką stronę widzi wczytujące się elementy aż do chwili pojawienia się panelu bocznego, następuje dłuższa chwila przerwy, po czym dopiero zaczyna się wyświetlać reszta strony.</li>
</ol>
<p>Takich sytuacji jest mnóstwo, ale wszystkie mają wspólną przyczynę: jest nią <strong>uruchamianie czasochłonnego skryptu w trakcie ładowania strony</strong>.</p>
<h3>Od czego zacząć?</h3>
<p>Po pierwsze przyjrzymy się, które elementy najbardziej spowalniają stronę. Polecam dwa narzędzia: <a title="firebug" href="https://addons.mozilla.org/en-US/firefox/addon/1843">firebug</a> (dodatek do firefoxa) lub <a title="IBM Page Detailer" href="http://www.alphaworks.ibm.com/tech/pagedetailer">IBM Page Detailer</a> (działa zarówno z IE jak i FF). Żeby nabrać trochę wprawy zobaczmy co się dzieje w przypadku naszych case studies:</p>
<p><a href="http://webfocus.pl/labs/1/case1.php" target="_blank">W przykładzie pierwszym</a> wczytywanie elementów strony wygląda tak:</p>
<p><a href="http://webfocus.pl/blog/wp-content/uploads/2008/10/screenshot001.png"><img class="alignnone size-full wp-image-48" title="case nr 1" src="http://webfocus.pl/blog/wp-content/uploads/2008/10/screenshot001.png" alt="" width="468" height="123" /></a></p>
<p>Widzimy, że drugi element (skrypt statystyk umieszczony w sekcji ) wczytuje się 12 sekund. Przez ten czas przeglądarka czeka i nie wczytuje kolejnych fragmentów strony. Element trzeci ładowany jest dopiero po zakończeniu wczytywania i uruchomieniu skryptu. Efekt: przez 12 s użytkownik widzi pustą stronę.</p>
<p><a href="http://webfocus.pl/labs/1/case2.php" target="_blank">W przykładzie drugim</a> mamy skrypt umieszczony gdzieś w połowie sekcji . I podobnie jak poprzednio, przeglądarka parsując kod HTML natrafia na skrypt i zaczyna go wczytywać, przerywając inne czynności.</p>
<p><a href="http://webfocus.pl/blog/wp-content/uploads/2008/10/screenshot002.png"><img class="alignnone size-full wp-image-49" title="case nr 2" src="http://webfocus.pl/blog/wp-content/uploads/2008/10/screenshot002.png" alt="" width="469" height="121" /></a></p>
<p><a href="http://webfocus.pl/labs/1/case3.php" target="_blank">W przykładzie trzecim</a> mamy 3 widgety, każdy z nich ładuje się 2 sekundy:</p>
<p><a href="http://webfocus.pl/blog/wp-content/uploads/2008/10/screenshot005.png"><img class="alignnone size-full wp-image-56" title="case 3" src="http://webfocus.pl/blog/wp-content/uploads/2008/10/screenshot005.png" alt="" width="469" height="155" /></a></p>
<p>Charakterystyczne są tutaj &#8220;schodki&#8221; pokazujące kolejność ładowania się skryptów: każdy następny element czeka na załadowanie się poprzedniego. Nie ma tu mowy o równoległym wczytywaniu. Cała strona zacznie się wyświetlać dopiero po 6 s. <strong>Polecam zapamiętać sobie te &#8220;schodki&#8221;</strong>, bo będą się później pojawiać wszędzie tam, gdzie skrypty powodują blokowanie ładowania się strony.</p>
<h3>No tak, ale co teraz?</h3>
<p>Przejdźmy wprost do rozwiązania. Najlepiej by było, gdyby zmusić wszystkie skrypty do równoległego wczytywania i uruchamiania się PO załadowaniu całej treści strony. Niestety czasami mamy kilka skryptów które potrzebne są na etapie ładowania treści. Musimy zatem:</p>
<ol>
<li>wydzielić te skrypty, które muszą być uruchomione na etapie wczytywania strony</li>
<li>wszystkie pozostałe załadować po wyświetleniu strony</li>
<li>ładować skrypty równolegle w tym samym czasie, bez blokowania</li>
</ol>
<p>W pierwszym punkcie wszystko zależy od strony, ale im mniej skryptów w trakcie ładowania tym lepiej. No i żadnych skryptów z zewnętrznych serwerów. Żadnych.</p>
<p>W punkcie drugim, jeśli na stronie korzystamy z jQuery to najwygodniej skorzystać z $(document).ready() np w taki sposób:</p>
<pre>
<div class="codecolorer-container javascript default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$<span style="color: #009900;">&#40;</span>document<span style="color: #009900;">&#41;</span>.<span style="color: #660066;">ready</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; load_all<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
</pre>
<p>Jeśli nie chcemy ładować całego jQuery to można skorzystać z gotowych skryptów uruchamianych po załadowaniu struktury DOM. Nie będę wnikał w szczegóły, tylko zainteresowanych odsyłam do bloga Riddle&#8217;a, <a href="http://perfectionorvanity.com/2007/05/22/wykonywanie-skryptow-po-zaladowaniu-dom/" target="_blank">który już to opisał</a>, a w szczególności do <a href="http://riddle.pl/-/xhtml/js-documentready/document.ready.js" target="_blank">tego</a> skryptu.</p>
<p>Pozostało najciekawsze, czyli punkt trzeci. Okazuje się, że jest na to parę sposobów. Tutaj skupimy się na jednym, chyba najciekawszym: dynamicznym ładowaniu skryptów. Spójrzmy na taki kod:</p>
<pre>
<div class="codecolorer-container javascript default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #003366; font-weight: bold;">var</span> p<span style="color: #339933;">=</span>document.<span style="color: #660066;">getElementsByTagName</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;HEAD&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#91;</span>0<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span><br />
<span style="color: #003366; font-weight: bold;">var</span> c<span style="color: #339933;">=</span>document.<span style="color: #660066;">createElement</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;script&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
c.<span style="color: #660066;">type</span><span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;text/javascript&quot;</span><span style="color: #339933;">;</span><br />
c.<span style="color: #660066;">src</span><span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;http://host/skrypt.js&quot;</span><span style="color: #339933;">;</span><br />
p.<span style="color: #660066;">appendChild</span><span style="color: #009900;">&#40;</span>c<span style="color: #009900;">&#41;</span></div></td></tr></tbody></table></div>
</pre>
<p>Tworzy on tu dynamicznie element &lt;SCRIPT&gt; i umieszcza go w sekcji &lt;HEAD&gt; strony. Wszystko proste i jasne, ale najciekawsze jest to, że ściągnięcie i uruchomienie skryptu następuje w chwili wykonania linijki p.appendChild(c). Mamy więc idealną kontrolę nad tym kiedy skrypt się ściągnie. Co więcej, dodanie kilku skyptów na raz spowoduje, że będą się ściągać równocześnie. Super! Sposób ten działa zarówno w IE, FF, Operze jak i przeglądarkach opartych na WebKit (Chrome, Safari).</p>
<p>Połączmy teraz wszystkie elementy w jedną całość. <a href="http://webfocus.pl/labs/1/solution.php" target="_blank">Efekt można zobaczyć samemu</a> albo na poniższym obrazku:</p>
<p><img class="alignnone size-full wp-image-72" title="solution1" src="http://webfocus.pl/blog/wp-content/uploads/2008/10/screenshot006.png" alt="solution1" width="477" height="190" /></p>
<p>I oczywiście cały kod <a href="../../labs/1/script-loader.js" target="_blank">script-loader.js</a> (polecam też zajrzeć w źródło <a title="Rozwiązanie" href="../../labs/1/solution.php" target="_blank">rozwiązania</a>):</p>
<div class="codecolorer-container javascript default" style="overflow:auto;white-space:nowrap;border: 1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br /></div></td><td><div class="javascript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #003366; font-weight: bold;">function</span> load_script<span style="color: #009900;">&#40;</span>e<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
<span style="color: #003366; font-weight: bold;">var</span> p<span style="color: #339933;">=</span>document.<span style="color: #660066;">getElementsByTagName</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;HEAD&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#91;</span>0<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span><br />
<span style="color: #003366; font-weight: bold;">var</span> c<span style="color: #339933;">=</span>document.<span style="color: #660066;">createElement</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;script&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
c.<span style="color: #660066;">type</span><span style="color: #339933;">=</span><span style="color: #3366CC;">&quot;text/javascript&quot;</span><span style="color: #339933;">;</span><br />
c.<span style="color: #660066;">src</span><span style="color: #339933;">=</span>e<span style="color: #339933;">;</span><br />
p.<span style="color: #660066;">appendChild</span><span style="color: #009900;">&#40;</span>c<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<br />
<span style="color: #003366; font-weight: bold;">function</span> load_all<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
load_script<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'script1.js.php'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
load_script<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'script2.js.php'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
load_script<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'script3.js.php'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<br />
$<span style="color: #009900;">&#40;</span>document<span style="color: #009900;">&#41;</span>.<span style="color: #660066;">ready</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
load_all<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p>Podsumowując: ładujemy wszystkie skrypty dynamicznie po załadowaniu struktury DOM</p>
<p>Rozwiązanie ma następujące efekty:</p>
<ul>
<li>nie widać znaczących przestojów w czasie ładowania strony, pomimo użycia skryptów wprowadzających spore opóźnienie (nie liczę tu czasu wczytania pomocniczej biblioteki jQuery)</li>
<li>skrypty nie blokują wczytywania innych elementów strony. W przykładowym rozwiązaniu do czasu ich uruchomienia cała strona zostanie już wyświetlona</li>
<li>skrypty ładują się równolegle, bez konieczności czekania na siebie</li>
</ul>
<p>Ma niestety również pewną wadę: <strong>co z sytuacją kiedy różne skrypty są zależne od innych?</strong> Tu oczywiście rozwiązań jest wiele, być może kiedyś o tym napiszę.</p>
<p>Zainteresowanych poznaniem innych szczegółów optymalizacji odsyłam również do prezentacji &#8220;<a href="http://sites.google.com/site/io/even-faster-web-sites" target="_blank">Even Faster Web Sites</a>&#8221; Steve&#8217;a Soudersa.</p>
<h3>Real World Scenarios</h3>
<p>A teraz dwa przykłady znalezione w sieci:</p>
<p>1. Strona główna portalu gazeta.pl</p>
<p><img class="alignnone size-full wp-image-85" title="strona glowna gazeta.pl" src="http://webfocus.pl/blog/wp-content/uploads/2008/10/screenshot007.png" alt="strona glowna gazeta.pl" width="500" /></p>
<p>Skrypt umieszczający reklamy wydłuża ładowanie strony o 1.34 s (w tym przypadku).</p>
<p>2. <a title="Blog Harnira" href="http://blog.harnir.net/" target="_blank">Blog Harnira </a></p>
<p><img class="alignnone size-full wp-image-87" title="blog harnira - analiza" src="http://webfocus.pl/blog/wp-content/uploads/2008/10/screenshot008.png" alt="blog harnira - analiza" width="500" /></p>
<p>Klasyczne schodki: tracimy około 1 s na ładowanie pojedynczych skryptów z bieżącego template&#8217;u wordpressa.</p>
]]></content:encoded>
			<wfw:commentRss>http://webfocus.pl/blog/2008/09/29/skrocenie-czasu-ladowania-stron-www-optymalizacja-skryptow/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
