接触前端还不到一年,之前公司有一个UI的工程由我来独立完成,犹豫了几秒后决定选用react,花几天时间看完了阮一峰老师的React 技术栈系列教程就开始动手了,其中在webpack的配置上花了很多时间,因此整理了一下当成一个学习笔记吧
webpack2大部分的配置还是和webpack1一样,官方文档中有一篇从1迁移到2的指南
npm install -D webpack webpack-dev-middleware webpack-dev-server webpack-hot-middleware babel-preset-react-hmre
在webpack1的时候是使用的react-hot-loader,后来无意间发现了这篇文章The Death of React Hot Loader(只看懂了标题),果断弃用之,选用react-transform-hmr
如下方的简单示例
const webpack = require('webpack');
module.exports = {
entry: {
app: 'entrance.js'
},
output: {
path: 'fvrd/dist',
filename: '[name].js',
},
module: {
rules:[{
test: /\.css$/,
use:['style-loader', 'css-loader']
}],
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
],
};
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
//代码中
if(process.env.NODE_ENV==='production'){
//do something
}else{
//do something
}
//一个配置文件
entry: {
lib: ['react','react-dom','react-router','redux','...'],
}
new webpack.DllPlugin({
path: path.join(__dirname, 'fvrd/dist/manifest.json'),
name: '[name]',//在这个例子中将生成lib.js
context: __dirname,
})
//另一个配置文件
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./fvrd/dist/manifest.json'),
})
//以后使用jquery无需再require('jquery')
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
'window.$': 'jquery',
})
new webpack.optimize.UglifyJsPlugin({//压缩代码
output: {
comments: false, //不移除注释
},
compress: {
warnings: false//忽略警告,某些没有用到的组件在打包时会被移除,这时会产生警告,无需在意,webpack1默认true,webpack2默认false
},
})
//npm install html-webpack-plugin --save-dev
let HtmlWebpackPlugin = require('html-webpack-plugin');
//...
new HtmlWebpackPlugin({
favicon:'./src/images/icon_logo.png', //favicon路径
filename: '../index.html', //生成的html存放路径,相对于 path
template: './src/template/index.html', //html模板路径
inject: true,
hash: true,
minify: {//压缩HTML文件
removeComments:true, //移除HTML中的注释
collapseWhitespace:true //删除空白符与换行符
}
})
//webpack2需要安装beta版
//npm install --save-dev extract-text-webpack-plugin@beta
let ExtractTextPlugin = require('extract-text-webpack-plugin');
//...
module:{
rules:[{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use:[{
loader: 'css-loader',
options: {
sourceMap: true
}
},{
loader: 'less-loader',
options: {
sourceMap: true
}
}]
})
}]
}
//plugins中
new ExtractTextPlugin('[name].css')
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
下面为我的工程中所使用的配置文件
使用DllPlugin将第三方公共模块打包在一起,可以极快的提高打包速度,在生产和开发环境都需要用到
//webpack.config.dll.js
const webpack = require('webpack');
const path = require('path');
module.exports = {
entry: {
lib: [//公共组件
'react',
'react-dom',
'react-router',
'react-bootstrap',
'redux',
'react-redux',
'redux-thunk',
'react-intl',
'intl',
'react-dnd',
'react-dnd-html5-backend',
'immutable',
'antd',
'moment',
'isomorphic-fetch',
'pure-render-decorator',
// 'lodash',
],
},
output: {
path: 'fvrd/dist',
filename: '[name].js',
library: '[name]',
},
module: {
rules:[{
test: /\.css$/,
use:['style-loader', 'css-loader', 'autoprefixer-loader']
},{
test: /\.less$/,
use:['style-loader', 'css-loader', 'autoprefixer-loader', 'less-loader']
}],
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
new webpack.DllPlugin({
path: path.join(__dirname, 'fvrd/dist/manifest.json'),
name: '[name]',
context: __dirname,
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
mangle: {
except: ['exports', 'require']
}
})
],
};
在这里遇到了一个问题,由于react热加载模块是一个babel插件,且只用在开发模式下,因此在.babelrc中需要如下配置
"env": {
"development": {
"presets": ["react-hmre"]
}
}
然而无论实在命令行中还是配置文件中定义的环境变量在webpack配置文件中都获取不到,后来在配置文件中加上process.env.BABEL_ENV='production';
后问题解决
//webpack.config.dist.js
let path = require('path');
let webpack = require('webpack');
let ExtractTextPlugin = require('extract-text-webpack-plugin');
let HtmlWebpackPlugin = require('html-webpack-plugin');
let ROOT_PATH = path.resolve(__dirname);
let APP_PATH = path.resolve(ROOT_PATH, 'src');
let APP_FILE = path.resolve(APP_PATH, 'app');
let BUILD_PATH = path.resolve(ROOT_PATH, 'fvrd/dist');
process.env.BABEL_ENV='production';
module.exports = {
entry: {
app: ['babel-polyfill',APP_FILE],
},
output: {
publicPath: '/fvrd/dist/',
path: BUILD_PATH,
filename: '[name].js',
chunkFilename: '[name].[chunkhash:5].min.js',
},
module: {
rules:[{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use:[{
loader: "css-loader",
options: {
sourceMap: true
}
}]
})
},{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use:[{
loader: 'css-loader',
options: {
sourceMap: true
}
},{
loader: 'less-loader',
options: {
sourceMap: true
}
}]
})
}, {
test: /\.(eot|woff|svg|ttf|woff2|gif|appcache)(\?|$)/,
exclude: /^node_modules$/,
use:[{
loader: 'file-loader',
options: {
name: '[name].[ext]',
}
}],
}, {
test: /\.(png|jpg|gif)$/,
exclude: /^node_modules$/,
use:[{
loader: 'url-loader',
options: {
name: 'images/[hash:8].[name].[ext]',
limit: '8192'
}
}],
}, {
test: /\.jsx?$/,
exclude: /^node_modules$/,
use:['babel-loader'],
}],
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
BABEL_ENV: JSON.stringify('production'),
}
}),
new HtmlWebpackPlugin({
favicon:'./src/images/icon_logo.png',
filename: '../index.html',
template: './src/template/index.html',
inject: true,
hash: true,
minify: {
removeComments:true,
collapseWhitespace:true
}
}),
new ExtractTextPlugin('[name].css'),
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./fvrd/dist/manifest.json'),//此处路径为上面webpack.config.dll.js中DllPlugin插件中的path
}),
new webpack.optimize.UglifyJsPlugin({
output: {
comments: false,
},
compress: {
warnings: false
},
})
],
resolve: {
extensions: ['.js', '.jsx', '.css'] //后缀名自动补全
}
};
开发环境实现react的热加载,有两个配置文件(当然可以配置为一个)
//webpack.config.hot.js
let path = require('path');
let webpack = require('webpack');
let ROOT_PATH = path.resolve(__dirname);
let APP_PATH = path.resolve(ROOT_PATH, 'src');
let APP_FILE = path.resolve(APP_PATH, 'app');
let BUILD_PATH = path.resolve(ROOT_PATH, '/fvrd/dist');
module.exports = {
entry: {
app: [
'webpack-dev-server/client?http://172.16.16.30:8088',
'webpack/hot/only-dev-server',
'babel-polyfill',
APP_FILE,
],
},
output: {
publicPath: '/fvrd/dist/', //webpack-dev-server访问的路径
path: BUILD_PATH, //发布文件地址
filename: '[name].js', //编译后的文件名字
chunkFilename: '[name].[chunkhash:5].min.js',
},
module: {
rules:[{
test: /\.css$/,
use:['style-loader', 'css-loader', 'autoprefixer-loader']
},{
test: /\.less$/,
use:['style-loader', 'css-loader', 'autoprefixer-loader', 'less-loader']
}, {
test: /\.(eot|woff|svg|ttf|woff2|gif|appcache)(\?|$)/,
exclude: /^node_modules$/,
use:[{
loader: 'file-loader',
options: {
name: '[name].[ext]',
}
}],
}, {
test: /\.(png|jpg|gif)$/,
exclude: /^node_modules$/,
use:[{
loader: 'url-loader',
options: {
name: 'images/[hash:8].[name].[ext]',
limit: '8192'
}
}],
}, {
test: /\.jsx?$/,
exclude: /^node_modules$/,
use:['babel-loader'],
}],
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('development')
}
}),
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./fvrd/dist/manifest.json'),
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
],
resolve: {
extensions: ['.js', '.jsx', '.css'] //后缀名自动补全
}
};
//server_hot.js
let webpack = require('webpack');
let WebpackDevServer = require('webpack-dev-server');
let config = require('./webpack.config.hot');
let server = new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
hot: true,
historyApiFallback: true,
inline: true,
// progress: true,
stats: {
colors: true
},
});
//将其他路由,全部返回index.html
server.app.get('*', function(req, res) {
res.sendFile(__dirname + '/index.html')
});
server.listen(8088, function() {
console.log('监听8088端口')
});
在package.json中定义如下
"scripts": {
"hot": "node server_hot.js",
"dll": "webpack --config webpack.config.dll.js --progress --colors -p",
"dist": "webpack --config webpack.config.dist.js --progress --colors -p"
}
#生产环境打包
npm run dll
npm run dist
#开发环境
npm run dll
npm run hot
package.json
{
"name": "fvrd_ui",
"version": "3.1.0",
"description": "react-fvrd_ui",
"main": "./server_hot.js",
"scripts": {
"hot": "node server_hot.js",
"dll": "webpack --config webpack.config.dll.js --progress --colors -p",
"dist": "webpack --config webpack.config.dist.js --progress --colors -p"
},
"author": "stsc team",
"dependencies": {
"antd": "^2.5.2",
"bootstrap": "^3.3.7",
"immutable": "^3.8.1",
"intl": "^1.2.5",
"moment": "^2.17.1",
"pure-render-decorator": "^1.2.1",
"rc-pagination": "^1.7.0",
"react": "^15.3.2",
"react-bootstrap": "^0.30.7",
"react-dnd": "^2.1.4",
"react-dnd-html5-backend": "^2.1.2",
"react-dom": "^15.3.2",
"react-intl": "^2.2.3",
"react-redux": "^5.0.2",
"react-router": "^3.0.0",
"redux": "^3.6.0",
"redux-thunk": "^2.1.0"
},
"devDependencies": {
"autoprefixer-loader": "^3.2.0",
"babel-core": "^6.23.1",
"babel-loader": "^6.2.8",
"babel-plugin-import": "^1.1.0",
"babel-plugin-react-intl": "^2.3.1",
"babel-plugin-react-transform": "^2.0.2",
"babel-plugin-transform-class-properties": "^6.23.0",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-plugin-transform-object-assign": "^6.8.0",
"babel-polyfill": "^6.23.0",
"babel-preset-latest": "^6.16.0",
"babel-preset-react": "^6.23.0",
"babel-preset-react-hmre": "^1.1.1",
"babel-preset-stage-3": "^6.17.0",
"body-parser": "^1.15.1",
"chunk-manifest-webpack-plugin": "^1.0.0",
"css-loader": "^0.26.1",
"express": "^4.14.0",
"extract-text-webpack-plugin": "^2.0.0-rc.3",
"file-loader": "^0.10.0",
"html-webpack-plugin": "^2.22.0",
"jsx-loader": "^0.13.2",
"less": "^2.6.1",
"less-loader": "^2.2.3",
"postcss-loader": "^1.3.0",
"react-transform-catch-errors": "^1.0.2",
"react-transform-hmr": "^1.0.4",
"redbox-react": "^1.3.3",
"redux-promise": "^0.5.3",
"resolve-url-loader": "^1.6.0",
"style-loader": "^0.13.1",
"url-loader": "^0.5.7",
"webpack": "^2.2.1",
"webpack-dev-middleware": "^1.8.4",
"webpack-dev-server": "^2.3.0",
"webpack-hot-middleware": "^2.13.2"
}
}
.babelrc
{
"presets": [["latest", {"modules": false}], "react", "stage-3"],
"plugins": [
"transform-decorators-legacy",
"transform-class-properties",
["import", {"libraryName": "antd","style": true}],
["react-intl", {"messagesDir": "./fvrd/dist/"}]
],
"env": {
"development": {
"presets": ["react-hmre"]
}
}
}
打包脚本 package.sh
#!/bin/sh
svn_version=`svn info 2>/dev/null | grep "Revision:" | sed 's/Revision: //'`
version="fvrd_ui_r3.1.0_$svn_version"
chmod +x node_modules/.bin/*
npm run dll
npm run dist
mkdir $version
cp -r fvrd $version
cp install.sh $version
find $version -name ".svn" -exec rm -fr {} \;
tar -zcvf $version.tar.gz $version