ãã®æçš¿ã¯ãã¹ã ãŒãºãªç§»è¡ã®äœæã«é¢ããSmashing Magazineã®èšäºãããŒãžç§»è¡ã«ãããŠãŒã¶ãŒãããŒã®æ¹åãã®ç¿»èš³ã§ã ã ãã®èšäºã®èè
ã§ããLuigi De Rosaã¯ã EPICã®ããã³ããšã³ãéçºè
ã§ãã ããã«ãç©èªã¯èšäºã®èè
ã«ä»£ãã£ãŠè¡ãããŸãã è¯ãèªæžãããŠãã ããã
ãŠãŒã¶ãŒãã€ã³ã¿ã©ã¯ã·ã§ã³ïŒUXïŒã®ãšã¯ã¹ããªãšã³ã¹ã«åé¡ããããã³ã«ããŠãŒã¶ãŒãé¢è±ããæ©äŒãå¢ããŸãã ããããŒãžããå¥ã®ããŒãžã«å€æŽãããšãå€ãã®å Žåãã³ã³ãã³ãã®ãªãçœãããªãã«ãŒã®åœ¢ã§äžæãçºçããè² è·ãé·ããªããã以åã«éããããŒãžã®ã³ã³ããã¹ããããŠãŒã¶ãŒãåŒãé¢ãããŸãã
ããŒãžéã®é·ç§»ã¯ããŠãŒã¶ãŒã³ã³ããã¹ããä¿æïŒãŸãã¯æ¹åããïŒãããŠãŒã¶ãŒã®æ³šæãä¿æããèŠèŠçãªç¶ç¶æ§ãæäŸããããšã«ããããã®ãšã¯ã¹ããªãšã³ã¹ã匷åã§ããŸãã åæã«ãããŒãžéã®é·ç§»ã¯ç®ã楜ããŸããåªããããã©ãŒãã³ã¹ã§è峿·±ããã®ã«ãªããŸãã
ãã®èšäºã§ã¯ãããŒãžé·ç§»ã段éçã«äœæããŸãã ãŸãããã®ææ³ã®é·æãšçæãããã³æå€§éã«æŽ»çšããæ¹æ³ã«ã€ããŠã説æããŸãã
äŸ
å€ãã®ã¢ãã€ã«ã¢ããªã¯ããã¥ãŒéã®åªããç§»è¡ã䜿çšããŸãã Googleã®ãããªã¢ã«ãã¶ã€ã³ã¬ã€ãã©ã€ã³ã«åŸã£ããã®äŸã§ã¯ãã¢ãã¡ãŒã·ã§ã³ãããŒãžéã®éå±€çããã³ç©ºéçãªé¢ä¿ãã©ã®ããã«äŒãããã確èªããŸãã
ãŠã§ããµã€ãã«å¯ŸããŠåæ§ã®ã¢ãããŒãããšããªãã®ã¯ãªãã§ããïŒ ãŠãŒã¶ãŒãããŒãžã®å€æŽããšã«ãã¬ããŒããããŠããããã«æããããšã«åæããã®ã¯ãªãã§ããïŒ
ããŒãžé·ç§»ããªã³ã¯ããæ¹æ³
SPAãã¬ãŒã ã¯ãŒã¯
æãæ±ãåã«ãåäžããŒãžã¢ããªã±ãŒã·ã§ã³ïŒSPAïŒãã¬ãŒã ã¯ãŒã¯ã«ã€ããŠäœãèšããªããã°ãªããŸããã SPAãã¬ãŒã ã¯ãŒã¯ïŒAngularJSãBackbone.jsãEmberãªã©ïŒã䜿çšããå Žåããã¹ãŠã®ãã¹ãJavaScriptã«ãã£ãŠåŠçããããããé·ç§»ã®äœæãã¯ããã«ç°¡åã«ãªããŸãã ãã®å Žåãé©åãªäŸãšæç€ºããããããé¢é£ããããã¥ã¡ã³ããåç
§ããŠãéžæãããã¬ãŒã ã¯ãŒã¯ã®ããŒãžéã®é·ç§»ã®å®è£
ã確èªããå¿
èŠããããŸãã
æªãæ¹æ³
ããŒãžéã®é·ç§»ãäœæããæåã®è©Šã¿ã¯æ¬¡ã®ããã«ãªããŸããã
document.addEventListener('DOMContentLoaded', function() {
æŠå¿µã¯åçŽã§ãããŠãŒã¶ãŒãããŒãžãé¢ãããšãã«ã¢ãã¡ãŒã·ã§ã³ã䜿çšããæ°ããããŒãžãèªã¿èŸŒãŸãããšãã«å¥ã®ã¢ãã¡ãŒã·ã§ã³ã䜿çšããŸãã
ãããããã®ãœãªã¥ãŒã·ã§ã³ã«ã¯å€ãã®å¶éãããããšã«ããã«æ°ä»ããŸããã
- ããŒãžã®èªã¿èŸŒã¿æéã¯ããããŸããã®ã§ãã¢ãã¡ãŒã·ã§ã³ãã¹ã ãŒãºã«è¡šç€ºãããªãå ŽåããããŸãã
- åã®ããŒãžã𿬡ã®ããŒãžã®ã³ã³ãã³ããçµã¿åããããã©ã³ãžã·ã§ã³ãäœæããããšã¯ã§ããŸããã
å®éãã¹ã ãŒãºãªç§»è¡ãå®çŸããå¯äžã®æ¹æ³ã¯ãããŒãžã倿Žããããã»ã¹ãå®å
šã«å¶åŸ¡ããããšã§ããããããã£ãŠãããŒãžå
šäœã倿Žããããšã¯ã§ããŸããã
ãããã£ãŠãåé¡ãžã®ã¢ãããŒãã倿Žããå¿
èŠããããŸãã
æ£ããæ¹æ³
æ£ããæ¹æ³ã§ããŒãžéã®ã·ã³ãã«ã§ã¹ã ãŒãºãªç§»è¡ãäœæããããã®æé ãèŠãŠã¿ãŸãããã pushState
AJAXïŒãŸãã¯PJAXïŒããã²ãŒã·ã§ã³ãšåŒã°ãããã®ããããããã¯åºæ¬çã«ç§ãã¡ã®ãµã€ãã1ããŒãžã®ãµã€ãã®ãããªãã®ã«å€ããŸãã
ããã¯ãã¹ã ãŒãºã§å¿«é©ãªç§»è¡ãå®çŸããæ¹æ³ã§ããã ãã§ãªãããã®èšäºã®åŸåã§è©³ãã説æããä»ã®å©ç¹ã掻çšããŸãã
ããã©ã«ãã®ãªã³ã¯åäœãé²ã
æåã®ã¹ãããã¯ããã¹ãŠã®ãªã³ã¯ã®ã¯ãªãã¯ã€ãã³ããã³ãã©ãŒãäœæãããªã³ã¯ãæšæºã®åäœãè¡ããªãããã«ããããŒãžã®å€æŽã®åŠçæ¹æ³ã倿Žããããšã§ãã
ç¹å®ã®ããŒãã«è¿œå ããã®ã§ã¯ãªãã芪èŠçŽ ã«ãã³ãã©ãŒã远å ãããã®æ¹æ³ã¯ã ã€ãã³ãã®å§ä»»ãšåŒã°ããŸã ãããã¯ãHTML DOM API ããã«ã€ãã³ãã®æ§è³ªã«ããå¯èœã§ãã
ååŸããŒãž
ãã©ãŠã¶ã«ããããŒãžã®èªã¿èŸŒã¿ãäžæããã®ã§ã Fetch APIã䜿çšããŠããŒãžãæåã§ååŸã§ããŸã ã URLãåãåã£ããšãã«HTMLããŒãžã®ã³ã³ãã³ããåãåãæ¬¡ã®é¢æ°ãèŠãŠã¿ãŸãããã
function loadPage(url) { return fetch(url, { method: 'GET' }).then(function(response) { return response.text(); }); }
Fetch APIããµããŒãããŠããªããã©ãŠã¶ãŒã®å Žåã polyfillã远å ããã ã XMLHttpRequestã䜿çšããå¿
èŠããããŸãã
çŸåšã®URLã倿Ž
HTML5ã«ã¯pushState
ãšåŒã°ããçŽ æŽãããAPIããããWebãµã€ããããŒãžãèªã¿èŸŒãŸãã«ãã©ãŠã¶ãŒã®å±¥æŽã«ã¢ã¯ã»ã¹ããŠå€æŽã§ããããã«ããŸãã 以äžã§ã¯ãããã䜿çšããŠãçŸåšã®URLãæ¬¡ã®ããŒãžã®URLã«å€æŽããŸãã ããã¯ã以åã«çºè¡šãããã¯ãªãã¯ãã³ãã©ãŒã®ä¿®æ£ã§ããããšã«æ³šæããŠãã ããã
if (el) { e.preventDefault(); history.pushState(null, null, el.href); changePage(); return; }
ãæ°ã¥ããããããŸãããã changePage
颿°ã®åŒã³åºãã远å ããŸããã changePage
颿°ã®è©³çްã確èªããŸãã åæ§ã®é¢æ°ã¯ã popstate
ã€ãã³ãã§ãåŒã³åºãããŸããããã¯ãã¢ã¯ãã£ããªãã©ãŠã¶ãŒã®å±¥æŽã倿ŽããããšãïŒããšãã°ããŠãŒã¶ãŒã[æ»ã]ãã¿ã³ãæŒãããšãïŒã«çºçããŸãã
window.addEventListener('popstate', changePage);
ãããã£ãŠãã¢ã¯ãã£ãã¢ãŒããšããã·ãã¢ãŒããæã€éåžžã«åå§çãªã«ãŒãã£ã³ã°ã·ã¹ãã ãæ§ç¯ããŠããŸãã
ãŠãŒã¶ãŒããªã³ã¯ãã¯ãªãã¯ãããšã¢ã¯ãã£ãã¢ãŒããçºçãã pushState
ã䜿çšããŠURLã倿ŽããŸãããURLã倿Žããããšããã·ãã¢ãŒããçºçãã popstate
ã€ãã³ãããéç¥ãåãåããŸãã ãããã«ããŠãã changePage
ãåŒã³åºããŠãæ°ããURLã®èªã¿åããšããŒãžã®èªã¿èŸŒã¿ãåŠçããŸãã
æ°ããã³ã³ãã³ãã®è§£æãšè¿œå
éåžžãããã²ãŒãããŠããããŒãžã«ã¯ãããããŒãããã¿ãŒãªã©ã®åºæ¬çãªèŠçŽ ããããŸãã ãã¹ãŠã®ããŒãžã§æ¬¡ã®DOMæ§é ã䜿çšããŸãïŒããèªäœã¯Smashing Magazineã®æ§é ã§ãïŒã
<header> ⊠</header> <main> <div class="cc"> ⊠</div> </main> <footer> ⊠</footer>
åããŒãžã§å€æŽããå¿
èŠãããã®ã¯ã cc
ã³ã³ããã®ã³ã³ãã³ãã®ã¿ã§ãã ãããã£ãŠã changePage
ãããªchangePage
颿°ãäœæã§ããŸãã
var main = document.querySelector('main'); function changePage() {
ã¢ãã¡ãŒã·ã§ã³ïŒ
ãŠãŒã¶ãŒããªã³ã¯ãã¯ãªãã¯ãããšã changePage
颿°ã¯ãã®ããŒãžã® HTMLãåãåã ã cc
ã³ã³ãããŒãæœåºããŠmain
èŠçŽ ã«è¿œå ããŸãã çŸæç¹ã§ã¯ãããŒãžã«2ã€ã®cc
ã³ã³ãããŒããããŸãã1ã€ç®ã¯åã®ããŒãžã«å±ãã2ã€ç®ã¯æ¬¡ã®ããŒãžã«å±ããŸãã
次ã®é¢æ°ã§ããanimate
ã¯ãå€ãã³ã³ãããé衚瀺ã«ããŠãæ°ããã³ã³ããã衚瀺ããå€ãã³ã³ãããåé€ãã2ã€ã®ã³ã³ãããã¹ã ãŒãºã«ãªãŒããŒã©ãããããŸãã ãã®äŸã§ã¯ã Web Animations APIã䜿çšããŠå€èгã¢ãã¡ãŒã·ã§ã³ãäœæããŸãããä»ã®ä»»æã®ã¡ãœãããŸãã¯ã©ã€ãã©ãªã䜿çšã§ããŸãã
function animate(oldContent, newContent) { oldContent.style.position = 'absolute'; var fadeOut = oldContent.animate({ opacity: [1, 0] }, 1000); var fadeIn = newContent.animate({ opacity: [0, 1] }, 1000); fadeIn.onfinish = function() { oldContent.parentNode.removeChild(oldContent); }; }
çµæã®ã³ãŒãã¯Githubã§å
¥æã§ããŸã ã
ãããã¯ãã¹ãŠãããŒãžããã²ãŒã·ã§ã³ã®åºæ¬ã§ãã
泚æãšå¶é
äœæããã°ããã®å°ããªäŸã¯çæ³ãšã¯ã»ã©é ããã®ã§ãã å®éãå€ãã®ããšãèæ
®ããŠããŸããã
- æ£ãããªã³ã¯ãããããšã確èªããŠãã ããã
ãªã³ã¯ã®åäœã倿Žããåã«ã倿Žãå¿
èŠã§ããããšã確èªãããã§ãã¯ã远å ããå¿
èŠããããŸãã ããšãã°ã target="_blank"
ïŒæ°ããã¿ãã§ããŒãžãéãïŒãæã€ãã¹ãŠã®ãªã³ã¯ãå€éšãã¡ã€ã³ãžã®ãã¹ãŠã®ãªã³ã¯ãããã³Control/Command + click
ïŒæ°ããã¿ãã§ããŒãžãéãïŒãªã©ã®ä»ã®ç¹æ®ãªã±ãŒã¹ãç¡èŠããå¿
èŠããããŸãã - ã¡ã€ã³ã³ã³ããå€ã®ã¢ã€ãã ãæŽæ°ããŸãã
çŸæç¹ã§ã¯ãããŒãžã倿ŽãããŠããéã cc
ã³ã³ãããŒã®å€åŽã®ãã¹ãŠã®èŠçŽ ã¯åããŸãŸã§ãã ãã ããããã¥ã¡ã³ãã®title
ã active
ã¯ã©ã¹ã®ã¡ãã¥ãŒé
ç®ãããã³å Žåã«ãã£ãŠã¯ãµã€ãäžã®ä»ã®å€ãã®äŸåé¢ä¿ãªã©ããããã®èŠçŽ ã®äžéšã倿Žããå¿
èŠããããŸãïŒæåã§ã®ã¿å€æŽã§ããããã«ãªããŸããïŒã - JavaScriptã©ã€ããµã€ã¯ã«ç®¡çã
ããã§ãããŒãžã¯SPAãšã»ãŒåãããã«åäœããŸããSPAã§ã¯ããã©ãŠã¶ã¯ããèªäœã§ããŒãžã倿ŽããŸããã ãã®ãããç¹å®ã®ã€ãã³ãã®ãã€ã³ããšè§£æŸãããªãã£ã«ããµãŒãããŒãã£ã³ãŒããªã©ã®ãã©ã°ã€ã³ã®å®è¡ãªã©ãJavaScriptã®ã©ã€ããµã€ã¯ã«ãæåã§åŠçããå¿
èŠããããŸãã
ãã©ãŠã¶ã®ãµããŒã
ãã®ããã²ãŒã·ã§ã³ã¢ãŒãã®å¯äžã®èŠä»¶ã¯pushState
APIã§ãããã¯ãã¹ãŠã®ææ°ã®ãã©ãŠã¶ãŒã§å©çšå¯èœã§ã ã ãã®æ¹æ³ã¯ã鲿©çãªæ¹åãšããŠå®å
šã«æ©èœããŸãã ããŒãžã¯éåžžã®æ¹æ³ã§åŒãç¶ãã¢ã¯ã»ã¹ã§ããJavaScriptãç¡å¹ã«ãªã£ãŠããŠãWebãµã€ãã¯æ£åžžã«æ©èœãç¶ããŸãã
SPAãã¬ãŒã ã¯ãŒã¯ã䜿çšããŠããå Žåã¯ã代ããã«PJAXããã²ãŒã·ã§ã³ã䜿çšããŠãããã²ãŒã·ã§ã³ãé«éåããããšãæ€èšããŠãã ããã ãã®èŠè¿ãã«ãå€ããã©ãŠã¶ã®ãµããŒããååŸããããSEOã«åªãããµã€ããäœæããŸãã
é²ã
ããã€ãã®åŽé¢ãæé©åããããšã§ããã®æ¹æ³ãæå€§éã«æŽ»çšãç¶ããããšãã§ããŸãã æ¬¡ã®ããã€ãã®ããªãã¯ã¯ ãããã²ãŒã·ã§ã³ãé«éåã ããŠãŒã¶ãŒãšã¯ã¹ããªãšã³ã¹ã倧å¹
ã«æ¹åããŸãã
ãã£ãã·ã¥ã®äœ¿çš
loadPage
颿°ããããã«å€æŽããããšã§ãåçŽãªãã£ãã·ã¥ã远å ããŠãæ¢ã«ã¢ã¯ã»ã¹ããããŒãžãå床èªã¿èŸŒãŸããªãããã«ããããšãã§ããŸãã
var cache = {}; function loadPage(url) { if (cache[url]) { return new Promise(function(resolve) { resolve(cache[url]); }); } return fetch(url, { method: 'GET' }).then(function(response) { cache[url] = response.text(); return cache[url]; }); }
ãæ³åã®ãšããã ãã£ãã·ã¥APIãŸãã¯ãŠãŒã¶ãŒåŽã®ãã®ä»ã®æ°žç¶ã¹ãã¬ãŒãžïŒIndexedDBãªã©ïŒã§é·æãã£ãã·ã¥ã䜿çšã§ããŸãã
çŸåšã®ããŒãžã®ã¢ãã¡ãŒã·ã§ã³
ãã§ãŒããšãã§ã¯ãã§ã¯ãé·ç§»ãå®äºããåã«æ¬¡ã®ããŒãžãããŒãããŠæºåããå¿
èŠããããŸãã ãªã³ã¯ãã¯ãªãã¯ãããšããã«å€ãããŒãžã§ã¢ãã¡ãŒã·ã§ã³ãéå§ããŸããããã«ããããŠãŒã¶ãŒã¯å³å¿æ§ãšããã©ãŒãã³ã¹ã®ç¥èŠãåŸãããšãã§ããŸãã
promiseã䜿çšãããšããã®ãããªç¶æ³ã®åŠçã¯éåžžã«ç°¡åã«èŠãããããããŸããã .all
ã¡ãœããã¯ãåŒæ°ãšããŠæž¡ããããã¹ãŠã®ãããã¹ãæºããããåŸã«å®è¡ãããæ°ãããããã¹ãäœæããŸãã
次ã®ããŒãžãããªããŒããã
PJAXããã²ãŒã·ã§ã³ã䜿çšãããšããã©ãŠã¶ã¯æ°ããããŒãžã®ã¹ã¯ãªãããã¹ã¿ã€ã«ãè§£æããã³èšç®ããå¿
èŠããªããããããŒãžã¯ããã©ã«ãã®ããã²ãŒã·ã§ã³ã®ã»ãŒ2åã®é床ã§å€æŽãããŸãã
ãã ãããŠãŒã¶ãŒããªã³ã¯ã«ã«ãŒãœã«ãåããããšãã«æ¬¡ã®ããŒãžãããªããŒãããããšã«ãããããã«å
ãžé²ãããšãã§ããŸãã
ã芧ã®ãšãããã¯ãªãã¯ãšãããªã³ã°ã®éã®é
å»¶ã¯éåžž200ã300ããªç§ã§ãã ãã®æéã¯éåžžãæ¬¡ã®ããŒãžãããŒãããã®ã«ååã§ãã
ããããããã¯ç§ãã¡ã®åŽã«ç°¡åã«æ¥ãããšãã§ããŸãã ããšãã°ããªã³ã¯ã®é·ããªã¹ããããããŠãŒã¶ãŒããªã³ã¯ãä»ããŠããŒãžãã¹ã¯ããŒã«ããå Žåããªã³ã¯ãã«ãŒãœã«ã®äžã«ããããããã®ã¡ãœããã¯ãã¹ãŠã®ããŒãžãããªããŒãããŸãã
æ°ã¥ããŠèæ
®ã§ãããã1ã€ã®ãã€ã³ãã¯ããŠãŒã¶ãŒã®æ¥ç¶é床ãäºæž¬ããããšã§ãã ïŒããããããã¯ãå°æ¥Network Information APIã§å¯èœã«ãªãã§ããããïŒ
éšåæ€å
loadPage
颿°loadPage
ã¯ã cc
ã³ã³ããã®ã¿ãå¿
èŠã§ãããHTMLããã¥ã¡ã³ãå
šäœãååŸããŸãã ãµãŒããŒåŽã§èšèªã䜿çšããå Žåããªã¯ãšã¹ããç¹å®ã®AJAXãŠãŒã¶ãŒã³ãŒã«ããã®ãã®ãã©ããã確èªããããã§ããã°ãç®çã®ã³ã³ãããŒã®ã¿ã衚瀺ã§ããŸãã Headers APIã䜿çšããŠããªã¯ãšã¹ãã§ã«ã¹ã¿ã HTTPããããŒãéä¿¡ã§ããŸãã
function loadPage(url) { var myHeaders = new Headers(); myHeaders.append('x-pjax', 'yes'); return fetch(url, { method: 'GET', headers: myHeaders, }).then(function(response) { return response.text(); }); }
次ã«ããµãŒããŒåŽïŒãã®å Žåã¯PHPã䜿çšïŒã§ãå¿
èŠãªã³ã³ãããŒã衚瀺ããåã«ã«ã¹ã¿ã ããããŒãååšãããã©ããã倿ã§ããŸãã
if (isset($_SERVER['HTTP_X_PJAX'])) {
ããã«ãããHTTPã¡ãã»ãŒãžã®ãµã€ãºãåæžããããµãŒããŒã®è² è·ã軜æžãããŸãã
ãŸãšãããš
ãã®ææ³ãå€ãã®ãããžã§ã¯ãã«å°å
¥ããåŸãåå©çšå¯èœãªã©ã€ãã©ãªãéåžžã«åœ¹ç«ã€ãšæãããŸããã ããã«ãããæ¬¡åã¯æéãç¯çŽã§ããé·ç§»å¹æèªäœã«éäžã§ããŸãã
ãã®ããã«ããŠçãŸããBarba.js-å°ããªã©ã€ãã©ãªïŒå§çž®ç¶æ
ã§4 KBïŒããã®è€éãããã¹ãŠæœè±¡åããéçºè
ã«ããŠãã§ãããã§ã·ã³ãã«ãªAPIãæäŸããŸãã ãŸããããŸããŸãªèгç¹ãèæ
®ããæ¢æã®ãã©ã³ãžã·ã§ã³ããã£ãã·ã¥ãããªããŒããã€ãã³ããä»å±ããŠããŸãã ã©ã€ãã©ãªã¯ãªãŒãã³ãœãŒã¹ã§ãããGitHubã§å
¥æã§ããŸã ã
ãããã«
ã¹ã ãŒãºãªãªãŒããŒã©ãã广ãäœæããæ¹æ³ãPJAXããã²ãŒã·ã§ã³ã䜿çšããŠå¹æçã«ãµã€ããSPAã«å€ããããšã®é·æãšçæã«ã€ããŠèŠãŠããŸããã ç§»è¡èªäœã®å©ç¹ã«å ããŠãæ°ããããŒãžã®ããŒããé«éåããããã«ãåçŽãªãã£ãã·ã³ã°ãšããªããŒãã®ã¡ã«ããºã ãå°å
¥ããããšãæ€èšããŸããã
ãã®èšäºå
šäœã¯ãç§ã®å人çãªçµéšãšãåãçµãã ãããžã§ã¯ãã®ç§»è¡ã®å®è£
äžã«åŠãã ããšãåºã«ããŠããŸãã