記一次React專案的優化(webpack4外掛的使用)
這裡記錄了自己在開發一個 React 專案時使用 Webpack 優化專案的過程,歡迎大家圍觀點贊或吐槽。
學習 React 時候,寫了個個人部落格站點。使用 webpack 作為打包工具,在這之前學習 webpack 時候,知道 webpack 有外掛可以做資源壓縮、抽離,以達到減小資源的體積,便於快取資源的目的,但是開始這個專案時候並沒有想立即使用 webpack 的外掛帶來的便利,主要是想先寫完再來優化,也便於優化前後有個對比,便於深入的瞭解外掛的作用。
寫完後專案打包後的main.js
檔案體積是 3.38 MiB,我部署使用的騰訊雲 1 M 頻寬的伺服器,訪問速度很慢。
看看此時 webpack.config.js 的配置:
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
結果:
Asset Size Chunks Chunk Names
main.js 3.38 MiB main [emitted] main
3 M 的單檔案實在是太大了。適當減少請求次數,減少單次請求的檔案大小,這是做前端優化的重要手段。如何縮小這個單頁面應用體積,或者適當拆分資源(利用瀏覽器可以同時下載多個資源的特性)來優化訪問速度。
js壓縮
去掉註釋,減少空格可以減少無用字元佔用的檔案體積。webpack 外掛 UglifyjsWebpackPlugin
官方對外掛的介紹是用來縮小你 javascript 檔案,對於我這個部落格而言就是 main.js 檔案。
webpack.config.js 配置如下:
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
輸出:
Asset Size Chunks Chunk Names
main.js 3.1 MiB main [emitted] main
可見資源減少了 0.28 MIB。
gzip 壓縮
上面的 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]
....
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。
main.js 是個大雜燴 — css 提取
已經將檔案壓縮成 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 也不多。
html 檔案自動生成: 抽離後檔案自動在 html 頁面引入
上一步抽離了 css,在打包的使用啟用了 chunk hash,這樣當 js 或者 css 檔案有改動後執行 npm run build
每次生成的 js 和 css 的打包檔名是不同的,這樣就有兩個問題需要解決: 1、每次 build 後需要在 index.html 頁面修改 css 和 js 檔案的名稱,2、多次修改後 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 生成的檔案,會自動被刪除。
被打包的檔案裡到底有些什麼 — BundleAnalyzerPlugin
上面對檔案的壓縮,css 的提取都起到了減少 js 提交的作用,但是經過上面兩個步驟後,最後打包的 main.js 仍有 501 KiB,想要進一步減少檔案體積,我們就要清楚 main.js 檔案裡到底有些什麼,是什麼導致了檔案如此龐大。BundleAnalyzerPlugin
外掛可以幫我們分析出檔案的組成,可以以檔案的或者網頁的形式展示給我們。配置和使用這裡不做具體的說明。
公共 JavaScript 模組抽離
將公共的 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 的幾個外掛,希望對有相同需求的朋友有所幫助。
歡迎指正!
原文釋出時間為:2018年06月25日
原文作者:其言
本文來源: 掘金 如需轉載請聯絡原作者
相關文章
- 記一次 React 專案的優化(webpack4 外掛的使用)React優化Web
- 記一次Node專案的優化優化
- React 優秀外掛記錄React
- 一次vue-cli 2.x專案打包優化經歷(優化xlsx外掛)Vue優化
- 記一次 Webpack 專案優化Web優化
- 記一次大型React專案的國際化方案探索React
- React中型專案的優化實踐React優化
- 記一次 VUE 專案優化實踐Vue優化
- 基於webpack4搭建的react專案框架WebReact框架
- webpack4入門筆記——外掛Web筆記
- 優雅的在React專案中使用ReduxReactRedux
- 在React專案中,如何優雅的優化長列表React優化
- 記一次使用idea外掛CamelCase技巧Idea
- 用實驗的思路優化webpack4專案編譯速度優化Web編譯
- fastclick外掛的使用--移動端vue專案開發(vue常用外掛)ASTVue
- 記一次ionic使用file外掛cordova plugin file的坑Plugin
- webpack 優化react專案沒有解決的問題Web優化React
- 記一次vue+element+echarts專案的優化(如何輕鬆將專案效能提升70%)VueEcharts優化
- 記一次前端技術選型和專案優化前端優化
- React 折騰記 - (5) 記錄用React開發專案過程遇到的問題(Webpack4/React16/antd等)ReactWeb
- Flutter日曆專案的優化記錄Flutter優化
- 在react專案中使用shouldComponentUpdate方法進行元件效能優化React元件優化
- 使用webpack4一步步搭建react專案(三)WebReact
- 使用webpack4一步步搭建react專案(二)WebReact
- 使用webpack4一步步搭建react專案(一)WebReact
- 使用Webpack4優化Web效能Web優化
- 記一次iOS自動化打包走過的坑-關於React Native-iOS專案iOSReact Native
- 使用 Webapck 優化 VS Code 外掛載入效能Web優化
- 外掛化之VirtualApk實戰一:專案配置APK
- 升級vue專案中的外掛版本Vue
- 記一次ASP.NET MVC效能優化(實際專案中)ASP.NETMVC優化
- Flutter進階 | Flutter 優質練手專案以及優質外掛Flutter
- 記一次離線下載nessus 外掛的方法
- Egg 學習筆記 - 外掛的使用筆記
- React Native - react-native-code-push-熱更新外掛的使用[譯文]React Native
- 使用webpack.require優化vue專案的路由WebUI優化Vue路由
- 如何使用外掛化機制優雅的封裝你的請求hook封裝Hook
- 記一次使用策略模式優化程式碼的經歷模式優化