在项目的开发和生产这两个阶段,Webpack的配置是有差别的,主要体现在以下三个方面:
- 开发阶段有,但生产阶段没有的配置:比如
eslint
做js语法校验、devServer
来配置webpack神奇的热模块替换(HMR, hot module replacement)功能 - 生产阶段有,但开发阶段没有的配置:比如
UglifyJsPlugin
做js代码压缩 - 开发和生产阶段皆有的配置,但配置方法不同:比如
output.publicPath
的配置、source-map
来调试js代码等
webpack相关配置
移除不必要的插件
拷贝一份开发阶段的配置文件webpack.dev.config.js
到webpack.production.config.js
中,我们基于此来进行二次的改造。首先要删去生产阶段不需要的配置:devServer
, HotModuleReplacementPlugin
, NoErrorsPlugin
等。
配置output.publicPath
说到output.publicPath
的配置,首先要讲到基于Epress框架的后端代码了:通过Epress唯一的中间件express.static
我们可以配置引入静态资源的根路径,比如app.use(express.static(path.join(__dirname, 'assets')));
。在基于Webpack的前端代码中,如果没有给output.publicPath
做相关配置,则通过HtmlWebpackPlugin
插件生成的html文件中,所有的资源引用都是相对的,比如:<script type="text/javascript" src="build/main.js"></script></body>
,而相对路径是无法找到相关资源的。
output.publicPath
的作用是可以配置URL前缀,比如当我们使用了某cdn来引入静态资源,如www.xxxcdn.com/
,则配置output.publicPath: 'www.xxxcdn.com/'
可以在通过HtmlWebpackPlugin
生成的html文件中,将所有的资源以绝对路径的形式进行引用<script type="text/javascript" src="www.xxxcdn.com/build/main.js"></script></body>
。而针对以上的Express后端引入静态资源的方式,当配置output.publicPath
为/
时候,通过HtmlWebpackPlugin
生成的html文件中,所有的资源引用都是以<script type="text/javascript" src="/build/main.js"></script></body>
形式引用的。
提取公共类库/打包公共代码
在基于Webpack构建的多页面应用中,每个页面配有唯一的入口文件,即使多个入口使用了相同的类库(比如jQuery),也会各自打包进自己的js文件中,这样无疑增加了额外的代码量。传统的前端项目并没有这样的问题,因为那个时候没有模块打包这样的过程,多个页面会引入公共的静态资源。Webpack的CommonsChunkPlugin
可以分析出各个入口文件中引用的公共代码片段,然后将其提取出来,作为公共类库供各个入口文件使用。
new webpack.optimize.CommonsChunkPlugin({
name: 'commons', // 这公共代码的 chunk 名为 'commons'
filename: '[name].bundle.js', // 生成后的文件名 commons.bundle.js
minChunks: 2 // 设定要有4个页面均加载的js模块才会被纳入公共代码
})
CommonsChunkPlugin
未解决的bug
当引入CommonsChunkPlugin
插件后,HtmlWebpackPlugin
并不会把提取出来的公共代码片段插入html模板中。目前我的做法是手动将公共资源添加到各个页面最终由HtmlWebpackPlugin
生成好的html文件中,并且公共资源文件比如在入口文件之前引入,否则会报错!如下:
<body>
<div id="content"></div>
<script type="text/javascript" src="/commons.bundle.js"></script>
<script type="text/javascript" src="/build/main.js"></script>
</body>
js代码压缩
uglifyjs是一个js代码的压缩工具,webpack自带了该插件:UglifyJsPlugin,只需要在配置文件中引入即可:
new UglifyJSPlugin()
有关文件缓存
缓存是一个好东西,当浏览器第一次请求到静态文件之后会对其进行缓存,等下次再请求该文件时,那么浏览器会直接读取缓存文件。但浏览器读取缓存是基于文件名的,会导致即使文件内容更新了,但是浏览器依旧缓存中读取该文件。因此,缓存的最好方法是保证文件名和文件内容是一一对应的。
webpack可以通过如下的配置方法来决定打包文件的唯一性:
output:{
path: path.resolve(__dirname, '../ECSQueryServices/assets'), // build directly to ECSQueryServices
publicPath: '/',
filename: 'build/[name].[hash].bundle.js'
},
react相关配置
React在开发阶段会引入一些额外的模块来做代码警告等功能,这些是我们在生产阶段不再需要的,通过配置node环境为production
可以让React以生产模式运行,配置方法如下:
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
new webpack.optimize.UglifyJsPlugin()
其他配置
当完成了上述配置后,当我们执行"build": "webpack --progress --color --watch --config ./webpack.product.config.js"
命令,会报如下的错误:
Uncaught Error: locals[0] does not appear to be a `module` object with Hot Module replacement API enabled. You should disable react-transform-hmr in production by using `env` section in Babel configuration. See the example in README: https://github.com/gaearon/react-transform-hmr
看提示应该为react-transform-hmr
插件的问题,我们在.babel.rc中配置仅在dev模式下使用react-transform-hmr
模块:
"env": {
"development": {
// only enable it when process.env.NODE_ENV is 'development' or undefined
"plugins": [["react-transform", {
"transforms": [{
"transform": "react-transform-hmr",
"imports": ["react"],
"locals": ["module"]
}]
}]]
}
}
并且在package.json中强制NODE_ENV为生产模式:"build": "set NODE_ENV=production && webpack --progress --color --watch --config ./webpack.product.config.js"
待完成
其实仅通过webpack.dev.config.js
和webpack.production.config.js
来区分开发阶段和生产阶段的配置方案是不完美的,因为两个文件中有部分的相同配置项,如果有相应的改动,则两个文件的配置都需要进行一遍更新,这样维护起来不是很方便。Webpack官网的文章Production一文讲到了Advanced Approach,相同的配置项可以统一的放在同一份webpack.common.js
中:
A more complex approach would be to have a base configuration file, containing the configuration common to both environments, and then merge that with environment specific configurations. This would yield the full configuration for each environment and prevent repetition for the common bits.
React-bootstrap
貌似并没有类似于Antd
的模块按需加载功能,需要自己配置样式文件来引入自己项目需要的组件:
Customize Bootstrap's components, Less variables, and jQuery plugins to get your very own version.
参考
React官网文章:Optimizing Performance
Webpack官网文章:Production