这几天有空给自己写的一个react小项目的webpack配置进行优化,在这里进行一下总结。
项目地址:https://github.com/beiweiqiang/myh5
我把这个项目的开发环境和生产环境各写了一份webpack.config,用一个总的 webpack.config.js
进行switch:
// webpack.config.js
const webpackProduction = require('./webpack.prod.js');
const webpackDevelopment = require('./webpack.dev.js');
const isProduction = process.env.NODE_ENV === 'production';
process.noDeprecation = true;
module.exports = isProduction ? webpackProduction : webpackDevelopment;
对开发环境进行判断,switch合适的webpack配置。
我们先说开发环境,因为是react项目,开发环境用到了hmr,要用hmr,在项目入口处的代码还要进行一定的配置,下面是官方例子:
import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
// AppContainer 是一个 HMR 必须的包裹(wrapper)组件
import App from './components/App';
const render = (Component) => {
ReactDOM.render(
<AppContainer>
<Component/>
</AppContainer>,
document.getElementById('root')
);
};
render(App);
// 模块热替换的 API
if (module.hot) {
module.hot.accept('./components/App', () => {
render(App)
});
}
这里我在开发过程中有一个bug,就是console上虽然说了 app up to date,但是chrome浏览器自身不会进行自动刷新,经过google以后得到解决方案。
把最后部分改成下面这样就行:
// 模块热替换的 API
if (module.hot) {
module.hot.accept();
}
说回开发环境下的webpack配置,因为代码有点长,所以就不贴在这里,可以参考我的项目的 webpack.dev.js
文件。
说几个地方:
1
output: {
path: path.resolve(__dirname, 'client', 'dist'),
// filename: '[name].[chunkhash].js',
filename: '[name].js',
publicPath: '/',
}
这里并没有像生产环境下的webpack配置 (下面会介绍) 一样给文件加上hash值,因为计算这个hash会耗费时间,开发环境讲究构建速度要快,所以不需要这个hash值。
2
devtool: 'inline-source-map'
开启 source-map
3
devServer: {
historyApiFallback: true,
// Inline mode is recommended when using Hot Module Replacement.
inline: true,
open: true,
hot: true,
overlay: {
errors: true,
warnings: true,
},
watchOptions: {
aggregateTimeout: 300,
poll: 1000,
},
// contentBase: path.resolve(__dirname, 'client', 'dist'),
contentBase: '/',
// match the output path
publicPath: '/',
// match the output `publicPath`
port: 8080,
proxy: {
'/**': {
target: 'http://localhost:3000',
secure: false,
changeOrigin: true,
},
},
},
open: true
会在命令执行时自动打开页面,contentBase: '/'
资源文件放置处,这里讲一下 proxy:
因为我的项目有自己的后台,用nodejs写的,运行在3000端口,但是webpack-dev-server也有一个自己的运行后台,运行在 port: 8080
端口,那该怎么办?
这时我们就需要进行代理proxy:
host
默认是 localhost
,对8080端口下的服务器进行请求,会自动把相同的请求转到3000下,比如我们请求 localhost:8080/api
,代理以后会自动请求 localhost:3000/api
,我这里是把所以请求都转到端口3000下,也可以只进行 /api
代理:
'/api': {
target: 'http://localhost:3000/api',
secure: false,
changeOrigin: true,
},
4
关于 plugins:
plugins: [
// 除了自己写的代码,将其他的modules都bundle成一个vendor文件
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks(module) {
// 该配置假定你引入的 vendor 存在于 node_modules 目录中
return module.context && module.context.indexOf('node_modules') !== -1;
},
}),
// 生产manifest文件,用于在html模板中插入script
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity,
}),
// 生成插入script后的 html 文件
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'server', 'static', 'index_template.html'),
chunksSortMode: 'dependency',
minify: {
collapseWhitespace: true,
removeComments: true,
},
}),
// HMR
new webpack.HotModuleReplacementPlugin(),
// enable HMR globally
new webpack.NamedModulesPlugin(),
// prints more readable module names in the browser console on HMR updates
],
说到生成环境的webpack配置,可以参考我的项目的 webpack.prod.js
文件。
生产环境下我们就要兼顾很多方面,比如服务端发送到前端的文件要小,保证加载速度快;关于modules方面的代码我们很少会去修改,所以可以在前端进行缓存起来。
1
output: {
path: path.resolve(__dirname, 'client', 'dist'),
filename: '[name].[chunkhash].js',
publicPath: '/',
},
这里对文件名进行了hash处理,浏览器会自动缓存js文件,如果收到的文件的文件名是一样的,浏览器可能不会进行自动更新,所以我们加上hash值,保证每一次更新以后的文件名不同,从而让浏览器再次请求资源。
而那些modules代码基本是不会变的,所以保证它们的hash值不变,在浏览器处缓存起来。
2
plugins
plugins: [
// 将node_modules里的依赖整合成一个vendor
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks(module) {
// 该配置假定你引入的 vendor 存在于 node_modules 目录中
return module.context && module.context.indexOf('node_modules') !== -1;
},
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
}),
// 根据manifest生成插入script后的html
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'server', 'static', 'index_template.html'),
chunksSortMode: 'dependency',
minify: {
collapseWhitespace: true,
removeComments: true,
},
}),
// 将js文件压缩成gz
new CompressionPlugin(),
// 允许创建一个在编译时可以配置的全局常量
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"',
},
}),
// 压缩
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
},
// sourceMap: true,
}),
new WebpackMd5Hash(),
new webpack.NoEmitOnErrorsPlugin(),
],
除了将文件进行 uglify 以外,这里还将文件进行gz压缩 new CompressionPlugin()
。
在后端部分还要加上一些代码:
// 获取js文件时选择gz压缩文件
app.get('*.js', (req, res, next) => {
req.url = req.url + '.gz';
res.set('Content-Encoding', 'gzip');
next();
});
以上。