ãã®ããŒãã§ã¯ããµãŒããŒããŒããçµäºãã Ractiveã§å圢ã®ãHello Worldããèšè¿°ã ã Webpackã䜿çšããŠããããã¹ãŠãåéããŸã ã
ãã®ãã¥ãŒããªã¢ã«ãèªã¿ç¶ãããã¹ãŠã®äººã«äºåã«æè¬ããŸãïŒ ãŠãããŒãµã«Webã¢ããªã±ãŒã·ã§ã³ã®ãããã¯ã«æ¬åœã«èå³ãããå Žåã¯ãåããããã¯ã«é¢ããäžé£ã®èšäºãReact + Express Universal Applicationsãããèªã¿ãã ããã ããã¯ãå€ãã®ã³ãŒããæžãã®ã奜ããªäººã«ã¯ç¹ã«èå³æ·±ãã§ãããïŒç§ã¯ããŸããïŒã
å
責äºé
ãã®ãã¥ãŒããªã¢ã«ã¯ãäž»ã«äžã¬ãã«ä»¥äžã®ããã³ããšã³ãéçºè
ã察象ãšããŠããŸãã 誰ãææ°ã®éçºããŒã«ã«ç²ŸéããŠãããSPAããã³ååãšã¯äœããç¥ã£ãŠããŸãã
ãã®ãã¥ãŒããªã¢ã«ã§ã¯ãnpmã¢ãžã¥ãŒã«ã®ã€ã³ã¹ããŒã«ãwebpackã®å ¥éãã³ãã³ãã©ã€ã³ããã®ä»ã®åºæ¬çãªæäœã®åé¡ã«ã€ããŠã¯èª¬æããŸããã ã»ãšãã©ã®èªè ã«ãšã£ãŠãåŠå¥³ç°å¢ãèšå®ããéçºããŒã«ã§äœæ¥ããããã®æ¥åžžçãªæäœãªã©ã ãšæããŸãã æ¢ã«ããªãã¿ã§ãããã°æžã¿ã§ãã
ãã®ãã¥ãŒããªã¢ã«ã§ã¯ãnpmã¢ãžã¥ãŒã«ã®ã€ã³ã¹ããŒã«ãwebpackã®å ¥éãã³ãã³ãã©ã€ã³ããã®ä»ã®åºæ¬çãªæäœã®åé¡ã«ã€ããŠã¯èª¬æããŸããã ã»ãšãã©ã®èªè ã«ãšã£ãŠãåŠå¥³ç°å¢ãèšå®ããéçºããŒã«ã§äœæ¥ããããã®æ¥åžžçãªæäœãªã©ã ãšæããŸãã æ¢ã«ããªãã¿ã§ãããã°æžã¿ã§ãã
ãµãŒããŒãå®æãããŸã
ãªã¯ãšã¹ããããã·
ãããã£ãŠãååã§èª¬æããéžæããããé«ã¬ãã«ãã¢ãŒããã¯ãã£ã«åºã¥ããŠãããã³ããšã³ããä»ããŠããã¯ãšã³ããµãŒããŒãžã®ãããã·èŠæ±ãæŽçããå¿ èŠããããŸãã
ãªããã
ãã¥ãŒããªã¢ã«ã®æåã®éšåãžã®ã³ã¡ã³ãã§ã¯ãåççãªè³ªåãå°ããããŸãã-ããã¯ãšã³ãã®ãããã·èŠæ±ã¯ãªãã§ããïŒ ååã¢ãããŒãã®èŠ³ç¹ãããããã¯äžè¬ã«å¿
èŠãããŸããã ç¹ã«å°é£ã奜ãã§ãéæšæºçãªåé¡ãèªæã§ãªãæ¹æ³ã§è§£æ±ºããå Žåã
ãã ããå®éã«ã¯ããã®æ¹æ³ãååã®å€ãã®è³ªåãšåé¡ã«å¯Ÿããæéã§æã䟿å©ã§çã¿ã®ãªã解決çã§ããããšã瀺ããŠããŸããããã«ã€ããŠã¯åŸã§èª¬æããŸãã
ããã«ããããã·ã¯ããã€ãã®è¿œå æ©èœãéããŸãã
ç ãªã©
ãã ããå®éã«ã¯ããã®æ¹æ³ãååã®å€ãã®è³ªåãšåé¡ã«å¯Ÿããæéã§æã䟿å©ã§çã¿ã®ãªã解決çã§ããããšã瀺ããŠããŸããããã«ã€ããŠã¯åŸã§èª¬æããŸãã
ããã«ããããã·ã¯ããã€ãã®è¿œå æ©èœãéããŸãã
- XSS / CSRF /ãªã©ã®ã¯ã©ã€ã¢ã³ãæ»æã®ååã
- ãµãŒããŒããã¯ãšã³ããé衚瀺ã«ãã+ããã¯ãšã³ãã§CORSãæå¹ã«ããå¿ èŠã¯ãããŸããã
- ããŒã¿ãšã¯ãšãªã®å圢ãã£ãã·ã³ã°ã®å¯èœæ§ã
- ã»ãã·ã§ã³/ããŒã¯ã³/ãªã©ã«ããããå®å šãªäœæ¥ã
- èŠæ±/å¿çãã€ã³ã¿ãŒã»ãããããã€ã³ããå€æŽããŸãã
- ã¯ã©ã€ã¢ã³ãã®ããŒãºã«åãããAPIã®é©å¿ã
- æ¿èªæ¹æ³ãå€æŽããŸãã
- åæãã¹ããŒãã¬ã¹ãããã¯ãšã³ãäžã§ã®éåæãã¹ããŒããã«ãæ©èœã®å®è£ ã®ç°¡çŽ åã
- ããã€ãã®ããã¯ãšã³ãïŒãŸãã¯ãã€ã¯ããµãŒãã¹ïŒã§äœæ¥ããŸãã
ç ãªã©
ãããè¡ãã«ã¯ã ãŸãRealWorldãããžã§ã¯ãã®REST APIã®ä»æ§ãæ€èšããŸã ã APIèªäœã¯ã conduit.productionready.io / apiã«ãããŸãã
äœåãªã³ãŒããæžãã®ã¯å¥œãã§ã¯ãªãã®ã§ãèªè»¢è»ãçºæãããã express-http-proxyã¢ãžã¥ãŒã«ã䜿çšãããããŸããã ãã®ã¢ãžã¥ãŒã«ã¯http-requestsããããã·ããã ãã§ãªãããã®ããã»ã¹ã®ããŸããŸãªæ®µéã«ããã€ãã®ããã¯ãæäŸããŸããããã¯æããã«ãäŸç¶ãšããŠæçšã§ãã
æåã«ãAPIã®åçŽãªjson-configãäœæããŸããããã§ã¯ããããã·ããURLãšããã€ãã®è¿œå èšå®ãå®çŸ©ããŸãã
./config/api.json
{ "backendURL": "https://conduit.productionready.io", "timeout": 3000, "https": true }
ãHttpsãïŒtrueã¯ãhttpãä»ããŠå ã®èŠæ±ãè¡ãããå Žåã§ããhttpsã«ãããã·ããå¿ èŠãããããšãæå³ããŸãã ããŒã«ã«ãã¹ãã§äœæ¥ãããšãã«äŸ¿å©ã§ãã
æåã®éšåã§ã¯ããªã¯ãšã¹ãããããã·ããããã®ç¹å¥ãªãapiããã«ãŠã§ã¢ããæºåããŸããã ãããæžãæéã§ãïŒ
./middleware/api.js
const proxy = require('express-http-proxy'); const config = require('../config/api.json'); module.exports = () => (req, res, next) => { proxy(config.backendURL, { https: config.https, timeout: config.timeout })(req, res, next); };
ããããããã§ååã§ãããã / api / *ããã³ããšã³ããžã®ãã¹ãŠã®ãªã¯ãšã¹ãã¯ããã§ã«ããã¯ãšã³ããµãŒããŒã«ãããã·ãããŸãã ã€ãŸããããã³ããšã³ããµãŒããŒããGET / api / articleããªã¯ãšã¹ããããšãå¿çã¯æ¬¡ã®åœ¢åŒã®JSONãè¿ããŸãã
{ "articles":[...], "articlesCount": 100 }
GETãªã¯ãšã¹ãã ãã§ãªããå¯èœãªãã¹ãŠã®RESTåè©ïŒPOST / PUT / DELETEïŒã§ãåäœããã¯ã©ã€ã¢ã³ãã§JSãªãã§ãªã¯ãšã¹ããå®è¡ããïŒã€ãŸããHTMLãã©ãŒã ã䜿çšããïŒããšãèšç»ããŠãããããã¡ã€ã³WebãµãŒããŒãã¡ã€ã«ã«æåã®éšåããããã€ãã®å€æŽãå ããŸãã
./server.js
const methodOverride = require('method-override');
server.use(express.json()); server.use(express.urlencoded({ extended: true })); server.use(methodOverride('_method'));
json ïŒajaxçµç±ïŒãšurlencoded ïŒãã©ãŒã éä¿¡ïŒã®äž¡æ¹ã§ãªã¯ãšã¹ãæ¬æã解æããããšã«æ³šæããŠãã ããã method-overrideã¢ãžã¥ãŒã«ã¯ãhttpãªã¯ãšã¹ãã¡ãœãããç¹å¥ãªã¯ãšãªURLã®_methodãã©ã¡ãŒã¿ãŒã§æå®ãããã¡ãœããã§äžæžãããŸãã ããã¯ãhtmlãã©ãŒã ãGETããã³POSTã¡ãœããã®ã¿ããµããŒããããšããäºå®ã«ãããã®ã§ãã 次ã®ããã«æ©èœããŸãã
<form action="/something?_method=PUT" method="POST"> ..... </form>
ã¯ã©ã€ã¢ã³ãã§JSãç¡å¹ã«ãªã£ãŠããŠããã©ãŒã ããµããã³åãããŠããå Žåããã®ã¢ãžã¥ãŒã«ã¯PUTã®å ã®POSTãèªåçã«çœ®ãæãããããã·ã¯ããã«ãããã·ããããã®æ£ããRESTåè©ãåãåããŸãã ã·ã³ãã«ã§è² æ ãªãã
httpã¡ãœããã®æžãæãã«ã€ããŠ
èå³æ·±ãäºå®ã¯ãRESTfulãµãŒãã¹ã®ãé人ããããã¯ãšã³ãã§REST APIãèšèšãããšãã«åæ§ã®ã¯ãšãªãã©ã¡ãŒã¿ãŒã䜿çšããããšãæšå¥šããŠããããšã§ãã ããŠãåæ§ã«ãHTTPã¡ãœããã®éããããªã¹ãã®ã¿ããµããŒãããAPIã®ã¯ã©ã€ã¢ã³ãã«ã€ããŠèããããšæããŸãã
ãã ããå®éã«ã¯ãããã¯ãã©ãŠã¶ããŒã¹ã®HTMLãã©ãŒã ã«ã®ã¿é¢é£ããŸãã ãã®å ŽåãããŸããŸãªã¿ã€ãã®ã¯ã©ã€ã¢ã³ãã«ãã£ãŠäœ¿çšãããããã¯ãšã³ãã§ãã®ãããªçãã±ãŒã¹ããµããŒãããããšã¯ããŸãåççã§ã¯ãããŸããã ãã®ã»ãšãã©ã¯ãã®æ©èœãå¿ èŠãšããŸããã åæã«ãããã³ããšã³ãããŒã¹ã®ãã¬ãŒã ã¯ãŒã¯ã§ãã®ææ³ã䜿çšããããšèªäœãæ£åœã§ãããæãéèŠãªããšã¯ãä»ã®ã¿ã€ãã®é¡§å®¢ã®å©çã«åœ±é¿ãäžããªãããšã§ãã ããã«ããã
ãã ããå®éã«ã¯ãããã¯ãã©ãŠã¶ããŒã¹ã®HTMLãã©ãŒã ã«ã®ã¿é¢é£ããŸãã ãã®å ŽåãããŸããŸãªã¿ã€ãã®ã¯ã©ã€ã¢ã³ãã«ãã£ãŠäœ¿çšãããããã¯ãšã³ãã§ãã®ãããªçãã±ãŒã¹ããµããŒãããããšã¯ããŸãåççã§ã¯ãããŸããã ãã®ã»ãšãã©ã¯ãã®æ©èœãå¿ èŠãšããŸããã åæã«ãããã³ããšã³ãããŒã¹ã®ãã¬ãŒã ã¯ãŒã¯ã§ãã®ææ³ã䜿çšããããšèªäœãæ£åœã§ãããæãéèŠãªããšã¯ãä»ã®ã¿ã€ãã®é¡§å®¢ã®å©çã«åœ±é¿ãäžããªãããšã§ãã ããã«ããã
å®å
šãªserver.jsã³ãŒã
const express = require('express'), helmet = require('helmet'), compress = require('compression'), cons = require('consolidate'), methodOverride = require('method-override'); const app = require('./middleware/app'), api = require('./middleware/api'), req = require('./middleware/req'), err = require('./middleware/err'); const config = require('./config/common'); const server = express(); server.engine('html', cons.mustache); server.set('view engine', 'html'); server.use(helmet()); server.use(express.json()); server.use(express.urlencoded({ extended: true })); server.use(methodOverride('_method')); server.use(compress({ threshold: 0 })); server.use(express.static('dist')); server.use(req()); server.all('/api/*', api()); server.use(app()); server.use(err()); server.listen(config.port);
çµæãšããŠãããã¯ãšã³ãã®ãªã¯ãšã¹ãã®å®å šãªãããã·ãšãhtmlãã©ãŒã ããã®ãªã¯ãšã¹ãã®ãµããŒãããããŸãã
åæç¶æ ã®åé¡
ããã§ãåæç¶æ ããšã¯ãã¢ããªã±ãŒã·ã§ã³ããŒã¿ã®åæç¶æ ã§ã¯ãªããã¢ããªã±ãŒã·ã§ã³ãèµ·åããããã®äžé£ã®å ¥åãã©ã¡ãŒã¿ãŒãæå³ããŸãã ç¹å®ã®ãããžã§ã¯ãããšã«ããã®ãããªãã©ã¡ãŒã¿ãŒã®ã»ããã¯ç°ãªããããŸã£ããååšããªãå ŽåããããŸãã åºæ¬ã±ãŒã¹-èš±å¯ã®æç¡ã
ã»ãšãã©ã®å Žåãåæç¶æ ã¯ä»¥åã®äœæ¥ã»ãã·ã§ã³äžã«ãŠãŒã¶ãŒã«ãã£ãŠèšå®ããïŒããšãã°ãã¢ã«ãŠã³ãããã°ã€ã³ããŠããïŒããŠãŒã¶ãŒãæ»ã£ãåŸããŠãŒã¶ãŒã¯ãã®åæç¶æ ã®ã¢ããªã±ãŒã·ã§ã³ã衚瀺ãããïŒãã°ã€ã³ããŠããïŒããšãæåŸ ããŸãã
æšæºã®SPAãæåã«ããŒãžã«ããŒããããã¹ã¯ãªãããèµ·åãããŠãããããŒã¿ã®èŠæ±ãæ¢ã«è¡ãããŠããŸãã ãã®æç¹ã§ãã¹ã¯ãªããã¯ãã©ãŠã¶ãŒã®ããŒã¿ãªã©ã«åºã¥ããŠåæç¶æ ãã©ã¡ãŒã¿ãŒãååŸããAPIã«ãªã¯ãšã¹ããéä¿¡ããã ãã§ãã
SPAãšã¯ç°ãªããååã¢ããªã±ãŒã·ã§ã³ã«ã¯ãæ¯èŒçèšãã°ãããŒãããŒããŒãããããŸããã ãã®éšåã®å圢ã¢ããªã±ãŒã·ã§ã³ã¯ãéåžžã®Webãµã€ãã«äŒŒãŠããŸãã ã€ãŸããæåã®åæãªã¯ãšã¹ãã®æç¹ã§åæç¶æ ãååŸããå¿ èŠããããŸããããã«ããããµãŒããŒã§ã¬ã³ããªã³ã°ãããããŒãžã¯ããŠãŒã¶ãŒãæåŸ ããç¶æ ãšå®å šã«äžèŽããŸãã ãã¡ãããéçºè ãæ ãè ã§ããããµãŒããŒãäœããã®ããã©ã«ãç¶æ ã§ããŒãžãã¬ã³ããªã³ã°ããã¯ã©ã€ã¢ã³ãäžã®ã¹ã¯ãªãããèµ·åãããã¯ã©ã€ã¢ã³ãããã¹ãŠã®äœæ¥ãå床å®è¡ããå ŽåããããŸãã ããã¯æ£ããã¢ãããŒãã§ã¯ãããŸãããããã®ãããžã§ã¯ãã®ãããã§ã¹ãã§ã¯æ確ã«èšè¿°ãããŠããŸã-æŸèæã¯ãããŸããïŒé ç®7ïŒïŒ
ãã®è³ªåã¯ãããããçŸæç¹ã§å©çšå¯èœãªå¯äžã®æ¹æ³ã§è§£æ±ºãããŠããŸã-ã¯ãããŒã®å©ããåããŠïŒå€ãã®äººããã¹ã¿ãŒãèŠããšãã«ããã«æšæž¬ãããšæããŸãïŒã ã¯ãã確ãã«ãCookieã¯å®éã«ã¯ããµãŒããŒãžã®è€æ°ã®åæãªã¯ãšã¹ãéã§åæç¶æ ã転éããå¯äžã®å ¬åŒãªæ¹æ³ã§ãã ãã¡ããããã©ãŠã¶ã«é¢ããŠã¯ã
èšæ¶åã®èŠ³ç¹ãããCookieã¯ããŸãé©åã§ã¯ãªãïŒ4Kbã®ã¿ïŒããšãååã«ç解ããŠãããããæãéèŠãªããšã¯ãæçµç¶æ ã䜿çšããå¿ èŠãããåæç¶æ ã®ãã©ã¡ãŒã¿ãŒããŸã ç解ããŠããªããããã»ãã·ã§ã³ã«é²ã¿ãŸãïŒ ãããã£ãŠãéåžžã®ã»ãã·ã§ã³ã§ã¯ãç¹å®ã®session_idïŒsidïŒãCookieã«æžã蟌ãŸãããã®èå¥åã«é¢é£ä»ããããããŒã¿ã®å±±å šäœããµãŒããŒã«ä¿åãããŸãã
äžè¬çã«èšãã°ããªãå¿ èŠãªã®ãã¯æããã ãšæããŸããç¹°ãè¿ããŸãããããã§ã¯äœãæãã€ããŸããã ã¯ã©ã·ãã¯ãšã¯ã¹ãã¬ã¹ã»ãã·ã§ã³ã«åå ã ããã©ã¡ãŒã¿ãŒãå°ãè©°ããŠãäžå¿ èŠãªå°é£ã䌎ããã«ãç§ãã¡èªèº«ã«éåžžã«æå¹ãªã¡ã«ããºã ãæäŸããŸãã
ã¢ãžã¥ãŒã«ã®èšå®ãå«ããã»ãã·ã§ã³ããªããžã§ã¯ããã¡ã€ã³æ§æã«è¿œå ããŸãã
./config/common.json
{ "port": 8080, "session": { "name": "_sid", "secret": "ntVA^CUnyb=6w3HAgUEh+!ur4gC-ubW%7^e=xf$_G#wVD53Cgp%7Gp$zrt!vp8SP", "resave": false, "rolling": false, "saveUninitialized": false, "cookie": { "httpOnly": true, "secure": false, "sameSite": true } } }
Cookieã®ããã€ãã®éèŠãªèšå®-åžžã«httpOnlyãšsameSiteãèšå®ããå¿ èŠããããŸã ã SSLã«åãæ¿ããå Žåã ã»ãã¥ã¢ãã¢ã¯ãã£ãã«ããããšãã§ããŸãïŒCookieã¯httpsãä»ããŠäœæ¥ããŠããå Žåã«ã®ã¿éä¿¡ãããŸãïŒã
ãã®ã¢ãžã¥ãŒã«ãWebãµãŒããŒãã¡ã€ã«ã«è¿œå ããŸãã
./server.js
const session = require('express-session');
server.use(session(config.session));
å®å
šãªserver.jsã³ãŒã
const express = require('express'), helmet = require('helmet'), compress = require('compression'), cons = require('consolidate'), methodOverride = require('method-override'), session = require('express-session'); const app = require('./middleware/app'), api = require('./middleware/api'), req = require('./middleware/req'), err = require('./middleware/err'); const config = require('./config/common'); const server = express(); server.engine('html', cons.mustache); server.set('view engine', 'html'); server.use(helmet()); server.use(session(config.session)); server.use(express.json()); server.use(express.urlencoded({ extended: true })); server.use(methodOverride('_method')); server.use(compress({ threshold: 0 })); server.use(express.static('dist')); server.use(req()); server.all('/api/*', api()); server.use(app()); server.use(err()); server.listen(config.port);
çŸåšããšããããè€æ°ã®åæãªã¯ãšã¹ãéã§ã¢ããªã±ãŒã·ã§ã³ã®åæç¶æ ãä¿åããã³éä¿¡ããã¡ã«ããºã ããããŸãã ãã®ã¿ã¹ã¯ã¯ãã©ãŠã¶ãæäœããæ©èœã«å®å šã«æºæ ããŠããããããã®ãããªããšã«ã¯ããã³ããšã³ããµãŒããŒãæé©ãªå Žæã§ãã ããã¯ãšã³ãã§ãã®ãããªããšãè¡ããšããã®ãããªããªãã¯ã®å¿ èŠããªãä»ã®ã¿ã€ãã®ã¯ã©ã€ã¢ã³ãïŒã¢ãã€ã«ãªã©ïŒã«åœ±é¿ãäžããå¯èœæ§ããããŸãã ããã¯ãŠãããŒãµã«Webã¢ããªã±ãŒã·ã§ã³ã®å©ç¹ã®1ã€ã§ããã»ãã¥ãªãã£ãæãªãããšãªããããã¯ãšã³ãããè©°ãŸããããå¿ èŠãªããããã³ããšã³ãã¿ã¹ã¯ãå®è¡ã§ããå®å šãªç°å¢ïŒããã³ããšã³ããµãŒããŒïŒãååŸããŸãã ããã¯ãããã¯ãšã³ããå€ãã®ã¿ã€ãã®ã¯ã©ã€ã¢ã³ãã§åäœããå Žåã«ç¹ã«åœãŠã¯ãŸããŸãã
ãããŒã¯ãŒã«ã
ããŠããããŸã§ã¯ããªãåçŽã«èŠããŸãããäžäœååã¯ã©ãã«ããã®ã§ããããïŒ ãã¹ãŠã®ãã®ã«ãªããŸãããæåã«ã ãµãŒããŒåŽã¬ã³ããªã³ã°ïŒSSRïŒãªã©ã®éšåãæ±ããŸãããã
å§ããããã«ã RactiveJSã䜿çšããŠåçŽãªå圢ã®ãhello worldããæžããŸããã ã
./src/app.js
const Ractive = require('ractive'); Ractive.DEBUG = (process.env.NODE_ENV === 'development'); Ractive.DEBUG_PROMISES = Ractive.DEBUG; Ractive.defaults.enhance = true; Ractive.defaults.lazy = true; Ractive.defaults.sanitize = true; const options = { el: '#app', template: `<div id="msg">Static text! + {{message}} + {{fullName}}</div>`, data: { message: 'Hello world', firstName: 'Habr', lastName: 'User' }, computed: { fullName() { return this.get('firstName') + ' ' + this.get('lastName'); } } }; module.exports = () => new Ractive(options);
ãã®ãã¡ã€ã«ã¯ãååã¢ããªã±ãŒã·ã§ã³ãžã®ãšã³ããªãã€ã³ãã§ãã ãã£ãšè©³ããèŠãŠã¿ãŸãããã
RactiveJSã¯ãRactiveã€ã³ã¹ã¿ã³ã¹ãšRactiveã³ã³ããŒãã³ããäœæã§ããåãååã®ã³ã³ã¹ãã©ã¯ã¿ãŒããšã¯ã¹ããŒãããŸãïŒããã«ã€ããŠã¯æ¬¡ã®ã»ã¯ã·ã§ã³ã§è©³ãã説æããŸãïŒã VueJSã¯å€ãã®äººã«ãã®ã¢ãããŒããæãåºããããããããŸããããããã¯å¶ç¶ã§ã¯ãããŸããã å®éã Ractiveã¯Vueã®ãããã¿ã€ãã®1ã€ã§ããããããã®APIã¯ãŸã éåžžã«ãã䌌ãŠããŸãã
ããããã³ãŒãã«æ»ããæåã«ããã®ã¢ããªã±ãŒã·ã§ã³ã«èšå®ããã³ã³ã¹ãã©ã¯ã¿ãŒã®éçããããã£ãèŠã€ããŸãã ãŸãããããã¯Ractive.DEBUGãšRactive.DEBUG_PROMISESã§ãããçŸåšã®ç°å¢ã«å¿ããŠæ å ±ãšã©ãŒã¡ãã»ãŒãžãæå¹ãŸãã¯ç¡å¹ã«ããŸãã
次ã«ã Ractive.defaults.enhanceãã©ã°ããããŸããããã¯ãååã®éèŠãªåŽé¢ã®1ã€ãã¢ã¯ãã£ãã«ããŸããããã¯ãã¯ã©ã€ã¢ã³ãåŽã§SSRã®çµæãšããŠååŸãããããŒã¯ã¢ãããåå©çšããŸãã ããã¯çŸåšããã°ãã°äžæçãªçšèªãã€ãã¬ãŒããšåŒã°ããŠããŸãã
ãã€ãã¬ãŒãçè«
ç°¡åãªæ¹æ³ã§ã¯ãã¯ã©ã€ã¢ã³ãã§ã¢ããªã±ãŒã·ã§ã³ãåæåããåŸããµãŒããŒïŒSSRïŒããã®ããŒã¯ã¢ããã§ã¯ãªãããã¹ãŠã®ããŒã¯ã¢ãããåã¬ã³ããªã³ã°ãããå ŽåããããŸãã ååã«ãšã£ãŠéåžžã«æªããšããããã§ã¯ãããŸãã-ç§ãã¡ã¯ãŸã SEOã®ãµããŒããšä»ã®å€ãã®å©ç¹ãåŸãŠããŸãã ãã ãããããã«ããŠããããã¯éåžžã«éåççã§ãã
ãããã£ãŠã SSRãå®è¡ã§ããã ãã§ãªããå€ãã®äººãSSRãå®è¡ã§ããããã«ãªãããšãéèŠã§ãã ãã¬ãŒã ã¯ãŒã¯ããã®ããã€ãã¬ãŒã·ã§ã³ããå®è¡ã§ããã®ã¯è¯ãããšã§ãã ã¢ããªã±ãŒã·ã§ã³ãŸãã¯ãã®åã ã®ã³ã³ããŒãã³ãã®ç¹°ãè¿ãã¬ã³ããªã³ã°ã®çµæãåãããŒã¯ã¢ããã«ãªãããšãç解ããããã«ãçŸåšã®ããŒã¯ã¢ãããšããŒã¿ãåæã§ããŸããã€ãŸããããã¯å¿ èŠã§ã¯ãããŸããïŒãŸãã¯å¿ èŠã§ãããéšåçã«ïŒã ããã«ãæ¢åã®ããŒã¯ã¢ãããåã«ã埩掻ããããŸãã å¿ èŠãªãã¹ãŠã®ã€ãã³ãã©ã€ã»ã³ãµãŒããã³ãã©ãŒããŸãã¯å¿ èŠãªãã®ããã¹ãŠããã³ã°ãããŸãã
æ¯èŒçæè¿ããããããã°ã¹ãªãŒãã®ä»£è¡šè å šå¡ãããããã¹ãŠè¡ãããšãã§ããŸããã Ractiveã¯ããããã£ãšæ©ãç¥ã£ãã®ã§ãåå¿ç©ã«ãã£ãŠå°å ¥ãããçšèªãæ°Žåç©ãã®ä»£ããã«ç¬èªã®çšèªã enhance ãã䜿çšããŸãã åœæã¯ãããªèšèããªãã£ãã ãã§ãïŒïŒïŒ
ãããã£ãŠã SSRãå®è¡ã§ããã ãã§ãªããå€ãã®äººãSSRãå®è¡ã§ããããã«ãªãããšãéèŠã§ãã ãã¬ãŒã ã¯ãŒã¯ããã®ããã€ãã¬ãŒã·ã§ã³ããå®è¡ã§ããã®ã¯è¯ãããšã§ãã ã¢ããªã±ãŒã·ã§ã³ãŸãã¯ãã®åã ã®ã³ã³ããŒãã³ãã®ç¹°ãè¿ãã¬ã³ããªã³ã°ã®çµæãåãããŒã¯ã¢ããã«ãªãããšãç解ããããã«ãçŸåšã®ããŒã¯ã¢ãããšããŒã¿ãåæã§ããŸããã€ãŸããããã¯å¿ èŠã§ã¯ãããŸããïŒãŸãã¯å¿ èŠã§ãããéšåçã«ïŒã ããã«ãæ¢åã®ããŒã¯ã¢ãããåã«ã埩掻ããããŸãã å¿ èŠãªãã¹ãŠã®ã€ãã³ãã©ã€ã»ã³ãµãŒããã³ãã©ãŒããŸãã¯å¿ èŠãªãã®ããã¹ãŠããã³ã°ãããŸãã
æ¯èŒçæè¿ããããããã°ã¹ãªãŒãã®ä»£è¡šè å šå¡ãããããã¹ãŠè¡ãããšãã§ããŸããã Ractiveã¯ããããã£ãšæ©ãç¥ã£ãã®ã§ãåå¿ç©ã«ãã£ãŠå°å ¥ãããçšèªãæ°Žåç©ãã®ä»£ããã«ç¬èªã®çšèªã enhance ãã䜿çšããŸãã åœæã¯ãããªèšèããªãã£ãã ãã§ãïŒïŒïŒ
Ractive.defaults.enhance = true;
ããã©ã«ãã§ã¯ããã®ãã©ã°ã¯falseã«èšå®ãããŠããããã®ã³ãŒãè¡ã¯ãã¹ãŠã®Ractiveã³ã³ããŒãã³ãã«å¯ŸããŠãã®æ©èœãããã«ã¢ã¯ãã£ãã«ããŸãã ã€ãŸãã1è¡ã®ã³ãŒãã§ã Ractiveã¢ããªã±ãŒã·ã§ã³ã«ãµãŒããŒããã®ããŒã¯ã¢ãããåå©çšãããããšãã§ããŸãã åæã«ãç¹å®ã®ã³ã³ããŒãã³ããçªç¶ããã€ãã¬ãŒã·ã§ã³ããå¿ èŠãšããªãå Žåããã®ãªãã·ã§ã³ã䜿çšããŠããŒã«ã«ã§ç¡å¹ã«ããããšãã§ããŸã ã
æåŸã«ãç§ãåžžã«èšå®ããããã«2ã€ã®ãã©ã°ïŒ
- Ractive.defaults.sanitizeã䜿çšãããšããã³ãã¬ãŒãã®è§£æ段éã§å®å šã§ãªãHTMLã¿ã°ãã«ããã§ããŸãã
- Ractive.defaults.lazyã¯ã åæ¹åãã€ã³ãã£ã³ã° ïŒã¯ããäºéãã€ã³ãã£ã³ã°ãã©ã€ãïŒãããã«å®è¡ïŒããŒã¢ãããããŒããŠã³ïŒãã代ããã«ãé 延DOMã€ãã³ãïŒå€æŽããŒããïŒã䜿çšããããã«ãã¬ãŒã ã¯ãŒã¯ã«æ瀺ããŸãã
åæ¹åãã€ã³ãã£ã³ã°ã«ã€ããŠ
ç§ã¯ããã¹ãŠã®ãåæ¹åãã€ã³ãã£ã³ã°ãå«æªè
ãšãäžæ¹åããŒã¿ãããŒãæ奜家ã«ãã³ã¡ã³ãã§ããªããŒãæ§ãããããé¡ãããŸãã ããã¯å®æã®åé¡ã§ã¯ãããŸããã äºéããŒã¿ãã€ã³ãã£ã³ã°ãå人çãªè
åšã§ãããšæãããå Žåã¯ãããã䜿çšããªãã§ãã ããã 決ããŠèªåãå±éºã«ãããããšã¯ãããŸããã
ç§ã®ã¢ããªã±ãŒã·ã§ã³ã§ã¯ãååãšããŠãå¿ èŠãã€äŸ¿å©ãªå Žæã§äºéãã€ã³ãã£ã³ã°ãç©æ¥µçã«äœ¿çšããŸããããããŸã§ã®ãšãããæ·±å»ãªåé¡ã¯çºçããŠããŸããã 幞ããªããšã«ã Ractiveã¯ããã»ã©å®æçãªæ çµã¿ã§ã¯ãªããéçºè ã«ç¬èªã®é埳çããã³å«ççååãå°å ¥ããŸããã Ractiveã䜿çšããã¢ãããŒãã¯ã圌ã®ãµã€ãã®ããŒã ããŒãžã§è©³ãã説æãããŠããŸãã
ãã®åŸãç¹å®ã®ã³ã³ããŒãã³ãïŒ åæ¹åïŒãªãã·ã§ã³ã§true ïŒãŸãã¯ç¹å®ã®å ¥åãã£ãŒã«ãïŒdirective twoway = "true" ïŒã§æå¹ã«ããå Žåãé€ãããã¹ãŠã®ã³ã³ããŒãã³ãã¯äºéãªã³ã¯ã®å¯èœæ§ã倱ããŸãã ããã¯é垞䟿å©ã§ãã
ç§ã®ã¢ããªã±ãŒã·ã§ã³ã§ã¯ãååãšããŠãå¿ èŠãã€äŸ¿å©ãªå Žæã§äºéãã€ã³ãã£ã³ã°ãç©æ¥µçã«äœ¿çšããŸããããããŸã§ã®ãšãããæ·±å»ãªåé¡ã¯çºçããŠããŸããã 幞ããªããšã«ã Ractiveã¯ããã»ã©å®æçãªæ çµã¿ã§ã¯ãªããéçºè ã«ç¬èªã®é埳çããã³å«ççååãå°å ¥ããŸããã Ractiveã䜿çšããã¢ãããŒãã¯ã圌ã®ãµã€ãã®ããŒã ããŒãžã§è©³ãã説æãããŠããŸãã
ä»ã®ãã¬ãŒã ã¯ãŒã¯ãšã¯ç°ãªãã Ractiveã¯ããªãã®ããã«æ©èœããŸãã ããã®éã§ã¯ãããŸããã 䜿çšãããä»ã®ããŒã«ã«ã€ããŠã¯æèŠããããŸããã ãŸããå¿ èŠãªã¢ãããŒãã«ãé©å¿ããŸãã ãã¬ãŒã ã¯ãŒã¯åºæã®èãæ¹ã«çžãããŠããããã§ã¯ãããŸããã äœããã®çç±ã§ããŒã«ã®1ã€ãå«ããªå Žåãå¥ã®ããŒã«ã«ç°¡åã«äº€æããŠã人çãé²ããããšãã§ããŸããäºéãã€ã³ãã£ã³ã°ãå«ããŸãã¯æãå Žåããã®åé¡ã¯Ractive㧠1è¡ã§è§£æ±ºãããŸã ã
Ractive.defaults.twoway = false;
ãã®åŸãç¹å®ã®ã³ã³ããŒãã³ãïŒ åæ¹åïŒãªãã·ã§ã³ã§true ïŒãŸãã¯ç¹å®ã®å ¥åãã£ãŒã«ãïŒdirective twoway = "true" ïŒã§æå¹ã«ããå Žåãé€ãããã¹ãŠã®ã³ã³ããŒãã³ãã¯äºéãªã³ã¯ã®å¯èœæ§ã倱ããŸãã ããã¯é垞䟿å©ã§ãã
æ withãªèå³æ·±ãã±ãŒã¹
æµæã§ããŸããã§ããã å®éã®ãšããã é
延ã¯åã³ã³ããŒãã³ãã«å¯ŸããŠã°ããŒãã«ããã³ããŒã«ã«ã«é©çšã§ããã ãã§ãªããå
¥åãã£ãŒã«ããã£ã¬ã¯ãã£ããšããŠãåå¥ã«é©çšã§ããŸãã ããã«ã lazyã¯ããŒã«å€ã ãã§ãªããæ°å€ïŒé
延ããããªç§æ°ïŒãåãå
¥ããããšãã§ããŸãã
ãã®çŽ æŽãããããããã£ã䜿çšãããšãããšãã°ãæãäžè¬çãªWebéçºã¿ã¹ã¯ã®1ã€ã§ããæ€çŽ¢ã¯ãšãªè¡ãéåžžã«è¿ éãã€ç°¡æœã«è§£æ±ºã§ããŸãã
å®æœäŸ
ãã®çŽ æŽãããããããã£ã䜿çšãããšãããšãã°ãæãäžè¬çãªWebéçºã¿ã¹ã¯ã®1ã€ã§ããæ€çŽ¢ã¯ãšãªè¡ãéåžžã«è¿ éãã€ç°¡æœã«è§£æ±ºã§ããŸãã
<input type="search" value="{{q}}" lazy="1000"/>
å®æœäŸ
ãã®çµæãã¢ããªã±ãŒã·ã§ã³ã€ã³ã¹ã¿ã³ã¹ã®ãªãã·ã§ã³ãæã€ãªããžã§ã¯ããäœæããŸãã ã¢ããªã±ãŒã·ã§ã³ãã¬ã³ããªã³ã°ããæ¢åã®DOMèŠçŽ ïŒ el ïŒãéç¥ããå Žåã¯ãæååãšããŠãããçš®ã®ãã¹ããã³ãã¬ãŒãïŒ template ïŒãå®çŸ©ããŸãã ããŒã¿ïŒ data ïŒãš1ã€ã®èšç®ãããããããã£ïŒ fullName ïŒã§ãªããžã§ã¯ããå®çŸ©ããŸãã
ä»ã®ãšãããHello worldãã®ã¿ãèšè¿°ãããã®ç®çã¯ã¯ã©ã€ã¢ã³ãã§SSRãšããã€ãã¬ãŒã·ã§ã³ãããã¹ãããããšãªã®ã§ãããªã銬鹿ããŠããããã«èŠããŸãã åŸã§ããã確èªããæ¹æ³ã説æããŸãã
次ã«ããµãŒããŒãã¢ããªããã«ãŠã§ã¢ããäœæããŸããããã«ã¯ããããã·ã«åé¡ãããªããã¹ãŠã®ãªã¯ãšã¹ããå«ãŸããŸãã
./middleware/app.js
const run = require('../src/app'); module.exports = () => (req, res, next) => { const app = run(); const meta = { title: 'Hello world', description: '', keywords: '' }, content = app.toHTML(), styles = app.toCSS(); app.teardown(); res.render('index', { meta, content, styles }); };
ããã§ã¯ãã¹ãŠãç°¡åã§ãã æ°ã¥ããå Žåãã¡ã€ã³ã¢ããªã±ãŒã·ã§ã³ãã¡ã€ã«ïŒ./src/app.jsïŒã¯ãæ°ããRactiveã€ã³ã¹ã¿ã³ã¹ãè¿ãé¢æ°ããšã¯ã¹ããŒãããŸãã æ¬è³ªçã«æ°ããã¢ããªã±ãŒã·ã§ã³ãªããžã§ã¯ãã ã¯ã©ã€ã¢ã³ãã§ã³ãŒããå®è¡ãããå Žåãããã¯ããŸãéèŠã§ã¯ãããŸãããã¿ãå ã«è€æ°ã®ã¢ããªã±ãŒã·ã§ã³ã€ã³ã¹ã¿ã³ã¹ãäœæããããšã¯ã»ãšãã©ãããŸããã ãã ãããã¹ããŒããã«ãnodejsãµãŒããŒã§ã³ãŒããå®è¡ããã«ã¯ãåæèŠæ±ããšã«æ°ããã¢ããªã±ãŒã·ã§ã³ãªããžã§ã¯ããäœæã§ããããšãéåžžã«éèŠã§ãã ããã¯æããã ãšæããŸãã
ãã®ããããªã¯ãšã¹ãæã«ãæ°ããã¢ããªã±ãŒã·ã§ã³ãªããžã§ã¯ããäœæããŸãã ã¡ã¿ã¿ã°ïŒãŸã éçïŒã§ãªããžã§ã¯ããäœæããæªåé«ãSSRã§2è¡ã®ã³ãŒããäœæããŸãã
- app.toHTMLïŒïŒ -ã¢ããªã±ãŒã·ã§ã³ã®çŸåšã®ç¶æ ãæååã«ã¬ã³ããªã³ã°ããŸãã
- app.toCSSïŒïŒ -åå空éã«ãã£ãŠæ¢ã«å£ããŠãããã¹ãŠã®ãã³ã³ããŒãã³ãåºæã®ãã¹ã¿ã€ã«ãåéããæååãšããŠè¿ããŸãã
ãšãŠãã·ã³ãã«ã§ã Ractiveã§åäœããŸã ã ãããŠãã¯ãã ã³ã³ããŒãã³ã ã¹ã¿ã€ã«ã®æ©èœã¯æ¢ã«ç®±ããåºããŠããŸãã
次ã«ã teardownïŒïŒã¡ãœãããåŒã³åºããŠçŸåšã®ã¢ããªã±ãŒã·ã§ã³ã€ã³ã¹ã¿ã³ã¹ãç Žæ£ãããµãŒããŒãã³ãã¬ãŒã"./views/index.html"ãåä¿¡ããå€ã§ã¬ã³ããªã³ã°ããå¿çãéä¿¡ããŸãã ããã¯å šäœãšããŠçŽ æŽãããæãããSSRã§ãã
ãµãŒããŒãã³ãã¬ãŒã
ä»ãå°ããå£ã²ããã ãã®ããããµãŒããŒãã³ãã¬ãŒãã眮ããã"./views"ãã©ã«ããŒãããããã®äžã«ãã°ãããå圢ã¢ããªã±ãŒã·ã§ã³ãã¬ã³ããªã³ã°ããããŸãã« "åäžããŒãž"ã®index.htmlãæåŸ ãããŸãã ãã ãã index.htmlã¯äœæããŸããã
ããã¯ã Webpackã«ãã£ãŠçæãããæçµçãªã¯ã©ã€ã¢ã³ããã³ãã«ãããã«ãäžã«ãã®ãã«ãã䜿çšããŠç»é²ãããå¿ èŠãããããã§ãã ãããã£ãŠã ãã³ãã¬ãŒãçšã®ãã³ãã¬ãŒããäœæããå¿ èŠããããŸãïŒ ;-)
./views/_index.html
<!doctype html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="description" content="{{ meta.description }}"> <meta name="keywords" content="{{ meta.keywords }}"/> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0"> <title>{{ meta.title }}</title> <link rel="stylesheet" href="//code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css"> <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Titillium+Web:700|Source+Serif+Pro:400,700|Merriweather+Sans:400,700|Source+Sans+Pro:400,300,600,700,300italic,400italic,600italic,700italic"> <link rel="stylesheet" href="//demo.productionready.io/main.css"> <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico"> <link rel="icon" type="image/png" href="/img/favicon.png"> <link rel="apple-touch-icon" href="/img/favicon.png"> <link rel="manifest" href="/manifest.json"> <style> {{& styles }} </style> </head> <body> <div id="app"> {{& content }} </div> <script> window.msgEl = document.getElementById('msg'); </script> </body> </html>
ããã«ãç¹å¥ãªãã®ã¯ãããŸãããdoctype ãããããçš®é¡ã®ã¡ã¿ã¿ã°ã RealWorldãæäŸããã¹ã¿ã€ã«ãã¡ã€ã«ããã©ã³ããžã®ãªã³ã¯ãªã©ãåããå®å šãªHTMLãã¡ã€ã«ãªã©ã§ãã ããã§ã¯ã ã¿ã€ãã« ã 説æ ã ããŒã¯ãŒãã®ã¡ã¿ã¿ã°ãã©ã®ããã«ãã³ãã¬ãŒãåããããã確èªããŸãã ãŸããã³ã³ããŒãã³ãã®ã¹ã¿ã€ã«ã¯ããŒãžã®ã¹ã¿ã€ã«ã¿ã°ã«é 眮ãããã ãã§ãã ãããŠãã¡ãããã¬ã³ããªã³ã°ãããã¢ããªã±ãŒã·ã§ã³ã¯ã察å¿ããèå¥åãæã€èŠçŽ ã«é 眮ãããŸãã ã¢ããªã±ãŒã·ã§ã³ãã¯ã©ã€ã¢ã³ãäžã®ããŒã¯ã¢ãããæ€çŽ¢ããŠãæ°ŽçŽ åãããã®ã¯ããã®htmlèŠçŽ å ã§ãïŒåŸæ¥ã®SPAã¢ãããŒãã§ã¯é垞空ã®#appã¿ã°ïŒ
ã¯ã©ã€ã¢ã³ãã³ãŒãããã«ãããåŸã Webpackã¯ãã®ãã¡ã€ã«ã®æåŸïŒ bodyã®çµäºã¿ã°ã®çŽåïŒã«ãæ¥ç¶ã·ãŒã±ã³ã¹å ã®ãã¹ãŠã®ãã³ãã«ãã¡ã€ã«ãæžã蟌ã¿ããããžã§ã¯ããšãã®æ§æã§æå®ãããååãä»ããæçµçãªindex.htmlãçæããŸãïŒæ¬¡ã®ã»ã¯ã·ã§ã³ã§è©³ãã説æããŸãïŒ ïŒ ãããã©ããããããã§ãã
Webpack
ååã¢ããªã±ãŒã·ã§ã³ãäœæããããã«ç¹å®ã®ããŒã«ã䜿çšããªãããšãçŽæããŸããã Webpackãšãã®æ§æã«ãåãããšãåœãŠã¯ãŸããŸãã
ãããã£ãŠãåã®ãã¢ãããžã§ã¯ãã§äœ¿çšããæ¢åã®webpackæ§æïŒæåã®éšåïŒããã®ãŸãŸäœ¿çšãããã©ãŒã ã§ãã®ãŸãŸäœ¿çšããŸãã ããã«ããã®æ§æã¯ãä»ã®ãããžã§ã¯ããããã®ãã¢ãããžã§ã¯ãã«ãåãå ¥ããããå®éã«ã¯ãã¡ã€ãã©ã€ãºãããŠããŸããã§ããã ãããã®ãããžã§ã¯ãã«ã¯åºæ¬çã«å¿ èŠã®ãªãåæ©çãªãã®ããããŸãããããããã¯éªéããããç§ã¯ããããã«ããããã®ãé¢åã§ããã ããŸããããŸãã
ååãããã¯ã«é¢ãããã¥ãŒããªã¢ã«ãã¹ã¿ãŒã¿ãŒãããã®å€§éšåãšã¯ç°ãªããã¯ã©ã€ã¢ã³ããšãµãŒããŒã«å¥ã ã®webpackæ§æãäœæããŸããã ããã«ããµãŒããŒçšã®ãã³ãã«ã¯ãŸã£ããäœæããŸããã ãµãŒããŒäžã®ãã¹ãŠã®ã¢ããªã±ãŒã·ã§ã³ãã¡ã€ã«ã¯ãæäœãªãã§æ©èœããŸãã
ãªããã
ç°¡åãªçãïŒãµãŒããŒã«ãšã£ãŠãã¢ã»ã³ããªã¯å®çšçã§ã¯ãããŸããã
ãã詳现ã«ã¯ãç§ã¯ãã°ãã°ããŸããŸãªããŸããŸãªç°å¢ã§äœæ¥ããå¿ èŠããããå®å šã«ç§ã®ç®¡çäžã«ããã®ã¯ããã³ããšã³ããµãŒããŒã ãã§ãã ãã®ãããå¶åŸ¡ãããç°å¢ã®æ©èœã«åºã¥ããŠã³ãŒããèšè¿°ããã®ã«æ £ããŠããŸãããµãŒããŒã§ã¯ãããããçš®é¡ã®ãã©ã³ã¹ãã€ã©ãŒãã³ãŒãã®çž®å°ãããã«ã¯ãã³ãã«ãžã®ã³ãŒãã¢ã»ã³ããªã¯å¿ èŠãããŸããã ãã ããå¶åŸ¡ãããŠããªãç°å¢ã®ç¡å¶éã®ãªã¹ãã«ã¯ããããã¹ãŠãå¿ èŠã§ãããããã¯ã©ã€ã¢ã³ãã³ãŒãã¯ãã¹ãŠã®ããªãã¯ãšãšãã«webpackã«ãã£ãŠåéãããŸãã
ãã詳现ã«ã¯ãç§ã¯ãã°ãã°ããŸããŸãªããŸããŸãªç°å¢ã§äœæ¥ããå¿ èŠããããå®å šã«ç§ã®ç®¡çäžã«ããã®ã¯ããã³ããšã³ããµãŒããŒã ãã§ãã ãã®ãããå¶åŸ¡ãããç°å¢ã®æ©èœã«åºã¥ããŠã³ãŒããèšè¿°ããã®ã«æ £ããŠããŸãããµãŒããŒã§ã¯ãããããçš®é¡ã®ãã©ã³ã¹ãã€ã©ãŒãã³ãŒãã®çž®å°ãããã«ã¯ãã³ãã«ãžã®ã³ãŒãã¢ã»ã³ããªã¯å¿ èŠãããŸããã ãã ããå¶åŸ¡ãããŠããªãç°å¢ã®ç¡å¶éã®ãªã¹ãã«ã¯ããããã¹ãŠãå¿ èŠã§ãããããã¯ã©ã€ã¢ã³ãã³ãŒãã¯ãã¹ãŠã®ããªãã¯ãšãšãã«webpackã«ãã£ãŠåéãããŸãã
åã«ãèšã£ãããã«ã Webpackã®äœ¿çšæ¹æ³ã®åŠç¿ã¯ãã®ãã¥ãŒããªã¢ã«ã®ç¯å²å€ã§ãã ç§ã®ä»äºã¯ãç¹å®ã®æ§æãªãã§ãŠãããŒãµã«Webã¢ããªã±ãŒã·ã§ã³ãäœæã§ããããšã瀺ãããšã§ãã ããã§ããããã€ãã®éèŠãªç¹ã«æ³šæãæããŸãã
ãšã³ããªãã€ã³ãã¯ãã / src / app.jsã§ãã
entry: { app: [ path.resolve(__dirname, 'src/app'), ] },
ãã³ãã«ã¯./distãã©ã«ããŒã«çæããã次ã®ã«ãŒã«ã«åŸã£ãŠååãä»ããããŸãã
output: { path: path.resolve(__dirname, 'dist'), publicPath: '/', filename: `[name]-${ VERSION }.bundle.[hash].js`, chunkFilename: `[name]-${ VERSION }.bundle.[chunkhash].js`, },
ãµãŒãããŒãã£ã¢ãžã¥ãŒã«ãé€ããã¹ãŠã®ã³ãŒãã¯Babelãä»ããŠæž¡ãããŸãã
{ test: /\.(js)$/, exclude: /(node_modules|bower_components)/, loader: 'babel-loader', },
æ§æã®äžã§æãç©è°ãéžãéšåïŒ
new WrapperPlugin({ test: /\app(.*).js$/, header: '(function(){"use strict";\nreturn\t', footer: '\n})()();' }),
以åã¯ãã¢ããªãã³ãã«å šäœã«ã¹ããªã¯ãã¢ãŒããããã«é©çšããããã«ãçŽç²ã«WrapperPluginã䜿çšããŠããŸããããã ãããŠãããŒãµã«Webã¢ããªã±ãŒã·ã§ã³ã®å Žåã¯ãIIFEãšããŠã¢ããªã±ãŒã·ã§ã³ããšã¯ã¹ããŒãããããã«ã䜿çšããŸãããã³ãã«ãããŠã³ããŒãããããããã«ã¢ããªã±ãŒã·ã§ã³ãå®è¡ããŠãã ãããæ®å¿µãªãããWebPACKã®ã¯ãšããŠçåœç¶æããµããŒãããŠããŸãlibraryTargetããããããããã¯å圢ãããžã§ã¯ãã«è¿œå ããæ§æã®å¯äžã®éšåã§ãã圌ã§ãã圌女ãšçŽæ¥é¢ä¿ã¯ãããŸããããç§ã¯æåã§é¢æ°ãåŒã³åºãããšãã§ããŸãã
次ã«ããã¹ãŠã®ãµãŒãããŒãã£ã¢ãžã¥ãŒã«ãåå¥ã®ãã³ãã«ã«å ¥ããŸãã
new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: ({ resource }) => ( resource !== undefined && resource.indexOf('node_modules') !== -1 ) }),
çŽæã©ããããã³ãã¬ãŒãã®ãã³ãã¬ãŒãã®æåŸã«ãã³ãã«ãè¿œå ããindex.htmlãçæããŠããŸãïŒ
new HtmlWebpackPlugin({ template: './views/_index.html', filename: '../views/index.html', minify: { html5: true }, hash: true, cache: true, showErrors: false, }),
åºåãã£ã¬ã¯ããªãã¯ãªãŒã³ã¢ããããéçã¢ã»ãããããã«ã³ããŒããŸãã
new CleanWebpackPlugin(['dist']), new CopyWebpackPlugin([{ from: 'assets', force: true }]),
æ§æã®æ®ãã®éšåã¯ããããžã§ã¯ãã®ã³ã³ããã¹ãã§ã¯éèŠã§ã¯ãããŸãããããããçš®é¡ã®UglifyJSPluginãBundleAnalyzerPluginããã³ãã®ä»ã®äŸ¿å©ãªãã®ããããŸãããããã§ã¯ãããŸããã
ããå°ããµãŒããŒ
ãã¥ãŒããªã¢ã«ã®æåã®éšåã§çºè¡šããã2ã€ã®ãã¡ã€ã«ããreqããã«ãŠã§ã¢ããšãerrããã«ãŠã§ã¢ãã¯å®è£ ãããŠããŸãããæåŸã®ãã¡ã€ã«ã¯ãéåžžã®ãšã©ãŒåŠçããã«ãŠã§ã¢ãšã¯ã¹ãã¬ã¹ã§ããããã䜿çšããŠãçŽç²ãªãµãŒããŒãšã©ãŒããŸãã¯ajaxãªã¯ãšã¹ãäžã«ãµãŒããŒãšã©ãŒãçºçããå Žåã¯jsonãå«ãç¹å¥ãªããŒãžïŒ./views/error.htmlïŒãæäŸããŸãã次ã®ããã«ãªããŸããïŒ
module.exports = () => (err, req, res, next) => { res.status(500); (req.accepts(['html', 'json']) === 'json') ? res.json({ errors: { [err.name]: [err.message] } }) : res.render('error', { err }); };
jsonå¿çã®ããå¥åŠãªåœ¢åŒã¯ãRealWorldä»æ§ã§åãå ¥ããããŠãããšã©ãŒåœ¢åŒãããã«æš¡å£ããŠããããã§ããçµ±äžã®ããã«ãããã°ãä»ã®ãšãã2çªç®ã®ãreqããã«ãŠã§ã¢ãã¯ã¢ã€ãã«ç¶æ ã®ãŸãŸã«ããŸããã圹ã«ç«ã€ãšç¢ºä¿¡ããŠããŸãã
module.exports = () => (req, res, next) => next();
SSRãšãã€ãã¬ãŒãã®ãã¹ã
SSRã®åäœã確èªããæ¹æ³ã¯ãã¹ãŠããã£ãŠããã®ã§ããããŒãžã³ãŒãã®è¡šç€ºããéããŠãïŒappã¿ã°ã空ã§ã¯ãªãïŒSPAã®å Žåã®ããã«ïŒããã¢ããªã±ãŒã·ã§ã³ã®ããŒã¯ã¢ãããå«ãŸããŠããããšã確èªããŠãã ããããã€ãã¬ãŒãã§å·ããã®ã¯å°ãé£ããã§ãã
泚ææ·±ãç®ã§èŠãã°ããã®ç解ã§ããªãã³ãŒãã®æçã«æ°ä»ããããããŸãããããã¯ããµãŒããŒãã³ãã¬ãŒãindex.htmlã«ãæã«ãéœåžã«ããååšããªããšããããšã§ãã
window.msgEl = document.getElementById('msg');
ãæ°Žåè£çµŠããæ©èœãããã©ããã確èªã§ããã®ã¯ã圌ã®å©ãããããŸããã³ã³ãœãŒã«ãéãã次ãå ¥åããŸãã
msgEl === document.getElementById('msg');
trueã®å Žåãã¢ã€ãã ã¯ã¯ã©ã€ã¢ã³ãã³ãŒãã«ãã£ãŠåæç»ãããŠããŸãããRactive.defaults.enhance = false;ãšããå€ãè©ŠããŠèšå®ããããšãã§ããŸãããã¢ããªã±ãŒã·ã§ã³ãåæ§ç¯ããŠåèµ·åãããã®å Žåããã®ãã§ãã¯ãfalseãè¿ãããšã確èªããŠãã ãããã€ãŸããã¯ã©ã€ã¢ã³ãã³ãŒãã¯ããŒã¯ã¢ãããåæç»ããŸãã
ãããã£ãŠãSSRãšããã€ãã¬ãŒã·ã§ã³ãã¯ãéçãªå€ãšåçãªå€ãéåžžã«åçãªå€ïŒèšç®ãããããããã£ïŒã®äž¡æ¹ã§å®å šã«æ©èœããŸãã確èªããå¿ èŠããããŸããã
â ãªããžããª
â ãã¢
ãã®ãã¥ãŒããªã¢ã«ã®æ¬¡ã®ããŒãã§ã¯ãååWebã¢ããªã±ãŒã·ã§ã³ã®ãã2ã€ã®éèŠãªåé¡ã解決ããŸããå圢ã«ãŒãã£ã³ã°ãšããã²ãŒã·ã§ã³ãããã³ç¹°ãè¿ããããã§ãããšåæããŒã¿ç¶æ ãç§ã¯ãããã®åé¡ã«å¥ã®èšäºãæ§ãããšããäºå®ã«ããããããããœãªã¥ãŒã·ã§ã³èªäœã¯æåéã5è¡ã®ã¢ããªã±ãŒã·ã§ã³ã³ãŒããå¿ èŠãšããŸããåãæ¿ããªãã§ãã ããïŒ
ãæž èŽããããšãããããŸããïŒããªããããã奜ãã°ãã楜ãã¿ã«ïŒäŒæ¥ãšå¹žéã«ãããããã¹ãŠïŒ
UPDïŒ SSRãšProgressive Enhancementã䜿çšããå圢RealWorldã¢ããªã±ãŒã·ã§ã³ã®éçºã ããŒã3-ã«ãŒãã£ã³ã°ãšãã§ãã