Dynamické nahrávání JS souborů, zn. efektivní, i z komponent a na 14 řádcích kódu
- Tharos
- Člen | 1030
Ahoj,
na fóru se poměrně
často
řeší,
jak připojit do stránky externí JavaScripty (a styly jakbysmet). Samozřejmě
se vždycky dají plácnout někam dovnitř
<body></body>
, ale řekněme, že hledáme
estetičtější řešení. Kritéria jsou, aby to bylo elegantní, výkonné a
pokud možno to i umožňovalo lazy vytváření komponent, které si nakonec
ale také budou chtít něco málo svého skriptového ke stránce
připojit.
Pro řešení tohoto problému již vzniklo několik komponent a já osobně
je nedokáži překousnout proto, protože to jsou komponenty. :) Rozumějme
potomci Nette\Application\UI\Control
. Jde o to, že se mi
nelíbí, když se v Presenteru, který si tak hezky řídí životní cyklus
aplikace, najednou řeší nějaké JavaScriptové soubory okořeněné špetkou
CSS… To jsou věci, které IMHO do Presenteru moc nepatří. A nemyslím si,
že by to bylo zbytečné puritánství. Prostě ho to znepřehledňuje a
přenáší to do něj věci, které… které mně osobně prostě vyhovuje
mít jinde. (I když to lze omluvit tím, že Presenter má za úkol
inicializovat view, a tohle tedy do něj tedy taky v jistém smyslu
patří.)
Populární WebLoader od Honzy Marka elegantně slučuje závislosti do
minimálního počtu souborů, čímž výrazně šetří HTTP požadavky a to
je rozhodně fajn věc. Na druhou stranu už není tak černobílé, jestli je
tohle rychlejší
, protože moderní prohlížeče závislosti
stahují paralelně a teoreticky tedy vyjde nastejno, jestli se má stáhnout
jeden 10 kB soubor, anebo čtyři 2 kB (neřešme nyní hlavičku 304). Jisté zdroje dokonce hlásají,
že paralelní stahování je rychlejší. Předesílám ale, že kdo byste
tohle důmyslné slučování odmítli oželet, nemusíte číst dál. :)
Používám řešení, které mi přijde až směšně jednoduché (proto jsem repositár s ním pracovně nazval FoolishLoader), ale přijde mi, že ze všech nejelegantněji řeší mé požadavky. Zajímal by mě váš názor na to, totiž jestli mi třeba někde něco neuniká. :)
Představme si, že máme do stránky připojené knihovny jQuery a Head JS (a třeba i netteForms.js, ale to už není podstatné). Připojme si nyní ještě jeden externí skript s následujícím obsahem:
jQuery(function($) {
basepath = $('body').data('basepath'); // It is useful to have basepath in global scope
// load required JavaScripts
$('[data-js]').each(function(index, element) {
var script = $(element).data('js');
if (script instanceof Array) {
$.each(script, function (index, script) {
head.js(basepath + script);
})
} else {
head.js(basepath + script);
}
});
});
No, a to je vlastně všechno. Fígl je v tom, že jakýkoliv element může
obsahovat atribut data-js
s názvem skriptu (respektive skriptů,
pojme to i JSON pole), které potřebuje, a Head JS se již postará
o efektivní paralelní stažení a spuštění.
Úplně nejlepšího výkonu se dá dosáhnout například následujícím layoutem:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" media="screen,projection,tv" href="{$basePath}/css/core.css" type="text/css">
<title>FoolishLoader</title>
</head>
<body data-basePath="{$basePath}">
{include #content}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="{$basePath}/libs/head.load.min.js"></script>
<script src="{$basePath}/libs/netteForms.js"></script>
<script src="{$basePath}/libs/foolishLoader.js"></script>
</body>
</html>
Pokud někde v šabloně pohledu vykreslíme komponentu s následujícím kódem:
<div id="control" data-js="/js/simpleControl.js">I'm a very simple control.</div>
tak se v ideální moment (tj. při DOM ready) připojí a spustí požadovaný skript a to vše i bez narušení lazy vytváření komponenty a bez znepřehledňování kódu Presenteru.
Když to shrnu, vidím v tomhle přístupu následující výhody:
- JS a CSS se řeší ve view vrstvě, nikoliv v Presenteru.
- Je to efektivní, komunikace se serverem typicky vypadá nějak takhle (prvotní načtení bez použití cache).
- Pokud by nějaká komponenta připojovala již připojený skript, Head JS to pozná a už jej znovu nepřipojuje.
- Je to vlastně řešení na zmíněných 14 řádcích kódu, pro vytahování se počítáno bez mezer ;).
Má tohle nějakou zásadní slabinu? Nahrál jsem na GitHub komplexní ukázku, jak to celém může fungovat v praxi (je tam i ta jednoduchá komponenta). Připojovat se takto dají samozřejmě i CSS soubory (definované třeba přes data-css).
- Tharos
- Člen | 1030
- Je to efektivní, komunikace se serverem typicky vypadá nějak takhle (prvotní načtení bez použití cache).
K tomuhle bych ještě rád dodal, že při prvotním nahrávání je mé řešení o něco pomalejší, než například WebLoader. To proto, protože v mém řešeí se skript při DOM ready začíná teprve stahovat, zatímco při využití WebLoaderu již bude pravděpodobně stažen a při DOM ready se už třeba jen spouští. Nicméně tenhle rozdíl se v podstatě smaže v okamžiku, kdy už je skript uložený v cache (pravda, že úplně dokonale také ne, protože v mém řešení se na něj ještě prohlížeč při DOM ready ptá, ale rozdíl už by měl být skutečně minimální).
- Matúš Matula
- Člen | 257
Ahoj,
myslim ze na fore sa dost riesi pripajanie css a js v ramci nejakeho modulu alebo komponenty, kedy vela ludi (aspon myslim, som medzi nimi:) uprednostnuje mat ich v nejakej adresarovej strukture v ramci modulu/komponenty pre prehladnost a jednoduchsiu znovupouzitelnost. Takto su zvonku nepristupne a je nutne ich nejako spristupnit, to riesi napr. spominany WebLoader skopirovanim do www_root/webtemp.
Pre nacitanie public js je to imo uzitocne, jednoduche, pekne :)
Inak co sa tyka nacitavania css navrhovanym sposobom, tak na ludi s vypnutym js uz hadzes definitivne bobek? :)
- Tharos
- Člen | 1030
Dobrá poznámka, že závislosti, které mají být uložené mimo veřejné složky, takhle řešit nelze. Já právě tušil, že mi tam ještě někde něco uniká ;).
Co se CSS týče, no, já tohle používám jenom pro JavaScript. :) A to, zda bych na uživatele bez zapnutého JavaScriptu eventuálně kašlal, by záviselo především na cílové skupině toho či onoho webu.
Editoval Tharos (11. 3. 2012 22:29)