這裏記錄了本身在開發一個 React 項目時使用 Webpack 優化項目的過程,歡迎你們圍觀點贊或吐槽。javascript
學習 React 時候,寫了個我的博客站點。使用 webpack 做爲打包工具,在這以前學習 webpack 時候,知道 webpack 有插件能夠作資源壓縮、抽離,以達到減少資源的體積,便於緩存資源的目的,可是開始這個項目時候並無想當即使用 webpack 的插件帶來的便利,主要是想先寫完再來優化,也便於優化先後有個對比,便於深刻的瞭解插件的做用。css
寫完後項目打包後的main.js
文件體積是 3.38 MiB,我部署使用的騰訊雲 1 M 帶寬的服務器,訪問速度很慢。前端
看看此時 webpack.config.js 的配置:java
const path = require('path');
var webpack = require('webpack');
const config = {
entry: ['babel-polyfill','./src/app.js'],
output: {
path: path.resolve(__dirname, 'build'),
filename: 'main.js', // 打包輸出,單文件輸出
publicPath: '/',
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
include: /(src)/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader'],
},
{
test: /\.scss$/,
use: [{
loader: 'style-loader',
}, {
loader: 'css-loader',
}, {
loader: 'sass-loader',
},],
},
],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
],
devServer: {
contentBase: path.join(__dirname, "build"),
compress: true,
port: 9000,
open: true,
inline: true,
},
};
module.exports = config;
複製代碼
npm run build
結果:node
Asset Size Chunks Chunk Names
main.js 3.38 MiB main [emitted] main
複製代碼
3 M 的單文件實在是太大了。適當減小請求次數,減小單次請求的文件大小,這是作前端優化的重要手段。如何縮小這個單頁面應用體積,或者適當拆分資源(利用瀏覽器能夠同時下載多個資源的特性)來優化訪問速度。react
去掉註釋,減小空格能夠減小無用字符佔用的文件體積。webpack 插件 UglifyjsWebpackPlugin
官方對插件的介紹是用來縮小你 javascript 文件,對於我這個博客而言就是 main.js 文件。webpack
webpack.config.js 配置以下:nginx
const path = require('path');
var webpack = require('webpack');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const config = {
entry: ['babel-polyfill','./src/app.js'],
output: {
path: path.resolve(__dirname, 'build'),
filename: 'main.js',
publicPath: '/',
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
include: /(src)/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader'],
},
{
test: /\.scss$/,
use: [{
loader: 'style-loader',
}, {
loader: 'css-loader',
}, {
loader: 'sass-loader',
},],
},
],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new UglifyJsPlugin({
uglifyOptions: {
ie8: false,
mangle: true,
output: { comments: false },
compress: {
warnings: false,
drop_console: true,
drop_debugger: true,
unused: false,
},
},
sourceMap: true,
cache: true,
}),
],
devServer: {
contentBase: path.join(__dirname, "build"),
compress: true,
port: 9000,
open: true,
inline: true,
},
};
module.exports = config;
複製代碼
npm run build
輸出:git
Asset Size Chunks Chunk Names
main.js 3.1 MiB main [emitted] main
複製代碼
可見資源減小了 0.28 MIB。
上面的 UglifyjsWebpackPlugin
插件帶來的壓縮效果可能並不能知足咱們的要求。咱們熟悉有一種打包壓縮方式,將文件壓縮爲 zip 包,這種壓縮效果顯著,一般能夠將文件成倍壓縮,那麼這種壓縮方式可否在這裏使用呢,答案是能夠的。CompressionWebpackPlugin
插件就提供了這種功能,咱們來引入看看效果。
webpack.config.js
配置以下:
const path = require('path');
var webpack = require('webpack');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin");
const config = {
entry: ['babel-polyfill','./src/app.js'],
output: {
path: path.resolve(__dirname, 'build'),
filename: 'main.js',
publicPath: '/',
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
include: /(src)/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader'],
},
{
test: /\.scss$/,
use: [{
loader: 'style-loader',
}, {
loader: 'css-loader',
}, {
loader: 'sass-loader',
},],
},
],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new UglifyJsPlugin({
uglifyOptions: {
ie8: false,
mangle: true,
output: { comments: false },
compress: {
warnings: false,
drop_console: true,
drop_debugger: true,
unused: false,
},
},
sourceMap: true,
cache: true,
}),
new CompressionPlugin(),
],
devServer: {
contentBase: path.join(__dirname, "build"),
compress: true,
port: 9000,
open: true,
inline: true,
},
};
module.exports = config;
複製代碼
npm run build
結果:
....
Asset Size Chunks Chunk Names
main.js 3.1 MiB main [emitted] main
main.js.gz 544 KiB [emitted]
....
複製代碼
能夠看到,多出一個 main.js.gz 壓縮包,只有 554 KiB,很驚喜有沒有?只不過要使用這種壓縮文件,nginx 須要配置支持,nginx 的 nginx_http_gzip_static_module
模塊能夠支持請求壓縮包,nginx 配置以下:
...
server {
gzip on;
gzip_static on;
gzip_min_length 1000;
gzip_buffers 4 8k;
gzip_types text/plain application/xml text/css text/js text/xml application/x-javascript text/javascript application/json application/xml+rss image/jpeg image/png image/g
gzip_vary on;
listen 80;
location / {
...
}
}
...
複製代碼
這樣瀏覽器端下載的資源就由原來的 3.38M 降到了 554K。
已經將文件壓縮成 gzip 文件,從減小文件體積方面好像已經機關用盡。但回頭看看 main.js 文件,不難發現他是個即有 js 又有 css 的大雜燴,要是能把 css 抽離出來,是否是能夠進一步減小單文件體積,雖然會多出一個 css 文件,多了次請求,但正好利用了瀏覽器的併發下載,從緩存資源角度來說也是有利的。webpack 插件 extract-text-webpack-plugin
能夠用來提取 css。可是須要注意的是 extract-text-webpack-plugin
只能用在 webpack 4 如下,webpack4 及以上版本須要使用 mini-css-extract-plugin
webpack.config.js
配置以下:
const path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const config = {
entry: ['babel-polyfill','./src/app.js'],
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].[hash:8].js',
publicPath: '/',
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
include: /(src)/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: 'index.html',
}),
new webpack.HotModuleReplacementPlugin(),
new UglifyJsPlugin({
uglifyOptions: {
ie8: false,
mangle: true,
output: { comments: false, },
compress: {
warnings: false,
drop_console: true,
drop_debugger: true,
unused: false,
},
},
sourceMap: true,
cache: true,
}),
new CompressionPlugin(),
new MiniCssExtractPlugin({
filename: "[name].[hash:8].css",
chunkFilename: "[id].[hash:8].css",
}),
],
devServer: {
contentBase: path.join(__dirname, "build"),
compress: true,
port: 9000,
open: true,
inline: true,
},
};
module.exports = config;
複製代碼
npm run build:
複製代碼
Built at: 2018-06-21 08:03:57
Asset Size Chunks Chunk Names
main.b2c90941.css 317 KiB main [emitted] main
main.b2c90941.js 2.69 MiB main [emitted] main
index.html 263 bytes [emitted]
index.html.gz 196 bytes [emitted]
main.b2c90941.css.gz 33.4 KiB [emitted]
main.b2c90941.js.gz 501 KiB [emitted]
複製代碼
可見 css 被取了出來,這裏的提取效果沒有想一想中的理想,js 的體積縮小的並很少,本來 css 也很少。
上一步抽離了 css,在打包的使用啓用了 chunk hash,這樣當 js 或者 css 文件有改動後運行 npm run build
每次生成的 js 和 css 的打包文件名是不一樣的,這樣就有兩個問題須要解決: 一、每次 build 後須要在 index.html 頁面修改 css 和 js 文件的名稱,二、屢次修改後 build,會產生須要沒用的 js、css 文件。針對這兩個問題,大牛早已經給出瞭解決方案。
html-webpack-plugin
插件可讓咱們指定生成 index.html 使用的模版文件,build 後會自動生成 index.html 文件,並將 css 和 js 文件自動引入到 html 文件。
clean-webpack-plugin
插件則能夠幫咱們在 build 的開始階段,自動刪除指定的目錄或者文件。
webpack.config.js
配置以下:
const path = require('path');
var webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const config = {
entry: ['babel-polyfill','./src/app.js'],
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].[hash:8].js',
publicPath: '/',
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
include: /(src)/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
},
],
},
plugins: [
// build 開始階段須要刪除的文件
new CleanWebpackPlugin(['build/*'], {
watch: true,
}),
// 指定生成 html 文件使用的模版文件
new HtmlWebpackPlugin({
template: 'index.html',
}),
new webpack.HotModuleReplacementPlugin(),
new UglifyJsPlugin({
uglifyOptions: {
ie8: false,
mangle: true,
output: { comments: false },
compress: {
warnings: false,
drop_console: true,
drop_debugger: true,
unused: false,
},
},
sourceMap: true,
cache: true,
}),
new CompressionPlugin(),
new MiniCssExtractPlugin({
filename: "[name].[hash:8].css",
chunkFilename: "[id].[hash:8].css",
}),
],
devServer: {
contentBase: path.join(__dirname, "build"),
compress: true,
port: 9000,
open: true,
inline: true,
},
};
module.exports = config;
複製代碼
npm run build
:
...
// 刪除指定的文件
clean-webpack-plugin: /Users/wewin/reactLearn/redux-blog/build/* has been removed.
...
Asset Size Chunks Chunk Names
main.ad06a35d.css 317 KiB main [emitted] main
main.ad06a35d.js 2.69 MiB main [emitted] main
index.html 265 bytes [emitted]
index.html.gz 196 bytes [emitted]
main.ad06a35d.css.gz 33.4 KiB [emitted]
main.ad06a35d.js.gz 501 KiB [emitted]
...
複製代碼
自動生成的 index.html 文件:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>redux blog</title>
<link href="/main.ad06a35d.css" rel="stylesheet">
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="/main.ad06a35d.js"></script>
</body>
</html>
複製代碼
js 和 css 文件被自動引入到了 html 頁面,上次 build 生成的文件,會自動被刪除。
上面對文件的壓縮,css 的提取都起到了減小 js 提交的做用,可是通過上面兩個步驟後,最後打包的 main.js 仍有 501 KiB,想要進一步減小文件體積,咱們就要清楚 main.js 文件裏到底有些什麼,是什麼致使了文件如此龐大。BundleAnalyzerPlugin
插件能夠幫咱們分析出文件的組成,能夠以文件的或者網頁的形式展現給咱們。配置和使用這裏不作具體的說明。
將公共的 JavaScript 模塊抽離,避免重複的引入,能夠有效的減小 js 文件體積。webpack 4 可使用 SplitChunksPlugin
插件來提取共同的 js,在 webpack 4 如下版本可使用 CommonsChunkPlugin
插件。
webpackge.config.js
const path = require('path');
var webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const config = {
entry: ['babel-polyfill', './src/app.js'],
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].[hash:8].js',
publicPath: '/',
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
},
],
},
plugins: [
new CleanWebpackPlugin(['dist', 'build/*'], {
watch: true,
}),
new HtmlWebpackPlugin({
template: 'index.html',
}),
new webpack.HotModuleReplacementPlugin(),
new UglifyJsPlugin({
uglifyOptions: {
ie8: false,
mangle: true,
output: { comments: false },
compress: {
warnings: false,
drop_console: true,
drop_debugger: true,
unused: false,
}
},
sourceMap: true,
cache: true,
}),
new CompressionPlugin(),
new MiniCssExtractPlugin({
filename: "[name].[hash:8].css",
chunkFilename: "[id].[hash:8].css",
}),
],
optimization: {
splitChunks: {
chunks: 'initial',
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 2,
maxInitialRequests: 2,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /\/node_modules\//,
priority: -10,
},
'react-vendor': {
test: (module, chunks) => /react/.test(module.context),
priority: 1,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
}
}
}
},
devServer: {
contentBase: path.join(__dirname, "build"),
compress: true,
port: 9000,
open: true,
inline: true,
},
};
module.exports = config;
複製代碼
npm run build
:
Asset Size Chunks Chunk Names
main.3413403b.js 8.33 KiB main [emitted] main
react-vendor~main.3413403b.css 318 KiB react-vendor~main [emitted] react-vendor~main
react-vendor~main.3413403b.js 2.68 MiB react-vendor~main [emitted] react-vendor~main
index.html 355 bytes [emitted]
main.3413403b.js.gz 3.2 KiB [emitted]
index.html.gz 214 bytes [emitted]
react-vendor~main.3413403b.css.gz 33.5 KiB [emitted]
react-vendor~main.3413403b.js.gz 498 KiB [emitted]
複製代碼
這裏對 splitChunks 的配置基本上使用的基本都是默認配置,splitChunks 的使用能夠參考官網。提取 js 不但能夠縮小文件體積,對 React React-dom 這種基礎依賴的提取更有利於緩存。
這裏主要是記錄下本身在減小打包後的文件體積使用到的 webpack 的幾個插件,但願對有相同需求的朋友有所幫助。
歡迎指正!