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
