這幾天有空給自己寫的一個(gè)react小項(xiàng)目的webpack配置進(jìn)行優(yōu)化,在這里進(jìn)行一下總結(jié)。
項(xiàng)目地址:https://github.com/beiweiqiang/myh5
我把這個(gè)項(xiàng)目的開發(fā)環(huán)境和生產(chǎn)環(huán)境各寫了一份webpack.config舍肠,用一個(gè)總的 webpack.config.js
進(jìn)行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;
對開發(fā)環(huán)境進(jìn)行判斷严衬,switch合適的webpack配置周瞎。
我們先說開發(fā)環(huán)境,因?yàn)槭莚eact項(xiàng)目崭参,開發(fā)環(huán)境用到了hmr,要用hmr款咖,在項(xiàng)目入口處的代碼還要進(jìn)行一定的配置何暮,下面是官方例子:
import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
// AppContainer 是一個(gè) 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)
});
}
這里我在開發(fā)過程中有一個(gè)bug,就是console上雖然說了 app up to date铐殃,但是chrome瀏覽器自身不會(huì)進(jìn)行自動(dòng)刷新海洼,經(jīng)過google以后得到解決方案。
把最后部分改成下面這樣就行:
// 模塊熱替換的 API
if (module.hot) {
module.hot.accept();
}
說回開發(fā)環(huán)境下的webpack配置富腊,因?yàn)榇a有點(diǎn)長坏逢,所以就不貼在這里,可以參考我的項(xiàng)目的 webpack.dev.js
文件。
說幾個(gè)地方:
1
output: {
path: path.resolve(__dirname, 'client', 'dist'),
// filename: '[name].[chunkhash].js',
filename: '[name].js',
publicPath: '/',
}
這里并沒有像生產(chǎn)環(huán)境下的webpack配置 (下面會(huì)介紹) 一樣給文件加上hash值是整,因?yàn)橛?jì)算這個(gè)hash會(huì)耗費(fèi)時(shí)間俯树,開發(fā)環(huán)境講究構(gòu)建速度要快,所以不需要這個(gè)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
會(huì)在命令執(zhí)行時(shí)自動(dòng)打開頁面许饿,contentBase: '/'
資源文件放置處,這里講一下 proxy:
因?yàn)槲业捻?xiàng)目有自己的后臺(tái)舵盈,用nodejs寫的陋率,運(yùn)行在3000端口,但是webpack-dev-server也有一個(gè)自己的運(yùn)行后臺(tái)秽晚,運(yùn)行在 port: 8080
端口瓦糟,那該怎么辦?
這時(shí)我們就需要進(jìn)行代理proxy:
host
默認(rèn)是 localhost
赴蝇,對8080端口下的服務(wù)器進(jìn)行請求菩浙,會(huì)自動(dòng)把相同的請求轉(zhuǎn)到3000下,比如我們請求 localhost:8080/api
句伶,代理以后會(huì)自動(dòng)請求 localhost:3000/api
劲蜻,我這里是把所以請求都轉(zhuǎn)到端口3000下,也可以只進(jìn)行 /api
代理:
'/api': {
target: 'http://localhost:3000/api',
secure: false,
changeOrigin: true,
},
4
關(guān)于 plugins:
plugins: [
// 除了自己寫的代碼考余,將其他的modules都bundle成一個(gè)vendor文件
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks(module) {
// 該配置假定你引入的 vendor 存在于 node_modules 目錄中
return module.context && module.context.indexOf('node_modules') !== -1;
},
}),
// 生產(chǎn)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
],
說到生成環(huán)境的webpack配置,可以參考我的項(xiàng)目的 webpack.prod.js
文件楚堤。
生產(chǎn)環(huán)境下我們就要兼顧很多方面疫蔓,比如服務(wù)端發(fā)送到前端的文件要小,保證加載速度快身冬;關(guān)于modules方面的代碼我們很少會(huì)去修改衅胀,所以可以在前端進(jìn)行緩存起來。
1
output: {
path: path.resolve(__dirname, 'client', 'dist'),
filename: '[name].[chunkhash].js',
publicPath: '/',
},
這里對文件名進(jìn)行了hash處理酥筝,瀏覽器會(huì)自動(dòng)緩存js文件滚躯,如果收到的文件的文件名是一樣的,瀏覽器可能不會(huì)進(jìn)行自動(dòng)更新樱哼,所以我們加上hash值哀九,保證每一次更新以后的文件名不同,從而讓瀏覽器再次請求資源搅幅。
而那些modules代碼基本是不會(huì)變的阅束,所以保證它們的hash值不變,在瀏覽器處緩存起來茄唐。
2
plugins
plugins: [
// 將node_modules里的依賴整合成一個(gè)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',
}),
// 根據(jù)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(),
// 允許創(chuàng)建一個(gè)在編譯時(shí)可以配置的全局常量
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"',
},
}),
// 壓縮
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
},
// sourceMap: true,
}),
new WebpackMd5Hash(),
new webpack.NoEmitOnErrorsPlugin(),
],
除了將文件進(jìn)行 uglify 以外息裸,這里還將文件進(jìn)行g(shù)z壓縮 new CompressionPlugin()
蝇更。
在后端部分還要加上一些代碼:
// 獲取js文件時(shí)選擇gz壓縮文件
app.get('*.js', (req, res, next) => {
req.url = req.url + '.gz';
res.set('Content-Encoding', 'gzip');
next();
});
以上。