From 86e28913cfe5ec4c2f737b13249302bd89eab01c Mon Sep 17 00:00:00 2001 From: liaoyunda Date: Wed, 22 Mar 2017 19:01:34 +0800 Subject: [PATCH 1/3] =?UTF-8?q?build=E5=90=8E=E8=87=AA=E5=8A=A8=E5=BC=80?= =?UTF-8?q?=E5=90=AFexpress=20server=EF=BC=8C=E6=94=AF=E6=8C=81=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E5=AE=8C=E6=88=90=E5=90=8E=E5=BF=AB=E9=80=9F=E6=9F=A5?= =?UTF-8?q?=E7=9C=8B=E7=94=9F=E4=BA=A7=E7=8E=AF=E5=A2=83=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- build/prod.js | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 16fae7b..a7e5d38 100644 --- a/README.md +++ b/README.md @@ -196,8 +196,8 @@ *** ## § 部署 -在 `react-demo` 的命令窗口下,敲下 `npm run build`,将会在项目根目录下生成 `dist/` -> 您可以使用命令行静态资源服务器 [serve](https://github.com/tj/serve) ( `npm i serve -g` ),敲下 `serve dist/ -p [端口]` 来快速查看 build 后的项目 +在 `react-demo` 的命令窗口下,敲下 `npm run build`,将会在项目根目录下生成 `dist/` 同时开启一个express server,此时打开 `localhost:9091` 即可查看 build后的项目 +> 您也可以使用命令行静态资源服务器 [serve](https://github.com/tj/serve) ( `npm i serve -g` ),敲下 `serve dist/ -p [端口]` 来快速查看 build 后的项目 > 还可以 `cd dist` 后,`python -m SimpleHTTPServer [端口]` 或 `php -S localhost:[端口]` 快速便捷地实现静态资源服务器 > > 关于生产环境下的部署与优化,已超出本文档的论述范围,请自行查阅相关资料 diff --git a/build/prod.js b/build/prod.js index 01210a8..e1744ae 100644 --- a/build/prod.js +++ b/build/prod.js @@ -1,7 +1,10 @@ var fs = require('fs'), path = require('path'), webpack = require('webpack'), - config = require('./webpack.prod.conf'); + config = require('./webpack.prod.conf'), + express = require('express'), + port = process.env.PORT || 9091, + app = express(); webpack(config, function(err, stats) { // show build info to console @@ -12,4 +15,14 @@ webpack(config, function(err, stats) { path.join(config.commonPath.dist, '__build_info__'), stats.toString({ color: false }) ); + + var rootPath = path.resolve(__dirname, '..') + // 通常用于加载静态资源 + app.use(express.static(rootPath + '/dist')) + // 将所有路径指向index + app.get('*', function (request, response){ + response.sendFile(rootPath + '/dist/index.html') + }) + app.listen(port) + console.log("server started on port " + port) }); From ba3da3410736a6ef1d0ae82ee58c7c73402014ab Mon Sep 17 00:00:00 2001 From: liaoyunda Date: Tue, 20 Jun 2017 14:47:57 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E5=8D=87=E7=BA=A7webpack2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/dev.js | 3 +- build/prod.js | 3 +- build/webpack.base.conf.js | 231 +++++++++++++++++++------------------ build/webpack.dev.conf.js | 80 +++++++------ build/webpack.prod.conf.js | 16 +-- package.json | 4 +- 6 files changed, 176 insertions(+), 161 deletions(-) diff --git a/build/dev.js b/build/dev.js index 756ef23..ff060ca 100644 --- a/build/dev.js +++ b/build/dev.js @@ -2,12 +2,13 @@ var express = require('express'), webpack = require('webpack'), // favicon = require('express-favicon'), config = require('./webpack.dev.conf'), + commonPath = require('./webpack.base.conf').commonPath, app = express(); var compiler = webpack(config); // for highly stable resources -app.use('/static', express.static(config.commonPath.staticDir)); +app.use('/static', express.static(commonPath.staticDir)); // app.use(favicon(path.join(__dirname, '../favicon.ico'))); diff --git a/build/prod.js b/build/prod.js index e1744ae..0bca986 100644 --- a/build/prod.js +++ b/build/prod.js @@ -2,6 +2,7 @@ var fs = require('fs'), path = require('path'), webpack = require('webpack'), config = require('./webpack.prod.conf'), + commonPath = require('./webpack.base.conf').commonPath, express = require('express'), port = process.env.PORT || 9091, app = express(); @@ -12,7 +13,7 @@ webpack(config, function(err, stats) { // save build info to file fs.writeFile( - path.join(config.commonPath.dist, '__build_info__'), + path.join(commonPath.dist, '__build_info__'), stats.toString({ color: false }) ); diff --git a/build/webpack.base.conf.js b/build/webpack.base.conf.js index b9ecd86..ca37442 100644 --- a/build/webpack.base.conf.js +++ b/build/webpack.base.conf.js @@ -1,123 +1,126 @@ var path = require('path'), - webpack = require('webpack'), - NyanProgressPlugin = require('nyan-progress-webpack-plugin'); + webpack = require('webpack'), + NyanProgressPlugin = require('nyan-progress-webpack-plugin'); var rootPath = path.resolve(__dirname, '..'), // 项目根目录 - src = path.join(rootPath, 'src'), // 开发源码目录 - env = process.env.NODE_ENV.trim(); // 当前环境 + src = path.join(rootPath, 'src'), // 开发源码目录 + env = process.env.NODE_ENV.trim(); // 当前环境 var commonPath = { - rootPath: rootPath, - dist: path.join(rootPath, 'dist'), // build 后输出目录 - indexHTML: path.join(src, 'index.html'), // 入口基页 - staticDir: path.join(rootPath, 'static') // 无需处理的静态资源目录 + rootPath: rootPath, + dist: path.join(rootPath, 'dist'), // build 后输出目录 + indexHTML: path.join(src, 'index.html'), // 入口基页 + staticDir: path.join(rootPath, 'static') // 无需处理的静态资源目录 }; -module.exports = { - commonPath: commonPath, - entry: { - app: path.join(src, 'app.js'), - - // ================================ - // 框架 / 类库 分离打包 - // ================================ - vendor: [ - 'history', - 'lodash', - 'react', - 'react-dom', - 'react-redux', - 'react-router', - 'react-router-redux', - 'redux', - 'redux-thunk' - ] - }, - output: { - path: path.join(commonPath.dist, 'static'), - publicPath: '/static/' - }, - resolve: { - extensions: ['', '.js', '.jsx'], - alias: { - // ================================ - // 自定义路径别名 - // ================================ - ASSET: path.join(src, 'assets'), - COMPONENT: path.join(src, 'components'), - ACTION: path.join(src, 'redux/actions'), - REDUCER: path.join(src, 'redux/reducers'), - STORE: path.join(src, 'redux/store'), - ROUTE: path.join(src, 'routes'), - SERVICE: path.join(src, 'services'), - UTIL: path.join(src, 'utils'), - HOC: path.join(src, 'utils/HoC'), - MIXIN: path.join(src, 'utils/mixins'), - VIEW: path.join(src, 'views') - } - }, - resolveLoader: { - root: path.join(rootPath, 'node_modules') - }, - module: { - loaders: [{ - test: /\.(js|jsx)$/, - loaders: (function() { - var _loaders = ['babel?' + JSON.stringify({ - cacheDirectory: true, - plugins: [ - 'transform-runtime', - 'transform-decorators-legacy' - ], - presets: ['es2015', 'react', 'stage-0'], - env: { - production: { - presets: ['react-optimize'] - } - } - }), 'eslint']; +var configBase = { + context: rootPath, + entry: { + app: path.join(src, 'app.js'), - // 开发环境下引入 React Hot Loader - if (env === 'development') { - _loaders.unshift('react-hot'); + // ================================ + // 框架 / 类库 分离打包 + // ================================ + vendor: [ + 'history', + 'lodash', + 'react', + 'react-dom', + 'react-redux', + 'react-router', + 'react-router-redux', + 'redux', + 'redux-thunk' + ] + }, + output: { + path: path.join(commonPath.dist, 'static'), + publicPath: '/static/' + }, + resolve: { + extensions: ['.js', '.jsx'], + alias: { + // ================================ + // 自定义路径别名 + // ================================ + ASSET: path.join(src, 'assets'), + COMPONENT: path.join(src, 'components'), + ACTION: path.join(src, 'redux/actions'), + REDUCER: path.join(src, 'redux/reducers'), + STORE: path.join(src, 'redux/store'), + ROUTE: path.join(src, 'routes'), + SERVICE: path.join(src, 'services'), + UTIL: path.join(src, 'utils'), + HOC: path.join(src, 'utils/HoC'), + MIXIN: path.join(src, 'utils/mixins'), + VIEW: path.join(src, 'views') } - return _loaders; - })(), - include: src, - exclude: /node_modules/ - }, { - test: /\.json$/, - loader: 'json' - }, { - test: /\.html$/, - loader: 'html' - }, { - test: /\.(png|jpe?g|gif|svg)$/, - loader: 'url', - query: { - limit: 10240, // 10KB 以下使用 base64 - name: 'img/[name]-[hash:6].[ext]' - } - }, { - test: /\.(woff2?|eot|ttf|otf)$/, - loader: 'url-loader?limit=10240&name=fonts/[name]-[hash:6].[ext]' - }] - }, - eslint: { - formatter: require('eslint-friendly-formatter') - }, - plugins: [ - new NyanProgressPlugin(), // 进度条 - new webpack.DefinePlugin({ - 'process.env': { // 这是给 React / Redux 打包用的 - NODE_ENV: JSON.stringify('production') - }, - // ================================ - // 配置开发全局常量 - // ================================ - __DEV__: env === 'development', - __PROD__: env === 'production', - __COMPONENT_DEVTOOLS__: false, // 是否使用组件形式的 Redux DevTools - __WHY_DID_YOU_UPDATE__: false // 是否检测不必要的组件重渲染 - }) - ] + }, + module: { + rules: [ + { + test: /\.(js|jsx)$/, + use: (function () { + var _loaders = ['babel-loader?' + JSON.stringify({ + cacheDirectory: true, + plugins: [ + 'transform-runtime', + 'transform-decorators-legacy' + ], + presets: ['es2015', 'react', 'stage-0'], + env: { + production: { + presets: ['react-optimize'] + } + } + }), { + loader: 'eslint-loader', + options: { + formatter: require('eslint-friendly-formatter') // 编译后错误报告格式 + } + } + ]; + + // 开发环境下引入 React Hot Loader + if (env === 'development') { + _loaders.unshift('react-hot-loader'); + } + return _loaders; + })(), + include: src, + exclude: /node_modules/, + }, { + test: /\.html$/, + use: 'html-loader' + }, { + test: /\.(png|jpe?g|gif|svg)$/, + loader: 'url', + query: { + limit: 10240, // 10KB 以下使用 base64 + name: 'img/[name]-[hash:6].[ext]' + } + }, { + test: /\.(woff2?|eot|ttf|otf)$/, + use: 'url-loader?limit=10240&name=fonts/[name]-[hash:6].[ext]' + }] + }, + plugins: [ + new NyanProgressPlugin(), // 进度条 + new webpack.DefinePlugin({ + 'process.env': { // 这是给 React / Redux 打包用的 + NODE_ENV: JSON.stringify('production') + }, + // ================================ + // 配置开发全局常量 + // ================================ + __DEV__: env === 'development', + __PROD__: env === 'production', + __COMPONENT_DEVTOOLS__: false, // 是否使用组件形式的 Redux DevTools + __WHY_DID_YOU_UPDATE__: false // 是否检测不必要的组件重渲染 + }) + ] }; + +module.exports = { + commonPath: commonPath, + configBase: configBase +} \ No newline at end of file diff --git a/build/webpack.dev.conf.js b/build/webpack.dev.conf.js index 890918a..81d7684 100644 --- a/build/webpack.dev.conf.js +++ b/build/webpack.dev.conf.js @@ -1,10 +1,11 @@ var webpack = require('webpack'), - config = require('./webpack.base.conf'), - HtmlWebpackPlugin = require('html-webpack-plugin'), - ExtractTextPlugin = require('extract-text-webpack-plugin'), - BrowserSyncPlugin = require('browser-sync-webpack-plugin'), - // SOURCE_MAP = true; // 大多数情况下用不到 - SOURCE_MAP = false; + config = require('./webpack.base.conf').configBase, + commonPath = require('./webpack.base.conf').commonPath, + HtmlWebpackPlugin = require('html-webpack-plugin'), + ExtractTextPlugin = require('extract-text-webpack-plugin'), + BrowserSyncPlugin = require('browser-sync-webpack-plugin'), +// SOURCE_MAP = true; // 大多数情况下用不到 + SOURCE_MAP = false; config.output.filename = '[name].js'; config.output.chunkFilename = '[id].js'; @@ -13,45 +14,54 @@ config.devtool = SOURCE_MAP ? 'eval-source-map' : false; // add hot-reload related code to entry chunk config.entry.app = [ - 'eventsource-polyfill', - 'webpack-hot-middleware/client?reload=true', - 'webpack/hot/only-dev-server', - config.entry.app + 'eventsource-polyfill', + 'webpack-hot-middleware/client?reload=true', + 'webpack/hot/only-dev-server', + config.entry.app ]; config.output.publicPath = '/'; // 开发环境下直接内嵌 CSS 以支持热替换 -config.module.loaders.push({ - test: /\.css$/, - loader: 'style!css' +config.module.rules.push({ + test: /\.css$/, + use: [ + 'style-loader', + 'css-loader' + ] }, { - test: /\.less$/, - loader: 'style!css!less' + test: /\.less$/, + use: [ + 'style-loader', + 'css-loader', + 'less-loader' + ] }, { - test: /\.scss$/, - loader: 'style!css!sass' + test: /\.scss$/, + use: [ + 'style-loader', + 'css-loader', + 'sass-loader' + ] }); config.plugins.push( - new webpack.optimize.OccurenceOrderPlugin(), - new webpack.HotModuleReplacementPlugin(), - new webpack.NoErrorsPlugin(), - new ExtractTextPlugin('[name].css'), - new HtmlWebpackPlugin({ - filename: 'index.html', - template: config.commonPath.indexHTML, - chunksSortMode: 'none' - }), - new BrowserSyncPlugin({ - host: '127.0.0.1', - port: 9090, - proxy: 'http://127.0.0.1:9000/', - logConnections: false, - notify: false - }, { - reload: false - }) + new webpack.HotModuleReplacementPlugin(), + new ExtractTextPlugin('[name].css'), + new HtmlWebpackPlugin({ + filename: 'index.html', + template: commonPath.indexHTML, + chunksSortMode: 'none' + }), + new BrowserSyncPlugin({ + host: '127.0.0.1', + port: 9090, + proxy: 'http://127.0.0.1:9000/', + logConnections: false, + notify: false + }, { + reload: false + }) ); module.exports = config; diff --git a/build/webpack.prod.conf.js b/build/webpack.prod.conf.js index 6a511de..4ad025e 100644 --- a/build/webpack.prod.conf.js +++ b/build/webpack.prod.conf.js @@ -1,5 +1,6 @@ var webpack = require('webpack'), - config = require('./webpack.base.conf'), + config = require('./webpack.base.conf').configBase, + commonPath = require('./webpack.base.conf').commonPath, HtmlWebpackPlugin = require('html-webpack-plugin'), CopyWebpackPlugin = require('copy-webpack-plugin'), CleanWebpackPlugin = require('clean-webpack-plugin'), @@ -14,23 +15,23 @@ config.devtool = SOURCE_MAP ? 'source-map' : false; // 生产环境下分离出 CSS 文件 config.module.loaders.push({ test: /\.css$/, - loader: ExtractTextPlugin.extract('style', 'css') + loader: ExtractTextPlugin.extract('style-loader', 'css-loader') }, { test: /\.less$/, - loader: ExtractTextPlugin.extract('style', 'css!less') + loader: ExtractTextPlugin.extract('style-loader', 'css-loader', 'less-loader') }, { test: /\.scss$/, - loader: ExtractTextPlugin.extract('style', 'css!sass') + loader: ExtractTextPlugin.extract('style-loader', 'css-loader', 'sass-loader') }); config.plugins.push( new CleanWebpackPlugin('dist', { - root: config.commonPath.rootPath, + root: commonPath.rootPath, verbose: false }), new CopyWebpackPlugin([ // 复制高度静态资源 { - context: config.commonPath.staticDir, + context: commonPath.staticDir, from: '**/*', ignore: ['*.md'] } @@ -41,7 +42,6 @@ config.plugins.push( warnings: false } }), - new webpack.optimize.OccurenceOrderPlugin(), new webpack.optimize.CommonsChunkPlugin({ // 公共代码分离打包 names: ['vendor', 'mainifest'] @@ -55,7 +55,7 @@ config.plugins.push( }), new HtmlWebpackPlugin({ filename: '../index.html', - template: config.commonPath.indexHTML, + template: commonPath.indexHTML, chunksSortMode: 'none' }) ); diff --git a/package.json b/package.json index 96f35e8..73a71d1 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "eventsource-polyfill": "^0.9.6", "express": "^4.13.3", "express-favicon": "^1.0.1", - "extract-text-webpack-plugin": "^0.9.1", + "extract-text-webpack-plugin": "^2.1.2", "file-loader": "^0.8.4", "html-loader": "^0.4.3", "html-webpack-plugin": "^1.7.0", @@ -65,7 +65,7 @@ "sass-loader": "^3.2.0", "style-loader": "^0.13.1", "url-loader": "^0.5.7", - "webpack": "^1.12.2", + "webpack": "^3.0.0", "webpack-dev-middleware": "^1.4.0", "webpack-hot-middleware": "^2.6.0", "why-did-you-update": "0.0.8" From b257537b2142135b07951dc8b8650d07f85624f1 Mon Sep 17 00:00:00 2001 From: liaoyunda Date: Tue, 20 Jun 2017 15:44:37 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=9B=B4=E6=96=B0html-webpack-plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/webpack.dev.conf.js | 2 +- build/webpack.prod.conf.js | 13 +++++++------ package.json | 2 +- src/index.html | 6 ------ 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/build/webpack.dev.conf.js b/build/webpack.dev.conf.js index 81d7684..042f4d0 100644 --- a/build/webpack.dev.conf.js +++ b/build/webpack.dev.conf.js @@ -51,7 +51,7 @@ config.plugins.push( new HtmlWebpackPlugin({ filename: 'index.html', template: commonPath.indexHTML, - chunksSortMode: 'none' + chunksSortMode: 'dependency' }), new BrowserSyncPlugin({ host: '127.0.0.1', diff --git a/build/webpack.prod.conf.js b/build/webpack.prod.conf.js index 4ad025e..d06b18a 100644 --- a/build/webpack.prod.conf.js +++ b/build/webpack.prod.conf.js @@ -13,15 +13,15 @@ config.output.chunkFilename = '[id].[chunkhash:6].js'; config.devtool = SOURCE_MAP ? 'source-map' : false; // 生产环境下分离出 CSS 文件 -config.module.loaders.push({ +config.module.rules.push({ test: /\.css$/, - loader: ExtractTextPlugin.extract('style-loader', 'css-loader') + loader: ExtractTextPlugin.extract({fallback: 'style-loader', use: 'css-loader'}) }, { test: /\.less$/, - loader: ExtractTextPlugin.extract('style-loader', 'css-loader', 'less-loader') + loader: ExtractTextPlugin.extract({fallback: 'style-loader', use: ['css-loader', 'less-loader']}) }, { test: /\.scss$/, - loader: ExtractTextPlugin.extract('style-loader', 'css-loader', 'sass-loader') + loader: ExtractTextPlugin.extract({fallback: 'style-loader', use: ['css-loader', 'sass-loader']}) }); config.plugins.push( @@ -50,13 +50,14 @@ config.plugins.push( new webpack.optimize.MinChunkSizePlugin({ minChunkSize: 30000 }), - new ExtractTextPlugin('[name].[contenthash:6].css', { + new ExtractTextPlugin({ + filename: '[name].[contenthash:6].css', allChunks : true // 若要按需加载 CSS 则请注释掉该行 }), new HtmlWebpackPlugin({ filename: '../index.html', template: commonPath.indexHTML, - chunksSortMode: 'none' + chunksSortMode: 'dependency' }) ); diff --git a/package.json b/package.json index 73a71d1..f57d037 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "extract-text-webpack-plugin": "^2.1.2", "file-loader": "^0.8.4", "html-loader": "^0.4.3", - "html-webpack-plugin": "^1.7.0", + "html-webpack-plugin": "^2.28.0", "inject-loader": "^2.0.1", "json-loader": "^0.5.4", "less": "^2.6.0", diff --git a/src/index.html b/src/index.html index 712c8ce..fc8ba10 100644 --- a/src/index.html +++ b/src/index.html @@ -5,9 +5,6 @@ React Demo - {% for (var css in o.htmlWebpackPlugin.files.css) { %} - - {% } %} @@ -15,8 +12,5 @@ -{% for (var chunk in o.htmlWebpackPlugin.files.chunks) { %} - -{% } %}