React App Best Practice(Under construction)
What is covered
This blog covers following topics
- React
- webpack
- Sass
- ES6
- image
- React router
- Build local and pro environment
- Babel
Packages
For React, I use react, react-dom, react-router with npm
npm install react react-dom react-router --save
For webpack
webpack, webpack-dev-server, webpack-merge
npm install webpack webpack-dev-server webpack-merge --save-dev
For webpack-loader
sass-loader, style-loader, css-loader, file-loader, html-loader, html-webpack-plugin, raw-loader, node-sass, extract-text-webpack-plugin
npm sass-loader style-loader css-loader file-loader html-loader html-webpack-plugin raw-loader node-sass extract-text-webpack-plugin --save-dev
For babel
babel-core, babel-loader, babel-preset-es2015, babel-preset-react
npm install babel-core babel-loader babel-preset-es2015 babel-preset-react
Project Structure
Project |- .babelrc |- index.html |- package.json |- webpack.config.js |- images | |- riko.jpeg |- styles | |- index.css | |- test.css | |- pack.scss |- js | |- about.jsx | |- details.jsx | |- index.jsx | |- user.jsx |- config |- helpers.js |- webpack.common.js |- webpack.dev.js |- webpack.prod.js
Sources
.babelrc
{ 'presets': ['react', 'es2015'] }
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>React + Webpack</title> </head> <body> <body> <div id="content"></div> </body> </body> </html>
All js and css are inserted by webpack html plugin
js/index.jsx
import React from 'react'; import ReactDOM from 'react-dom'; import '../styles/index.css'; import '../styles/pack.scss'; import '../styles/test.css'; import logo from '../images/riko.jpeg'; import { Router, Route, browserHistory } from 'react-router'; import { About } from './about.jsx'; import { User } from './user.jsx'; import { Detail } from './details.jsx'; class Home extends React.Component { render() { return ( <div className="root"> <div className="hello"> Hello React </div> <p className="scss"> SCSS </p> <img src={logo} /> </div> ); } } class App extends React.Component { render() { return (<Router history={browserHistory}> <Route path="/" component={Home}> </Route> <Route path="/about" component={About}></Route> <Route path="/user" component={User}> <Route path="/details" component={Detail} /> </Route> <Route path="/user/details" component={Detail}></Route> </Router> ); } } ReactDOM.render( <App />, document.getElementById("content") );
js/about.jsx
import React from 'react'; export class About extends React.Component { render() { return ( <div className="root"> About </div> ); } }
js/user.jsx
import React from 'react'; export class Detail extends React.Component { render() { return ( <div className="root"> Detail </div> ); } }
webpack.config.js
module.exports = require('./config/webpack.dev.js');
config/helpers.js
var path = require('path'); var _root = path.resolve(__dirname, '..'); function root(args) { args = Array.prototype.slice.call(arguments, 0); return path.join.apply(path, [_root].concat(args)); } exports.root = root;
config/webpack.common.js
var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var ExtractTextPlugin = require('extract-text-webpack-plugin'); var helpers = require('./helpers'); module.exports = { entry: { bundle: helpers.root('js') + '/index.jsx' }, resolve: { extensions: ['', '.js', '.jsx'] }, module: { loaders: [ { test: /\.jsx$/, exclude: /node_modules/, loader: 'babel-loader' }, { test: /\.html$/, loader: 'html' }, { test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/, loader: 'file?name=assets/[name].[hash].[ext]' }, { test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader") }, { test: /\.scss$/, loader: ExtractTextPlugin.extract('css!sass') } ] }, plugins: [ new webpack.optimize.CommonsChunkPlugin({ name: ['bundle'] }), new HtmlWebpackPlugin({ template: 'index.html' }) ] };
config/webpack.dev.js
var webpackMerge = require('webpack-merge'); var ExtractTextPlugin = require('extract-text-webpack-plugin'); var commonConfig = require('./webpack.common.js'); var helpers = require('./helpers'); module.exports = webpackMerge(commonConfig, { devtool: 'cheap-module-eval-source-map', output: { path: helpers.root('dist'), publicPath: 'http://localhost:8080/', filename: '[name].js', chunkFilename: '[id].chunk.js' }, plugins: [ new ExtractTextPlugin('[name].css') ], devServer: { historyApiFallback: true, stats: 'minimal' } });
config/webpack.prod.js
var webpack = require('webpack'); var webpackMerge = require('webpack-merge'); var ExtractTextPlugin = require('extract-text-webpack-plugin'); var commonConfig = require('./webpack.common.js'); var helpers = require('./helpers'); const ENV = process.env.NODE_ENV = process.env.ENV = 'production'; module.exports = webpackMerge(commonConfig, { devtool: 'source-map', output: { path: helpers.root('dist'), publicPath: '/', filename: '[name].[hash].js', chunkFilename: '[id].[hash].chunk.js' }, plugins: [ new webpack.NoErrorsPlugin(), new webpack.optimize.DedupePlugin(), new webpack.optimize.UglifyJsPlugin({ // https://github.com/angular/angular/issues/10618 mangle: { keep_fnames: true } }), new ExtractTextPlugin('[name].[hash].css'), new webpack.DefinePlugin({ 'process.env': { 'ENV': JSON.stringify(ENV) } }) ] });
Build and Run
Add following script for package.json
"scripts": { "clean": "rm -rf dist", "start": "webpack && webpack-dev-server", "build": "rm -rf dist && webpack --config config/webpack.prod.js" }
start is just build and start as development environment with webpack-dev-server
Build for local and run
npm start
You can see build result under dist and can see localhost:8080
React router covers following
localhost:8080/about
localhost:8080/user
localhost:8080/details
localhost:8080/user/details
dist |- assets |- bundle.css |- bundle.js |- index.html
Build for PRO
npm run build
dist |- assets |- bundle.[hash].css |- bundle.[hash].css.map |- bundle.[hash].js |- bundle.[hash].js.map |- index.html