mobxã
M odelãšããŠäœ¿çšããåå¿ã¢ããªã±ãŒã·ã§ã³ã®ãã©ãããã©ãŒã ãäœæããããã»ã¹ã«ã€ããŠã話ããããšæããŸãã 空ã®ãããžã§ã¯ããã£ã¬ã¯ããªããå®éã®ãµã³ãã«ã«ç§»åããŸãã éçºããã»ã¹äžã«ç§ã泚æãæã£ãäž»ãªãã€ã³ããèæ
®ããŠãã ããã 修食ãªã³ã¯ã§ããã¹ãã飜åãããããšããŸããè¿œå ã®ã¡ã¢ã¯ã
ãã¡ã¢ïŒããšããŒã¯ãããæäœã§åŒ·èª¿è¡šç€ºãããŸãã
ã¹ããŒãªãŒã¯2ã€ã®éšåã§æ§æãããŸãã
- åå¿ã¢ããªã±ãŒã·ã§ã³ã®ããã®ããªããžãããã®æºå
- Mobx +åå¿ãåŽé¢å³
ãèŠããšããã«ãæžãã®ã§ãæ¹åã«é¢ããææ¡ãã³ã¡ã³ããæè¿ããŸãã èªè
ãnpmãnode.jsãreact.jsãäœã§ããããç¥ã£ãŠãããå°éå
·ãšç¶æ
ã®åºæ¬çãªç¥èãæã£ãŠããããšãé¡ã£ãŠããŸãã ãã®èšäºãæžããŠããæç¹ã§ã¯ãwindowsãšäžå®å®ãªnode.js 7.3.0ããŒãžã§ã³ããããŸãã
åå¿ã¢ããªã±ãŒã·ã§ã³ã®ããã®ããªããžãããã®æºå
åå¿ããã¹ã±ã«ãã³ãšãã€ã©ãŒãã¬ãŒããäœåãããã®ã§ããfbãã§ããããã©ãã¯ãžã£ãã¯ãšããããªããŒãã§
èªåèªèº«ããªãªãŒã¹
ããŸããã å®æãããã®ã¯äœ¿çšããŸãããããã¹ãŠãèªåã®æã§åéãããã®ä»çµã¿ã確èªããŸãã ç§ãã¡ã¯ç¬ç«ããŠãã®æ¹æ³ã§ããã¹ãŠã®æãã³ãŒããŒã調ã¹ãŠãããã»ã¹ã®å
šäœçãªã¡ã«ããºã ãç解ãã以åã¯ç解ã§ããªãã£ã詳现ãç解ããŸãã ç§ã¯å¥ã®èªè»¢è»ã®ãµããããã®ã§ã¯ãªããæè²ã®ããã«éçºããã®ã§ãã ç±æã«å§åããããæ°ã«å
¥ãã®IDEã§ã³ã³ãœãŒã«ãéãããããžã§ã¯ãçšã®æ°ãããã£ã¬ã¯ããªãäœæããŠäžã«å
¥ããŸãã è¡ããïŒ
npm init
ããã§ã¯ãã¹ãŠãç°¡åã§ããããã€ãã®äžè¬çãªè³ªåãæ瀺ããããã®åŸnpmã
package.jsonã®äŸåé¢ä¿ç®¡çãã¡ã€ã«ãäœæããŸãã
泚ïŒæ¯åèªåã«é¢ããæ
å ±ãå
¥åããªãããã«ããã«ã¯ãç»é²ããããšãã§ããŸã npm set init.author.name "your name" npm set init.author.email "your email" npm set init.author.url "your site url"
次ã«ãå¿
èŠãªåå¿ããã±ãŒãžãã€ã³ã¹ããŒã«ããäŸåé¢ä¿ã»ã¯ã·ã§ã³ã®package.jsonã«ãããã«ã€ããŠèšè¿°ããŸãã
react-routerã䜿çš
ããã®ã§ãããã«ããã眮ããŸãïŒ
npm i --save react react-dom react-router
ãããžã§ã¯ãã®æ§é ãäœæããããã€ãã®ãã£ã¬ã¯ããªãäœæããŸãã ã»ãšãã©ã®å Žåãã¯ã©ã€ã¢ã³ããµãŒããŒã¢ããªã±ãŒã·ã§ã³ãå¿
èŠã§ãã ã¯ã©ã€ã¢ã³ãéšåãšãµãŒããŒéšåã«2ã€ã®åå¥ã®ãã£ã¬ã¯ããªãå¿
èŠã§ããããã§ã¯ãç¡éã«é·ãããªãããã§ããŸããããšãã°ã次ã®æ§é ãéžæããŸããã

ã¹ã±ã«ãã³ã«ã¯æ¬¡ã®ãã®ãå¿
èŠã§ãã
- index.jsã¯ãã¯ã©ã€ã¢ã³ãã¢ããªã±ãŒã·ã§ã³ãžã®ãšã³ããªãã€ã³ãã§ãã ããã¯ãç§ãèŠãŠããã¢ããªã±ãŒã·ã§ã³ã®æåã®ãã¡ã€ã«ã§ããä»ã®äººã®ãããžã§ã¯ããåããŠèŠãå Žåãããã¯ããªãããã€ãå
šäœã解ãæããå§ããã¹ã¬ããã§ãã
- routes.js-ã«ãŒã¿ãŒã®ã»ããã¢ããã éå§ããã«ã¯ã1ã€ã®ã«ãŒãã§ååã§ã
- home.js-ããŒã ããŒãž;
- index.html- SPAãå®è¡ããŸããindex.html-ãããå¯äžã®ããŒãžã§ãã
index.html <!doctype html> <html> <head> </head> <body> <div id="app"></div> </body> </html>
ããã§ã¯ãdivïŒã¢ããªã«æ³šæãã䟡å€ããããŸããããã¯ãå°æ¥ã®ãªã¢ã¯ã·ã§ã³ã¢ããªã±ãŒã·ã§ã³ã®ã³ã³ããã§ãã å°ãåŸã§ã¹ã¯ãªãããããã«è¿œå ããŸãã
index.js import React from 'react'; import ReactDOM from 'react-dom'; import AppRouter from './routes'; ReactDOM.render(<AppRouter />, document.getElementById("app"));
<AppRouter />ãåãdivïŒã¢ããªã«ã¬ã³ããªã³ã°ããŸãã
ãã¥ãŒ/ home.js es6 import React from 'react'; export default class Home extends React.Component { render() { return ( <h1>Hello Kitty!</h1> ); } }
ã«ãŒãã®åã«ãããŒã ïŒãããŠãããŸã§ã®ãšããå¯äžã®ïŒãã¥ãŒãèŠãŠã¿ãŸãããã ããã¯ãæšæ¶ã¡ãã»ãŒãžã衚瀺ããã ãã®åå¿ã³ã³ããŒãã³ãã§ãã
åå¿ã³ã³ããŒãã³ããäœæãããšãã«ES6ã®æ¹æ³ã䜿çšããŸãã ES6ã§åéãåå¿ãããæ¹æ³ã¯ã
ããã¥ã¡ã³ããŸãã¯ãã·ã¢èªã§èŠã€ããããšãã§ããŸãã ç¹ã«ãããã¯ã¯ç解ããããã®ã§ãããã«ES6ã§æžãããšããå§ãããŸãã
ãã¡ããã䟿å®äžãjsxè¡šèšã䜿çšããŸãã ãã©ãŠã¶ãŒãã³ãŒããç解ããããã«ãç§ãã¡ã¯
babelãã©ã³ã¹ã¬ãŒã¿ãŒã䜿çšããŸããããã«ãæéã«é
ããã«ES6 / ES2015ã®
æ©èœã䜿çšãããã®ã§ããããã¹ãŠã®ãã©ãŠã¶ãŒããã®æšæºããµããŒãããŠããããã§ã¯ãªãã®ã§ãåã³babelã«å©ããæ±ããŸãã babelã¯ãæ°ããæšæºã§èšè¿°ãããã³ãŒãães5æšæºã³ãŒãã«æžãæãããã©ã³ã¹ãã€ã©ãŒã§ãããã»ãšãã©ãã¹ãŠã®ãã©ãŠã¶ãŒãç解ããåå¿ããjsxã³ãŒãããã©ãŠã¶ãŒãç解ã§ããã³ãŒãã«å€æããããšãã§ããŸãã ããã§ããããã¯ãã©ã°ã€ã³ã®æããµããŒãããŠããŸãã ããã¯ãšãŠãã¯ãŒã«ã§ãïŒ
泚ïŒãã®å€æã®éæ³ã¯ãã¹ãŠãªã³ã©ã€ã³ã§ãæããããšãã§ããŸãã
reactãŸãã¯es6ã®ã³ãŒãã貌ãä»ããŠãããšãã°home.jsã®ã³ãŒãã«å€æããããã®ã確èªããŠãã ãããã®æé ãå®è¡ãããšãES6ã³ãŒãã®9è¡ïŒã400ãã€ãïŒã44è¡ã«ãªã£ãããšã«æ°ä»ããããããŸããïŒ ES5æååïŒã2200ãã€ãïŒ

javascriptã«ã¯ã¯ã©ã¹ããªããããããã¯æ§æç³è¡£ã«å¯Ÿããå ±åŸ©ã§ãã babelã軜ãæã§ã¯ã©ã¹å€ã®é¢æ°ãäœæããæ§åã芳å¯ã§ããŸãã
ããããããã®æ®µéã§ã¯ãã¹ããŒãã¬ã¹ã³ã³ããŒãã³ãã«ã€ããŠ2ã3è¡ãèšãå¿
èŠããããŸãã 倧ãŸãã«èšã£ãŠããããã¯ç¶æ
ãæããªãã³ã³ããŒãã³ããšåŒã°ããŸãã Homeã³ã³ããŒãã³ãã«ã¯ç¶æ
ããªãããã次ã®ããã«æžãæããããšãã§ããŸãã
ã¹ããŒãã¬ã¹home.js import React from 'react'; const Home = (props) => { return ( <h1>Hello Kitty</h1> ); }; export default App;
ã¯ã©ã¹ãåé€ããããããã®ã³ãŒãã¯æçµçãªES5æ§æã§ã¯ã¯ããã«çããªãããã®ããªã¥ãŒã ã¯5å以äžæžå°ããŸãã ããã«ããœãŒã¹ã³ãŒããããã«ç°¡æœã«ããããšãã§ããŸãã
ã¹ããŒãã¬ã¹home.js import React from 'react'; const Home = () => ( <h1>Hello Kitty</h1> ); export default App;
泚ïŒã¹ããŒãã¬ã¹ã³ã³ããŒãã³ãã«é¢ãããã®èšäºã¯æ°ã«å
¥ã£ãŠããŸãããå§ãããŸããroutes.js import React from 'react'; import { Router, Route, browserHistory } from 'react-router'; import Home from './views/home'; export default () => ( <Router history={browserHistory}> <Route path='/' component={Home} /> </Router> );
æåŸã«ãã«ãŒãã§ã¯ãããŒã ã³ã³ããŒãã³ããžã®ãã¹ã1ã€ã ãç»é²ããŸãã ããã§ã¯çåã¯çããªãã¯ãã§ããã©ã€ãã©ãªã¯ã·ã³ãã«ã§ãããåæã«
匷åãªæ©èœãåããŠããŸã ã
ãããžã§ã¯ãã¯å€ãã®ãã¡ã€ã«ã§æ§æãããŠããã®ã§ãèªã¿åãå¯èœãªã³ãŒãããã©ãŠã¶ãŒã«æž¡ãã®ã¯æŠãã®ååã«éãããæçµçã«ã¯æå°åãããjsãã¡ã€ã«ïŒindex.htmlã«æ¥ç¶ããŸãïŒã1ã€ã ãå¿
èŠã§ãããã¢ãžã¥ãŒã«ãã«ããŒãå¿
èŠã«ãªããŸã
webpackã䜿çšããŠåéã
ãŸã ã
ç§ãã¡ã¯ããã眮ããŸãïŒ
npm i --save-dev webpack
泚ïŒwebpackã¯devDependenciesã»ã¯ã·ã§ã³ã«é
眮ãããããšã«æ³šæããŠãã ããã
éçºã«é¢é£ããæ¬çªç°å¢ã§äœ¿çšãããªããã®ã¯ãã¹ãŠ--save-devãã©ã°ã§èšå®ãããŸããå€ãã®å Žåããããã¯ã³ã¬ã¯ã¿ãŒãšãã©ã°ã€ã³ããã¹ãããªã³ã¿ãŒãããŒããŒããã¹ã/ããªããã»ããµãŒãªã©ã§ãã
ãã¹ãŠã®ã³ãŒãå€æã«ã€ããŠäžã§èª¬æããããã«ããã®ããã«babelãšå¿
èŠãªããªã»ããïŒãã©ã°ã€ã³ã®ã»ããïŒãå¿
èŠã§ãã
npm i --save-dev babel-loader babel-core babel-preset-es2015 babel-preset-react
Webpackã«ã¯
æ§æãã¡ã€ã«ãå¿
èŠã§ããããžã§ã¯ããã£ã¬ã¯ããªã®ã«ãŒãã«webpack.config.jsãäœæããŸãã
webpack.config.js var webpack = require('webpack'); module.exports = { entry: './client/index.js', output: { path: __dirname + '/public', filename: 'bundle.js' }, module: { loaders: [ { test: /.jsx?$/, loader: 'babel-loader', exclude: /node_modules/ } ] } };
ãŸããbabelrcã¯.babelrcãã¡ã€ã«ã®äœ¿çšãæšå¥šããŠããŸããããã§ã¯ã䜿çšããããªã»ããã説æããŸãã
{ "presets": ["es2015", "react"] }
泚ïŒããã€ãã®äŸ¿å©ãªãªã³ã¯ïŒbabel 6ã«ã€ããŠç¥ã£ãŠããå¿
èŠã®ãã6ã€ã®ããšãšã ããªã»ããã宣èšãããé åºã®éãã¯äœã§ããïŒããã§ãã¢ããªã±ãŒã·ã§ã³ãžã®ãšã³ããªãã€ã³ããclient / index.jsãã¡ã€ã«ã§ããããšãã³ã¬ã¯ã¿ãŒã«äŒããŸããwebpackã¯ãã®ãã¡ã€ã«ããäœæ¥ãéå§ããŸããã¢ã»ã³ããªã«å«ãããã¡ã€ã«ãæå®ããå¿
èŠã¯ãããŸããã åºåã¯ããããªãã¯ãã£ã¬ã¯ããªã«ãã1ã€ã®bundle.jsãã¡ã€ã«ã§ãã 倧ãŸãã«èšã£ãŠããã®æ§æã§ã¯babelãšèšããŸãïŒããããindex.jsããå§ããŠããã¹ãŠã®å¿
èŠãªãã¡ã€ã«ã1ã€ã«æ¥çããbabelããã¹ãŠã®.jsããã³.jsxãã¡ã€ã«ããã©ãŠã¶ãŒãç解ã§ããã³ãŒãã«å€æããããã«ããŸããããããïŒ Webpackæ§æã®æºåãã§ããããã³ã³ãœãŒã«ã«ç§»åããŠã³ã¬ã¯ã¿ãŒãå®è¡ããŸãã
webpack
bundle.jsãã¡ã€ã«ã¯ããããªãã¯ãã£ã¬ã¯ããªã«è¡šç€ºãããŸãã Publicã¯ç§ãã¡ã®ãããªãã¯ãã£ã¬ã¯ããªã§ãããã«ãåŸããã¹ãŠã®ãæ¢è£œããã¡ã€ã«ïŒç§ãã¡ã«ãšã£ãŠã¯index.html + bundle.jsïŒãããã«æ¥ãã¯ãã§ãã ãã³ãã«ã®æºåãã§ããŸãããhtmlãå®è¡ããŸãã çŸåšã®index.htmlã¯åãªãã¯ãŒã¯ããŒã¹ã§ãããå°æ¥çã«ã¯ãããšãã°ãCSSãŸãã¯jsãã¡ã€ã«ãæ·»ä»ããã³ã³ãã³ããæå°åãŸãã¯è¿œå ããç°ãªãã¢ã»ã³ããªã«å¯ŸããŠç°ãªãæäœãå®è¡ããå¿
èŠãããããšãç解ããå¿
èŠããããŸãã ãããã®ç®çã®ããã«ã
HtmlWebpackPluginãå¿
èŠ
ã§ã ã ç§ãã¡ã¯ããã眮ããŸãïŒ
npm i --save-dev html-webpack-plugin
æ§æãã¡ã€ã«ã«ç§»åããŠãã©ã°ã€ã³ãæ§æããåŸïŒ
webpack.config.js var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './client/index.js', output: { path: __dirname + '/public', filename: 'bundle.js' }, module: { loaders: [ { test: /.jsx?$/, loader: 'babel-loader', exclude: /node_modules/, } ] }, plugins: [ new HtmlWebpackPlugin({ template: './client/index.html', inject: "body" }) ] };
ããã¯ãindex.htmlã¹ã¿ãã«çµã¿èŸŒãŸããbundle.jsãžã®ãªã³ã¯ãå«ã 'script'ã¿ã°ãæ¿å
¥ããããã«webpackã«æ瀺ãããã®ã§ãã ãã®å Žåããæºåå®äºãã®index.htmlã¯ãã³ãã«ã®é£ãã€ãŸãå
¬éãããŸãã å床weââbpackãå®è¡ãããããªãã¯ãã£ã¬ã¯ããªã確èªããŠããã確èªããŸãã
ããŒãšç§ãã¡ãéããwebpackã«æ»ããŸãããã æ°é
ãã®ããèªè
ã¯ãããããŒããã£ïŒãã«ã¯ã710KBãå°ã倧ããããšã«æ°ä»ãã§ãããã åæããŸãããéçºè
ãã³ã³ãœãŒã«ã«ããŸããŸãªèŠåã衚瀺ãããªã©ã®è¿œå æ©èœãæäŸããéçºããŒãžã§ã³ããŸã ãããŸãã ãããã¯ã·ã§ã³çšã«ãããžã§ã¯ããçµã¿ç«ãŠãããšããåå¿ãã»ã®ããããŠã¿ãŸãããã ãããè¡ãã«ã¯ãæçµçãªbundle.jsãæå°åããNODE_ENVç°å¢å€æ°ããproductionãã«èšå®ããŸãã æ§æã«ãã©ã°ã€ã³ãè¿œå ãããšãè¿œå ã®ãã®ãããŠã³ããŒãããŠã€ã³ã¹ããŒã«ããå¿
èŠã¯ãããŸããã
webpack.config.js plugins: [ new webpack.DefinePlugin({ "process.env": { NODE_ENV: JSON.stringify("production") } }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: true } }), new HtmlWebpackPlugin({ template: './client/index.html', inject: "body" })]
ãã©ã°ã€ã³ã®å®å
šãªãªã¹ãã¯
ãã¡ããã芧ãã ãã ã
泚ïŒNODE_ENV = productionãèšå®ãããåã«ãã¡ã€ã«ãå§çž®ããå Žåãreactã¯ã³ã³ãœãŒã«ã«èŠåã衚瀺ããŸãïŒ

ãã©ã°ã€ã³ã䜿çšããŠãããžã§ã¯ããåæ§ç¯ããæ°ãããã³ãã«ãå床確èªããŸãã

ãã§ã«ããã§äœæ¥ã§ããŸãããããã¯å¶éã§ã¯ãããŸãããå¥ã®webpackæ§æèšå®-"
devtool "ãèŠãŠã¿ãŸãããã ãã®ãªãã·ã§ã³ã¯ãæçµçãªãã¡ã€ã«ãµã€ãºãšãã«ãé床ã«ã圱é¿ããŸãã ãããã£ãŠã補åãšéçºã«ç°ãªãå€ã䜿çšããŸãã
ããã§ã¯ãåãªãã·ã§ã³ã®ä»çµã¿ãèªãããšãã§ããŸãã ç§ã¯ãçç£ã®ããã«ããœãŒã¹ãããããéžæããéçºã®ããã«ãã€ã³ã©ã€ã³ãœãŒã¹ãããããéžæããŸãããããããããããžã§ã¯ãã«ãã£ãŠãããã®å€ã¯ç°ãªãå ŽåããããŸãã ããã§ã¯ãèªåã§ãã¬ã€ããŠæé«ã®ãã®ãéžæããå¿
èŠããããŸãã
ããŸããŸãªããŒãºã«åãããŠãããžã§ã¯ããç°¡åã«çµã¿ç«ãŠãããšãã§ããããã«ãªã£ããããæ§æãã¡ã€ã«ãå€æŽããæãæ¥ãŸãããæ¡ä»¶ãä»ããŠ1ã€ã®æ§æã§èšå®ã調æŽãããšãã®è§£æ±ºçãå«ãã§ã ã¢ã»ã³ããªã®èšå®ãã¿ã€ããå¢ãããšãæ§æãèªã¿ã«ãããªãããã
webpack-configã䜿çšããŸãã
npm install --save-dev webpack-config
説æãããããããã«ãããã¯æ§æãã¡ã€ã«ã®ããŒããå±éãããã³ããŒãžã®ã¢ã·ã¹ã¿ã³ãã§ãã ãã®äŸã§ã¯ãéçºãšæ¬çªã®2ã€ã®ã¢ã»ã³ããªãå®è¡ã§ããããã«ããããšèããŠããŸãã å³ã«ç€ºãããã«ãconfãã£ã¬ã¯ããªãš3ã€ã®æ§æãã¡ã€ã«ãè¿œå ããŸãã

webpack.base.config.js import Config from 'webpack-config'; import HtmlWebpackPlugin from 'html-webpack-plugin'; export default new Config().merge({ entry: './client/index.js', output: { path: __dirname + '/../public', }, module: { loaders: [ { test: /.jsx?$/, loader: 'babel-loader', exclude: /node_modules/, } ] }, plugins: [ new HtmlWebpackPlugin({ template: './client/index.html', inject: "body" })] });
åºæ¬æ§æã«ã¯ã2ã€ã®ã¢ã»ã³ããªã«æå¹ãªäžè¬èšå®ããããŸãã
webpack.development.config.js import Config from 'webpack-config'; export default new Config().extend('conf/webpack.base.config.js').merge({ output: { filename: 'bundle.js' } });
éçºèšå®ã§ã¯ãæçµçãªãã³ãã«ã®ååãbundle.jsãã®ã¿ãæå®ããŸã
webpack.production.config.js import webpack from 'webpack'; import Config from 'webpack-config'; export default new Config().extend('conf/webpack.base.config.js').merge({ output: { filename: 'bundle.min.js' }, plugins: [ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: true } })] });
æ¬çªç°å¢ã§ã¯ããã©ã°ã€ã³ãè¿œå ããŠæå°åãããã³ãã«ã®ååãå€æŽããŸãã ã芧ã®ãšãããäž¡æ¹ã®èšå®ãããŒã¹ããå±éãããŸãã
webpack.config.js import Config, { environment } from 'webpack-config'; environment.setAll({ env: () => process.env.NODE_ENV }); export default new Config().extend('conf/webpack.[env].config.js');
NODE_ENVç°å¢å€æ°ã䜿çšããŠã¢ã»ã³ããªã管çã§ããããã«ãªããŸããããã®å€ã«å¿ããŠãwebpack-configã¯å¿
èŠãªãã¡ã€ã«ãèªåçã«ãã«ã¢ããããŸãã
泚ïŒwebpack.config.jsã¯ES6æ§æã䜿çšãããããwebpackãèµ·åããããšãããšããSyntaxErrorïŒUnexpected token importããšãããšã©ãŒã衚瀺ãããŸãã åé¡ã解決ããã«ã¯ããã®ãã¡ã€ã«ã®ååãwebpack.config.babel.jsã«å€æŽããã ãã§ãã ããã«ãããèšå®ãbabel-loaderã«æž¡ããŸããscriptsã»ã¯ã·ã§ã³ã®package.jsonã«å¿
èŠãªwebpackèµ·åã¹ã¯ãªãããè¿œå ããŸãã
"scripts": { "build-dev": "set NODE_ENV=development&& webpack --progress", "build-prod": "set NODE_ENV=production&& webpack --progress" },
--progressãã©ã°ã䜿çš
ãããšããã³ãã«ã®é²è¡ç¶æ³ãšã¬ããŒãã確èªã§ããŸãã ããã§ã2ã€ã®ç°ãªãã¢ã»ã³ããªãçµã¿ç«ãŠãããšãã§ããŸãã 補åã®å ŽåïŒ
npm run build-prod
ããã³éçºçšïŒ
npm run build-dev
泚ïŒç§ã¯ãŠã£ã³ããŠã§äœæ¥ãããããå²ãåœãŠã¯ãset NODE_ENV = productionãã®ããã«ãªããŸãã ä»ã®ãªãã¬ãŒãã£ã³ã°ã·ã¹ãã ã®å Žåãå²ãåœãŠã¯ç°ãªããŸãããããããŒããŒ-æåŸã®ã¿ããããããŸãã ãã®ããšã«ããããœãŒã¹ãã¡ã€ã«ãå€æŽãããšãã«ãããžã§ã¯ãããã®å Žã§åæ§ç¯ã§ããŸãã ãã®å ŽåãããŒãžã¯ãªããŒãããããç¶æ
ã¯å€±ãããŸããã ããã«ãããéçºãã¹ããŒãã¢ããããéçºããã»ã¹ãåã³ã«å€ãããŸãã
ãã®ããããã£ã¹ãã§è©³çŽ°ãèãããšãã§ããŸãããã®ãããã¯ã«é¢ããèå³æ·±ããªãœãŒã¹ãžã®ãªã³ã¯ããããŸãã
ãã®ããã«å¿
èŠãªã®ã¯ã
react-hot-loader ã
webpack-dev-middleware ã
webpack-hot-middleware ããããŠãã¡ãããµãŒããŒèªäœ
ã§ããexpressã䜿çšããŸãã
npm i --save express
npm i --save-dev react-hot-loader@next webpack-dev-middleware webpack-hot-middleware
æ³šïŒ æ¬¡ã®ããŒãžã§ã³ã®react-hot-loaderãã€ã³ã¹ããŒã«ããå¿
èŠãããããšã«æ³šæããŠãã ããããã¡ã€ã«ããããžã§ã¯ãã«ãŒãã«è¿œå ããŸã
server.js import express from 'express'; import path from 'path'; const PORT = 7700; const PUBLIC_PATH = __dirname + '/public'; const app = express(); const isDevelopment = process.env.NODE_ENV === 'development'; if (isDevelopment) { const webpack = require('webpack'); const webpackConfig = require('./webpack.config.babel').default; const compiler = webpack(webpackConfig); app.use(require('webpack-dev-middleware')(compiler, { hot: true, stats: { colors: true } })); app.use(require('webpack-hot-middleware')(compiler)); } else { app.use(express.static(PUBLIC_PATH)); } app.all("*", function(req, res) { res.sendFile(path.resolve(PUBLIC_PATH, 'index.html')); }); app.listen(PORT, function() { console.log('Listening on port ' + PORT + '...'); });
æå°ã®ãšã¯ã¹ãã¬ã¹ãµãŒããŒãå¯äžã®æ³šæç¹ã¯ãã¢ã»ã³ããªã®éçºçšã®
ããã«ãŠã§ã¢ã®ã»ããã¢ãã
ã§ã ã ã芧ã®ãšãããããã«ãŠã§ã¢ã®ããŒã¿ã¯webpack.config.babelããååŸãããŸã
次ã®ã¹ãããã¯ããã©ã°ã€ã³ã»ã¯ã·ã§ã³ã.babelrcã«è¿œå ããããšã§ã
"plugins": [ "react-hot-loader/babel" ]
éçºçšã®æ§æãã¡ã€ã«ã¯æ¬¡ã®ããã«ãªããŸãã
webpack.development.config.js import webpack from 'webpack'; import Config from 'webpack-config'; export default new Config().extend('conf/webpack.base.config.js').merge({ entry: [ 'webpack-hot-middleware/client?reload=true', 'react-hot-loader/patch', __dirname + '/../client/index.js' ], devtool: 'inline-source-map', output: { filename: 'bundle.js' }, plugins: [ new webpack.HotModuleReplacementPlugin() ] });
ãŸããå€æŽãåããŸãã
index.js import React from 'react'; import { AppContainer } from 'react-hot-loader'; import ReactDOM from 'react-dom'; import AppRouter from './routes'; const render = (Component) => ReactDOM.render( <AppContainer> <Component /> </AppContainer>, document.getElementById('app') ); render(AppRouter); if (module.hot) { module.hot.accept('./routes', () => { require('./routes'); render(AppRouter); }); }
æåŸã«ãpackage.jsonã®ã¹ã¯ãªããã¯æ¬¡ã®ããã«ãªããŸãã
"scripts": { "build-dev": "set NODE_ENV=development&& node server.js", "build-prod": "set NODE_ENV=production&& webpack && node server.js" },
泚ïŒã¹ã¯ãªãããå®è¡ããããšãããšããSyntaxErrorïŒUnexpected token importããšãããšã©ãŒãåã³è¡šç€ºãããŸãã server.jsã¯ES6ã€ã³ããŒãã䜿çšããwebpack.config.babel.jsãèªã¿åãããšãããããã€ã³ããŒãã䜿çšããŸãã ãŸãããµããŒãã¯8çªç®ã®ããŒãžã§ã³ã§ã®ã¿çŽæãããŸãã babel-cliã³ãã³ãã©ã€ã³ã«ã¯Babelãå¿
èŠã§ãã npm i --save-dev babel-cli
nodeã§ã¯ãªãbabel-nodeã䜿çšããŸãããã¹ãŠãæ©èœããã¯ãã§ãã
"scripts": { "build-dev": "set NODE_ENV=development&& babel-node server.js", "build-prod": "set NODE_ENV=production&& webpack && babel-node server.js" },
äž¡æ¹ã®ã¢ã»ã³ããªãåéããããšããŠããŸãããæ¬çªã§ã¯æå°åãããbundle.min.jsãåéããããµãŒããŒã¯ããŒã7700ã§èµ·åããŸããéçºã§ã¯ããããªããŒããæ©èœããŸããããããªãã¯ãã£ã¬ã¯ããªã«ã¯ãã¡ã€ã«ã衚瀺ããããããã»ã¹å
šäœãã¡ã¢ãªã§å®è¡ãããŸãã ãã¹ãã®ããã«ãã³ãŒãhome.jsãè€éã«ããŸããã
home.js import React from 'react'; export default class Home extends React.Component { constructor() { super(); this.state = { name: "Kitty" }; this.clickHandler = this.clickHandler.bind(this); } clickHandler() { this.setState({ name: "Bunny" }); } render() { return ( <h1 onClick={this.clickHandler}> {`Hello ${this.state.name}!`} </h1> ); } }
ãšããã§ãéçºã¢ã»ã³ããªãéå§ããå Žåã¯ããã¹ãŠã®å€æŽãããã«ãã«ããå¿
èŠããããŸãã ããããŒãã¯ãªãã¯ããŠãååã®ç¶æ
ããKittyããããBunnyãã«å€æŽããã³ãŒãå
ã§ããããŒã®ããã¹ãããHelloããããByeãã«çœ®ãæããŸãã ãã©ãŠã¶ã«ç§»åããŠããBye Bunnyããšããç¢æã衚瀺ãããŸãã ããããªããŒãã¯æ©èœããŸããããå€æŽãããç¶æ
ã¯ãªã»ãããããŸããã§ããã
æåã¯CSSã§äœæ¥ãè¿œå ããããããŸããã§ããããèšäºãæžããŠããéçšã§ããã¹ãŠã®ã»ããã§åãããã«ã¹ã¿ã€ã«ãæ§ç¯ããããã»ã¹ãè¿œå ããå¿
èŠãããããšã«æ°ä»ããŸããã
ãããããããå Žæã§ã¬ã€ã¢ãŠããç·šéããå¥ã®å Žæã§éãã«æ°ããåé¡ãäœæããããã¹ã¿ã€ã«ãäºãã«äžæžãããããäžèšã§èª¬æãããã®ãšåãã¯ã©ã¹ã䜿çšãããããå ŽåããããŸããã åå¿ããã³ã³ããŒãã³ããäœæããã®ã§ãã°ããŒãã«ã§ã¯ãªãã³ã³ããŒãã³ãã«ããã«CSSã䜿çšããŠã¿ãŸãããïŒ
CSSã¢ãžã¥ãŒã«ã䜿çšã
ãŸã ïŒ
post-cssãšãã®
ãã©ã°ã€ã³ãå¿
èŠã«ãªã
ãŸã ã ãŸããéçºãé«éåããããã®
autoprefixerãš
precssã«èå³ããããŸãïŒ
npm i --save-dev css-loader style-loader postcss-loader autoprefixer precss
æ§æãå€æŽãã
webpack.base.config.js import webpack from 'webpack'; import Config from 'webpack-config'; import HtmlWebpackPlugin from 'html-webpack-plugin'; import autoprefixer from 'autoprefixer'; import precss from 'precss'; export default new Config().merge({ entry: './client/index.js', output: { path: __dirname + '/../public', }, module: { loaders: [ { test: /.jsx?$/, loader: 'babel-loader', exclude: /node_modules/, } ] }, plugins: [ new HtmlWebpackPlugin({ template: './client/index.html', inject: "body" }), new webpack.LoaderOptionsPlugin({ options: { postcss: [precss, autoprefixer] } }) ] });
webpack.development.config.js import webpack from 'webpack'; import Config from 'webpack-config'; export default new Config().extend('conf/webpack.base.config.js').merge({ entry: [ 'webpack-hot-middleware/client?reload=true', 'react-hot-loader/patch', __dirname + '/../client/index.js' ], devtool: 'inline-source-map', output: { filename: 'bundle.js' }, module: { loaders: [{ test: /\.css$/, use: [ 'style-loader', { loader: 'css-loader', options: { modules: true, importLoaders: 1, localIdentName: "[local]__[hash:base64:5]", minimize: false } }, { loader: 'postcss-loader' }, ] }] }, plugins: [ new webpack.HotModuleReplacementPlugin() ] });
webpack.production.config.js import webpack from 'webpack'; import Config from 'webpack-config'; export default new Config().extend('conf/webpack.base.config.js').merge({ output: { filename: 'bundle.min.js' }, devtool: 'source-map', module: { loaders: [{ test: /\.css$/, use: [ 'style-loader', { loader: 'css-loader', options: { modules: true, importLoaders: 1, localIdentName: "[hash:base64:10]", minimize: true } }, { loader: 'postcss-loader' }, ] }] }, plugins: [ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: true } })] });
åºæ¬èšå®ã«ãã©ã°ã€ã³ãè¿œå ããæ®ãã®ããŒããŒãè¿œå ããŸããèšå®ã®ã¿ãç°ãªããŸãã ããã§localIdentNameãªãã·ã§ã³ã¯èå³æ·±ããã®ã§ãCSSã¯ã©ã¹åãèšå®ã§ããŸãããããã¯ã·ã§ã³ããŒãžã§ã³ã§ã¯10æåã®ããã·ã¥ã䜿çšããéçºè
ã§ã¯ã¯ã©ã¹å+ 5æåã®ããã·ã¥ã䜿çšããŸãã ããã¯éåžžã«äŸ¿å©ã§ãããããã°ãããšãã«ãä¿®æ£ããå¿
èŠãããã¯ã©ã¹ãåžžã«ãããããã§ãã ããšãã°ãMenuã³ã³ããŒãã³ããè¿œå ããŸãããã
ã¡ãã¥ãŒ/index.js import React from 'react'; import styles from './style.css'; const Menu = () => ( <nav className={styles.menu}> <div className={styles['toggle-btn']}>â°</div> </nav> ); export default Menu;
cssã¢ãžã¥ãŒã«ã®äœ¿çšæ¹æ³ã«æ³šæããŠãã ããã ãããã¯ããŒã«ã«ã¹ã¿ã€ã«ã§ããã€ãŸããå¥ã®ã¡ãã¥ãŒã®å Žåãä»ã®ã¹ã¿ã€ã«ã§.menuã¯ã©ã¹ã䜿çšããããšãã§ããŸãããããã¯äº€å·®ããŸããã
menu / style.css .menu { position: fixed; top: 0; left: 0; bottom: 0; width: 40px; background-color: tomato; & .toggle-btn { position: absolute; top: 5px; right: 10px; font-size: 26px; font-weight: 500; color: white; cursor: pointer; } }
app.js import React from 'react'; import Menu from '../components/menu';
ãã ããããšãã°htmlãbodyã«å¯ŸããŠãã°ããŒãã«ãã¹ã¿ã€ã«ã䜿çšããããšãã§ããŸãã app.jsãæ¥ç¶ããã ãã§ååã§ãã
routes.js import React from 'react'; import { Router, Route, browserHistory } from 'react-router'; import App from './views/app'; import Home from './views/home'; export default () => ( <Router history={browserHistory}> <Route path='/' component={App}> <Route path='home' component={Home} /> </Route> </Router> );
ãã¹ããå°ãè¿œå ããŠãããŒã ããŒãžããã¹ããããAppã³ã³ãããçšæããŸããã
Webpackã®èšå®ã§ã¯ãã¹ã¿ã€ã«ã®ããããªããŒãã䜿çšããããšãã§ããŸããã¹ã¿ã€ã«ãå€æŽããã ãã§ãã
ããã¯çµäºããæåã®éšåã§ãã ç§ã¯æãæ£ããéžæè¢ã®ãµããããã€ããã¯ãããŸãããããã®èšäºãæžããŠããéã«ãã®æ¹æ³ã§ããã€ãã®èå³æ·±ãããšãåŠã³ãŸããã
èµ·ãã£ãããšãžã®ãªã³ã¯ïŒ
github.com/AlexeyRyashencev/react-hot-mobx-es6