├── .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 | logo 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 | 2 | 3 | 4 | 5 | 6 | 7 | 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 | --------------------------------------------------------------------------------