├── .babelrc
├── .eslintrc
├── .gitignore
├── README.md
├── app
├── favicon.ico
├── index.html
└── src
│ ├── actions
│ ├── demo.action.js
│ ├── index.js
│ └── type.js
│ ├── components
│ ├── app.js
│ ├── app.less
│ └── demo
│ │ └── index.js
│ ├── containers
│ └── demo.container.js
│ ├── images
│ └── logo.svg
│ ├── index.js
│ ├── reducers
│ ├── demo.reducer.js
│ └── index.js
│ ├── router.js
│ └── stores
│ └── index.js
├── dist
└── template.ejs
├── package.json
├── webpack.config.js
├── webpack.production.config.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-0", "react"],
3 | "plugins": [
4 | "transform-runtime",
5 | "add-module-exports",
6 | "transform-decorators-legacy"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "env": {
4 | "browser": true,
5 | "node": true,
6 | "commonjs": true,
7 | "amd": true,
8 | "es6":true
9 | },
10 | "extends": [
11 | "standard",
12 | "standard-jsx"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist/build/*
3 | npm-debug.log
4 | .idea/
5 | .DS_Store
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### React-Redux simple boilerplate
2 | > 没那么复杂的 react-redux 脚手架。
3 |
4 |
5 | ### Usage
6 | > 1、git clone
7 | > 2、安装依赖:$ npm install
8 | > 3、启动服务:$ npm start
9 | > 4、生成文件:$ npm run build
10 |
11 |
12 | ### 使用无 Redux 版
13 | ```bash
14 | git checkout no-redux
15 | ```
16 |
17 | 在使用时将 .git 文件夹删除,重新 init 即可
18 | ### Features
19 | * 开发环境单文件打包
20 | * 生产环境分块打包
21 | * Less代替Css
22 | * 内置 react-redux demo
23 | * Standard 代码风格检测✨
24 | * Visualizer 打包文件分析✨
25 |
26 | ### 依赖
27 | * UI框架:React & React-Dom
28 | * 路由:React-Router 2
29 | * 状态管理:React-Redux
30 | * JS:ES6 (添加 ES7 部分支持)
31 | * 样式:Less/Css
32 | * 网络请求:Fetch
33 | * 打包构建:Webpack2 Babel
34 | * 包管理:Npm/Yarn
35 |
36 | ### Webpack2
37 | * 打包 css (extract-text-webpack-plugin)
38 | * 自动处理浏览器前缀 (autoprefixer)
39 | * 自动生成 html (html-webpack-plugin)
40 | * 打包前清除上次文件 (clean-webpack-plugin)
41 |
42 | ### Redux Demo
43 | 在这就直接贴部分代码
44 |
45 | + action
46 | + aciton
47 |
48 | ```javascript
49 | const getDemoSucc = (data) => {
50 | return {
51 | type: GET_DEMO_SUCC,
52 | payload: {
53 | data
54 | }
55 | }
56 | };
57 |
58 | const getDemoErr = () => {
59 | return {
60 | type: GET_DEMO_ERR,
61 | }
62 | };
63 |
64 | ```
65 | + 用 Promise 触发 aciton
66 |
67 | ```javascript
68 | export function getDemo(params) {
69 | return (dispatch) =>{
70 | //a example with fetch
71 | fetch('url').then((res)=>{
72 | return res.json()
73 | }).then((json)=>{
74 | //do something with json
75 | dispatch(getDemoSucc(data))
76 |
77 | }).catch(()=>{
78 | dispatch(getDemoErr())
79 | })
80 | }
81 | }
82 | ```
83 | + 用 async 触发 aciton
84 |
85 | ```javascript
86 | export function getDemo(params) {
87 | return async (dispatch) =>{
88 | try {
89 | //do something with params
90 | dispatch(getDemoSucc(data))
91 | } catch(e) {
92 | dispatch(getDemoErr())
93 | }
94 | }
95 | }
96 | ```
97 |
98 | + store
99 | + react-redux
100 |
101 | ```javascript
102 | import {createStore, applyMiddleware, compose} from 'redux';
103 | import thunkMiddleware from 'redux-thunk';
104 | import createLogger from 'redux-log';
105 | import rootReducer from '../reducers';
106 |
107 | const configStore =(initialState)=>{
108 | const log = createLogger();
109 | const middleWare = [thunkMiddleware, log];
110 | const store = compose(
111 | applyMiddleware(...middleWare),
112 | window.devToolsExtension ? window.devToolsExtension() : f => f
113 | )(createStore)(rootReducer, initialState);
114 | //热替换
115 | if (module.hot) {
116 | module.hot.accept('../reducers', ()=> {
117 | const nextReducer = require('../reducers');
118 | store.replaceReducer(nextReducer);
119 | });
120 | }
121 | return store;
122 | };
123 |
124 | export default configStore;
125 | ```
126 |
127 | + reducer
128 | + index
129 |
130 | ```javascript
131 | import {combineReducers} from 'redux';
132 |
133 | import demo from './demo.reducer'
134 | const rootReducer = combineReducers({
135 | demo,
136 | });
137 |
138 | export default rootReducer;
139 | ```
140 | + demo.reducer
141 |
142 | ```javascript
143 | import {GET_DEMO_ERR,GET_DEMO_SUCC} from "../actions/type";
144 | const init_state = {
145 | data: {
146 | } //放默认值
147 | };
148 |
149 | export default function demo(state = init_state, action) {
150 | switch (action.type) {
151 | case GET_DEMO_SUCC:
152 | return {
153 | ...state,
154 | data: action.payload
155 | };
156 | case GET_DEMO_ERR:
157 | return {
158 | ...state,
159 | data: {}
160 | };
161 | default:
162 | return state
163 | }
164 | }
165 |
166 | ```
167 |
168 | + container
169 | + use React-redux
170 |
171 | ```javascript
172 | import React, {Component} from "react";
173 |
174 | import {connect} from "react-redux";
175 | import {bindActionCreators} from "redux";
176 | import {getDemo} from '../actions'
177 |
178 | import ReduxDemo from '../components/demo';
179 |
180 | class DemoContainer extends Component {
181 | render() {
182 | const {demo:{data}, action:{getDemo}} = this.props;
183 | return (
184 |
188 | );
189 | }
190 | }
191 | const mapStateToProps = (state) => {
192 | return {
193 | demo: state.demo
194 | };
195 |
196 | }
197 | const mapDispatchToProps = (dispatch) => {
198 | const actions = {getDemo};
199 | return {
200 | action: bindActionCreators(actions, dispatch)
201 | };
202 | };
203 |
204 | export default connect(mapStateToProps, mapDispatchToProps)(DemoContainer);
205 |
206 |
207 | ```
208 |
209 | + use ES7 Decorator 装饰器
210 |
211 | ```javascript
212 |
213 | @connect(mapStateToProps,mapDispatchToProps)
214 | class DemoContainer extends Component {
215 | render() {
216 | const {demo:{data}, action:{getDemo}} = this.props;
217 | return (
218 |
222 | );
223 | }
224 | }
225 |
226 | ```
227 |
228 | ---
229 |
230 | Example:[基于 React 开发的 Online Judge 系统](https://github.com/ouxu/NEUQ-OJ)[持续开发中]
231 |
232 | Author: NEUQer/ouxu
233 |
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ouxu/react-redux-simple-boilerplate/38eb37d1209a52917217c393497dd5e123d6cf89/app/favicon.ico
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | React
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/actions/demo.action.js:
--------------------------------------------------------------------------------
1 | import { GET_DEMO_ERR, GET_DEMO_SUCC } from './type'
2 |
3 | const getDemoSucc = (data) => {
4 | return {
5 | type: GET_DEMO_SUCC,
6 | payload: {
7 | data
8 | }
9 | }
10 | }
11 |
12 | const getDemoErr = () => {
13 | return {
14 | type: GET_DEMO_ERR,
15 | }
16 | }
17 |
18 | /**
19 | * 用 async 触发 aciton
20 | * @param params
21 | * @returns {function(*)}
22 | */
23 | export function getDemo (params) {
24 | return async (dispatch) => {
25 | try {
26 | //do something with params
27 | dispatch(getDemoSucc(data))
28 | } catch (e) {
29 | dispatch(getDemoErr())
30 | }
31 | }
32 | }
33 |
34 | /**
35 | * 用 Promise 触发 aciton
36 | * @param params
37 | * @returns {function(*)}
38 | */
39 | export function getDemo_P (params) {
40 | return (dispatch) => {
41 | //a example with fetch
42 | fetch('url').then((res) => {
43 | return res.json()
44 | }).then((json) => {
45 | //do something with json
46 | dispatch(getDemoSucc(data))
47 |
48 | }).catch(() => {
49 | dispatch(getDemoErr())
50 | })
51 | }
52 | }
--------------------------------------------------------------------------------
/app/src/actions/index.js:
--------------------------------------------------------------------------------
1 | export { getDemo } from './demo.action'
2 |
--------------------------------------------------------------------------------
/app/src/actions/type.js:
--------------------------------------------------------------------------------
1 | export const GET_DEMO_SUCC = 'GET_DEMO_SUCC'
2 | export const GET_DEMO_ERR = 'GET_DEMO_ERR'
3 |
--------------------------------------------------------------------------------
/app/src/components/app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by out_xu on 17/3/16.
3 | */
4 | import React, { Component } from 'react'
5 | import { Link } from 'react-router'
6 |
7 | import logo from '../images/logo.svg'
8 |
9 | import './app.less'
10 | class AppComponent extends Component {
11 | render () {
12 | return (
13 |
14 |
15 |

16 |
Welcome to React
17 |
18 |
19 |
20 | 欢迎使用 react-redux请查看代码,结合自己所学知识开始你的React之旅!
21 |
22 |
23 | {this.props.children}
24 |
25 | )
26 | }
27 | }
28 |
29 | export default AppComponent
30 |
--------------------------------------------------------------------------------
/app/src/components/app.less:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 80px;
8 | }
9 |
10 | .App-header {
11 | background-color: #222;
12 | height: 150px;
13 | padding: 20px;
14 | color: white;
15 | }
16 |
17 | .App-intro {
18 | font-size: large;
19 | }
20 |
21 | a {
22 | color: #222222;
23 | text-decoration: none;
24 | }
25 |
26 | @keyframes App-logo-spin {
27 | from {
28 | transform: rotate(0deg);
29 | }
30 | to {
31 | transform: rotate(360deg);
32 | }
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/app/src/components/demo/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | class ReduxDemo extends Component {
4 | render () {
5 | //you can dispatch ation by using this.props.getDemo() or
6 | const {data, getDemo} = this.props
7 |
8 | return (
9 | ation-> reducer-> store -> get data in container -> get data in Component
10 | )
11 | }
12 | }
13 |
14 | ReduxDemo.propTypes = {}
15 | ReduxDemo.defaultProps = {}
16 |
17 | export default ReduxDemo
18 |
--------------------------------------------------------------------------------
/app/src/containers/demo.container.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | //连接redux
3 | import { connect } from 'react-redux'
4 | import { bindActionCreators } from 'redux'
5 | import { getDemo } from '../actions'
6 |
7 | import ReduxDemo from '../components/demo'
8 |
9 | class DemoContainer extends Component {
10 | render () {
11 | const {demo: {data}, action: {getDemo}} = this.props
12 | return (
13 |
17 | )
18 | }
19 | }
20 |
21 | // use ES7 Decorator 装饰器
22 | // @connect(mapStateToProps,mapDispatchToProps)
23 | // class DemoContainer extends Component {
24 | // render() {
25 | // const {demo:{data}, action:{getDemo}} = this.props;
26 | // return (
27 | //
31 | // );
32 | // }
33 | // }
34 |
35 | DemoContainer.propTypes = {}
36 | DemoContainer.defaultProps = {}
37 |
38 | const mapStateToProps = (state) => {
39 | return {
40 | demo: state.demo
41 | }
42 |
43 | }
44 | const mapDispatchToProps = (dispatch) => {
45 | const actions = {getDemo}
46 | return {
47 | action: bindActionCreators(actions, dispatch)
48 | }
49 | }
50 |
51 | export default connect(mapStateToProps, mapDispatchToProps)(DemoContainer)
52 |
--------------------------------------------------------------------------------
/app/src/images/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/app/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | //引入redux
4 | import { Provider } from 'react-redux'
5 | import configStore from './stores'
6 | import RouterApp from './router'
7 | const store = configStore()
8 |
9 | ReactDOM.render(
10 |
11 |
12 | ,
13 | document.getElementById('app')
14 | )
--------------------------------------------------------------------------------
/app/src/reducers/demo.reducer.js:
--------------------------------------------------------------------------------
1 | import { GET_DEMO_ERR, GET_DEMO_SUCC } from '../actions/type'
2 |
3 | const init_state = {
4 | data: {} //放默认值
5 | }
6 |
7 | export default function demo (state = init_state, action) {
8 | switch (action.type) {
9 | case GET_DEMO_SUCC:
10 | return {
11 | ...state,
12 | data: action.payload
13 | }
14 | case GET_DEMO_ERR:
15 | return {
16 | ...state,
17 | data: {}
18 | }
19 | default:
20 | return state
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by out_xu on 16/12/20.
3 | */
4 | import { combineReducers } from 'redux'
5 |
6 | import demo from './demo.reducer'
7 | const rootReducer = combineReducers({
8 | demo,
9 | })
10 |
11 | export default rootReducer
12 |
--------------------------------------------------------------------------------
/app/src/router.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { hashHistory, Route, Router } from 'react-router'
3 |
4 | import AppComponent from './components/app'
5 | import DemoContainer from './containers/demo.container'
6 |
7 | const RouterApp = () => (
8 |
9 |
10 | {/**/}
11 |
12 |
13 |
14 |
15 | )
16 |
17 | export default RouterApp
--------------------------------------------------------------------------------
/app/src/stores/index.js:
--------------------------------------------------------------------------------
1 | import { applyMiddleware, compose, createStore } from 'redux'
2 | import thunkMiddleware from 'redux-thunk'
3 | import createLogger from 'redux-log'
4 | import rootReducer from '../reducers'
5 |
6 | const configStore = (initialState) => {
7 | const log = createLogger()
8 | const middleWare = [thunkMiddleware, log]
9 | const store = compose(
10 | applyMiddleware(...middleWare),
11 | window.devToolsExtension ? window.devToolsExtension() : f => f
12 | )(createStore)(rootReducer, initialState)
13 | //热替换
14 | if (module.hot) {
15 | module.hot.accept('../reducers', () => {
16 | const nextReducer = require('../reducers')
17 | store.replaceReducer(nextReducer)
18 | })
19 | }
20 | return store
21 | }
22 |
23 | export default configStore
24 |
--------------------------------------------------------------------------------
/dist/template.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= htmlWebpackPlugin.options.title %>
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-redux-simple-boilerplate",
3 | "version": "1.0.2",
4 | "description": "没那么难的 React-Redux脚手架",
5 | "main": "app/src/main.js",
6 | "scripts": {
7 | "build": "webpack --progress --config webpack.production.config.js",
8 | "start": "webpack-dev-server"
9 | },
10 | "dependencies": {
11 | "react": "^15.3.2",
12 | "react-dom": "^15.3.2",
13 | "react-router": "^2.4.0",
14 | "react-redux": "^5.0.3",
15 | "redux": "^3.6.0",
16 | "redux-log": "^2.0.0",
17 | "redux-thunk": "^2.1.0"
18 | },
19 | "devDependencies": {
20 | "autoprefixer": "^6.7.6",
21 | "babel-core": "6.x",
22 | "babel-eslint": "^7.1.1",
23 | "babel-runtime": "^6.11.6",
24 | "babel-loader": "^6.3.0",
25 | "babel-plugin-add-module-exports": "^0.2.1",
26 | "babel-plugin-transform-decorators-legacy": "^1.3.4",
27 | "babel-plugin-transform-runtime": "^6.23.0",
28 | "babel-polyfill": "^6.23.0",
29 | "babel-preset-es2015": "^6.18.0",
30 | "babel-preset-react": "^6.16.0",
31 | "babel-preset-stage-0": "^6.5.0",
32 | "clean-webpack-plugin": "^0.1.16",
33 | "css-loader": "^0.26.1",
34 | "eslint": "^3.13.1",
35 | "eslint-config-standard": "^7.1.0",
36 | "eslint-config-standard-jsx": "^3.3.0",
37 | "eslint-plugin-import": "^2.2.0",
38 | "eslint-plugin-jsx-a11y": "^4.0.0",
39 | "eslint-plugin-promise": "^3.5.0",
40 | "eslint-plugin-react": "^6.10.3",
41 | "eslint-plugin-standard": "^2.1.1",
42 | "extract-text-webpack-plugin": "^2.1.0",
43 | "file-loader": "^0.9.0",
44 | "html-webpack-plugin": "^2.28.0",
45 | "less": "^2.7.2",
46 | "less-loader": "^2.2.3",
47 | "path": "^0.12.7",
48 | "postcss-loader": "^1.3.3",
49 | "react-hot-loader": "^1.3.1",
50 | "style-loader": "^0.13.1",
51 | "url-loader": "^0.5.7",
52 | "webpack": "^2.2.1",
53 | "webpack-dev-server": "^2.5.0",
54 | "webpack-visualizer-plugin": "^0.1.11"
55 | },
56 | "standard": {
57 | "parser": "babel-eslint",
58 | "env": {
59 | "browser": true,
60 | "node": true,
61 | "commonjs": true,
62 | "amd": true,
63 | "es6": true
64 | },
65 | "extends": [
66 | "standard",
67 | "standard-jsx"
68 | ]
69 | },
70 | "author": "NEUQer/ouxu"
71 | }
72 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack')
2 | const path = require('path')
3 | const autoprefixer = require('autoprefixer')
4 |
5 | const dflPort = 8080 //配置端口
6 |
7 | module.exports = {
8 | // 配置服务器
9 | devServer: {
10 | port: dflPort,
11 | contentBase: path.join(__dirname, './app'),
12 | historyApiFallback: true,
13 | inline: true,
14 | noInfo: false,
15 | open: true,
16 | stats: {colors: true}
17 | },
18 | devtool: 'eval',
19 | entry: [
20 | 'webpack-dev-server/client?http://127.0.0.1:' + dflPort,
21 | 'webpack/hot/only-dev-server',
22 | path.join(__dirname, '/app/src/index.js'),
23 | ],
24 | output: {
25 | path: '/dist/assets',
26 | publicPath: '/assets',
27 | filename: 'bundle.js',
28 | },
29 | cache: true,
30 | module: {
31 | rules: [
32 | {
33 | test: /\.jsx?$/,
34 | loaders: 'react-hot-loader!babel-loader',
35 | exclude: /node_modules/,
36 | },
37 | {
38 | test: /\.css$/,
39 | loaders: ['style-loader', 'css-loader', 'postcss-loader']
40 | },
41 | {
42 | test: /\.less/,
43 | loaders: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader']
44 | },
45 | {
46 | test: /\.(png|jpg|gif|woff|woff2|svg)$/,
47 | loaders: [
48 | 'url-loader?limit=10000&name=[hash:8].[name].[ext]',
49 | ]
50 | }
51 | ]
52 | },
53 | resolve: {
54 | extensions: [' ', '.js', '.jsx'],
55 | },
56 | plugins: [
57 | new webpack.HotModuleReplacementPlugin(),
58 | new webpack.NoEmitOnErrorsPlugin(),
59 | new webpack.LoaderOptionsPlugin({
60 | options: {
61 | postcss: function () {
62 | return [autoprefixer]
63 | }
64 | }
65 | }),
66 | new webpack.DefinePlugin({
67 | __DEVCLIENT__: false,
68 | __DEVSERVER__: true,
69 | 'process.env': {
70 | 'NODE_ENV': JSON.stringify('development')
71 | }
72 | }),
73 | ]
74 | }
75 |
--------------------------------------------------------------------------------
/webpack.production.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack')
2 | const path = require('path')
3 | const ExtractTextPlugin = require('extract-text-webpack-plugin') //打包 css
4 | const autoprefixer = require('autoprefixer') //自动处理浏览器前缀
5 | const HtmlWebpackPlugin = require('html-webpack-plugin') //生成 html
6 | const CleanWebpackPlugin = require('clean-webpack-plugin') //用于清除上次打包文件
7 | const Visualizer = require('webpack-visualizer-plugin')
8 |
9 | module.exports = {
10 | entry: {
11 | bundle: path.join(__dirname, '/app/src/index.js'),
12 | vendors: ['react', 'react-dom', 'react-router'] //第三方库和框架另外打包
13 | },
14 | output: {
15 | path: path.join(__dirname, '/dist/build/'),
16 | // publicPath: '', //有需要请自己配置,表示 index.html 中引入资源的前缀path
17 | filename: 'js/bundle.[chunkhash:8].js',
18 | chunkFilename: 'js/[name].[chunkhash:8].js'
19 | },
20 | cache: true,
21 | devtool: false,
22 | module: {
23 | rules: [
24 | {
25 | test: /\.jsx?$/,
26 | loaders: 'react-hot-loader!babel-loader',
27 | exclude: /node_modules/
28 | },
29 | {
30 | test: /\.css$/,
31 | loaders: ExtractTextPlugin.extract({
32 | fallback: 'style-loader',
33 | use: ['css-loader?minimize', 'postcss-loader']
34 | })
35 | },
36 | {
37 | test: /\.less/,
38 | loaders: ExtractTextPlugin.extract({
39 | fallback: 'style-loader',
40 | use: ['css-loader?minimize', 'postcss-loader', 'less-loader']
41 | })
42 | },
43 | {
44 | test: /\.(png|jpg|gif|woff|woff2|svg)$/,
45 | loaders: [
46 | 'url-loader?limit=10000&name=img/[hash:8].[name].[ext]',
47 | ],
48 | }
49 | ]
50 | },
51 | resolve: {
52 | extensions: [' ', '.js', '.jsx'],
53 | },
54 | plugins: [
55 | new webpack.NoEmitOnErrorsPlugin(),
56 | new webpack.LoaderOptionsPlugin({
57 | options: {
58 | postcss: function () {
59 | return [autoprefixer]
60 | }
61 | }
62 | }),
63 | new webpack.optimize.CommonsChunkPlugin({
64 | name: 'vendors',
65 | filename: 'js/vendors.[chunkhash:8].js',
66 | }),
67 | new ExtractTextPlugin({
68 | filename: 'css/style.[contenthash:8].css',
69 | allChunks: true
70 | }),
71 | new webpack.DefinePlugin({
72 | 'process.env': {
73 | NODE_ENV: JSON.stringify('production')
74 | }
75 | }),
76 | new webpack.optimize.UglifyJsPlugin({
77 | output: {
78 | comments: false, // remove all comments
79 | },
80 | compress: {
81 | warnings: false,
82 | drop_debugger: true,
83 | drop_console: true
84 | }
85 | }),
86 | new Visualizer(),
87 | new webpack.optimize.LimitChunkCountPlugin({maxChunks: 15}),
88 | new webpack.optimize.MinChunkSizePlugin({minChunkSize: 1000}),
89 | new HtmlWebpackPlugin({
90 | template: './dist/template.ejs',
91 | title: 'React',
92 | favicon: './app/favicon.ico',
93 | chunks: ['bundle', 'vendors']
94 | }),
95 | new CleanWebpackPlugin(['dist/build'], {
96 | verbose: true,
97 | dry: false
98 | })
99 | ]
100 | }
101 |
--------------------------------------------------------------------------------