之前在开发一个react网站项目, 到正式部署到云服务器时才发现对react及webpack生产环境配置没有什么了解, 下面就总结一下自己配置生产环境的内容.
首先就是要进行打包后的js文件的压缩, 其中要使用uglifyjs-webpack-plugin
, 首先使用npm安装这个包, 然后在配置文件中加入插件配置项:
// js压缩插件
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
然后再在plugins插件部分添加它的使用:
...
plugins: [
// 压缩Js
new UglifyJSPlugin({
sourceMap: true
})
]
...
css压缩配置相对容易, 只要在css-loader的options中加入压缩的选项为true即可, 即:
...
{
loader: 'css-loader',
options: {
minimize: true
}
}
...
此处需要配置两个内容: 一是url-loader
二是image-webpack-loader
. 两个loader联用可以将各类在引入时进行压缩. 配置如下:
...
{
test: /\.(gif|png|jpe?g|svg)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
},
{
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true,
mozjpeg: {
progressive: true,
quality: 40
},
},
},
]
}
...
以image-webpack-loader
中的mozjpeg
为例, mozjpeg
是用来压缩jpeg格式文件的配置, 其中的qulity即是其压缩质量为40%, 其余对不同的图片格式使用了不同的技术去压缩, 需要分别配置.
在配置时有几个注意的点:
1. 经测试, 在js文件及css文件中引用图片要使用相对路径格式, webpack会将引入的文件单独压缩打包到输出文件夹中. 如import product1 from '../img/product/product-img1.png'
2. 在webpack官网配置教程中使用的是file-loader
与image-webpack-loader
, 这种配置方式对css中引用的图片无法进行压缩, 所以要使用url-loader
才可以对css中如background: url(...)
中引用的图片进行压缩.
可以将代码中公共的部分提取处理单独引用, 其中要使用webpack的CommonsChunkPlugin插件, 配置方法是在plugin中加入相应的内容:
...
plugins: [
// Plugin for code splitting, 抽离出公共部分单独打包
new webpack.optimize.CommonsChunkPlugin({
name: 'common' // Specify the common bundle's name.
})
...
]
...
在抽离出公共部分后, html中也要相应的加入内容, 如:
<script src="common.bundle.js">script>
可以将所有css单独提取为一个文件, 和js等并行进行读取. 其中要使用ExtractTextPlugin
, 配置方法分两部分, 一是在loader部分配置, 二是在plugin部分配置.
...
{
test: /\.(css|less)$/,
// 此处使用ExtractTextPlugin插件
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
minimize: true
}
},
{
loader: 'less-loader'
}
]
})
}
...
plugins: [
// Plugin for code splitting, css单独打包
new ExtractTextPlugin('style.css'),
]
...
此处配置执行打包所有css为style.css
这个文件, 然后需要在html中也单独引用这一css.
react配置生产模式与process.env.NODE_ENV
环境变量关联, 配置方法是使用 webpack 内置的 DefinePlugin
.
...
plugins: [
// react配置webpack生产模式
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
]
...
使用react生产环境时, 在babel中也要相应进行配置, 对开发和生产环境分别进行配置:
"presets": [
"react",
"es2015"
],
"env": {
"development": {
"plugins": [
[
"react-transform",
{
"transforms": [
{
"transform": "react-transform-hmr",
"imports": [
"react"
],
"locals": [
"module"
]
}
]
}
],
[
"syntax-dynamic-import",
[
"import-inspector",
{
"serverSideRequirePath": true
}
]
]
]
},
"production": {
"plugins": [
[
"syntax-dynamic-import",
[
"import-inspector",
{
"serverSideRequirePath": true
}
]
]
]
}
}
}
其中的development和production分别配置了开发环境和生产环境中使用的插件. 如此处的区别就在于生产环境中去除了热更新的部分, 都保留了动态import句法的部分.
另外, 在生产环境进行打包时, 需要在命令行中加入BABEL_ENV=production
, 告诉babel本次打包是用在生产环境中.
之前在开发时都是直接使用一个webpack.config.js
配置文件, 如果要区分生产和开发两种环境, 就要对配置文件进行分离. 参考官方教程, 新建webpack.dev.js
和webpack.prod.js
.
然后, 为了避免重复, 要将两种环境下相同的内容放在一个公共的配置文件中, 所以新建webpack.common.js
用来保存这个部分:
const webpack = require('webpack');
// css代码分割Plugin
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const path = require('path');
const OUTPUT_PATH = path.resolve(__dirname, 'dist');
module.exports = {
entry: {
index: './app/index.js',
},
output: {
// __dirname: 全局变量, 存储的是文件所在的文件目录
// path.resolve()(node)将一系列路径或路径片段解析为一个绝对路径
path: OUTPUT_PATH,
filename: '[name].bundle.js'
},
module: {
rules: [
{
// test: 一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)
test: /(\.js|\.jsx)$/,
// include/exclude: 手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选)
exclude: /^node_modules$/,
use: {
loader: 'babel-loader'
// 此处的options: {...} 单独提取到了.babelrc中
}
},
{
test: /\.(gif|png|jpe?g|svg)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
}
},
{
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true,
mozjpeg: {
progressive: true,
quality: 40
},
},
},
]
},
{
test: /\.(css|less)$/,
// 此处使用ExtractTextPlugin插件
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
minimize: true
}
},
{
loader: 'less-loader'
}
]
})
}
]
},
plugins: [
// Plugin for code splitting, 抽离出公共部分单独打包
new webpack.optimize.CommonsChunkPlugin({
name: 'common' // Specify the common bundle's name.
}),
// Plugin for code splitting, css单独打包
new ExtractTextPlugin('style.css'),
]
};
其中使用公共部分需要用到webpack-merge
, 需要npm安装一下这个包.
之后在webpack.dev.js
和webpack.prod.js
中分离开发环境和生产环境所用到的代码, 如:
// webpack.dev.js
const webpack = require('webpack');
const merge = require('webpack-merge');
const common = require('./webpack.common');
module.exports = merge(common, {
devtool: 'inline-source-map',
devServer: {
// 本地服务器所加载页面所在目录
contentBase: '../',
// 是否跳转, 单页面时使用ture
historyApiFallback: true,
// inline模式, 表示脚本将会在动态重载时被插入到bundle中
inline: true,
// 允许Hot Module Replacement功能
hot: true
},
plugins: [
// 也用于热加载, 配置后会显示更新的模块名称
new webpack.NamedModulesPlugin(),
// 热加载插件: npm install --save-dev babel-plugin-react-transform react-transform-hmr
new webpack.HotModuleReplacementPlugin(),
]
});
此处在webpack.dev.js中单独抽离出了开发环境的代码, 需要注意的有以下几点:
const common = require('./webpack.common');
来将公共部分引入merge()
去和单独的部分进行整合同理, prod即生产环境也使用同样的方式进行整合, 如:
// webpack.prod.js
const webpack = require('webpack');
// 用于配置文件的合并
const merge = require('webpack-merge');
// js压缩插件
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
// 抽离出公共部分
const common = require('./webpack.common');
module.exports = merge(common, {
plugins: [
// 压缩Js
new UglifyJSPlugin({
sourceMap: true
}),
// react配置webpack生产模式
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
]
});
最后一步要做的就是在package.json中加入开发环境和生产环境中不同的命令, 因为两种环境使用的配置文件不同, 所以要在命令中指定:
"scripts": {
"start": "webpack-dev-server --open --port 3000 --config webpack.dev.js",
"build": "BABEL_ENV=production webpack --config webpack.prod.js"
},
--config
指定了生产环境的配置文件