react+webpack4搭建前端項目(三)打包優化

本編文章接着前兩篇文章進行項目webpack打包的優化css

一、react+webpack4搭建前端項目(一)html

二、react+webpack4搭建前端項目(二)react全家桶的使用前端

主要從如下幾個方面進行:vue

  • react路由的異步加載
  • css處理
    • 使用mini-css-extract-plugincssbundle包中抽取
    • 使用optimize-css-assets-webpack-plugin壓縮css代碼
    • 使用postcss-loader,autoprefixer對瀏覽器兼容性的css代碼加前綴
  • js的處理
    • 使用uglifyjs-webpack-plugin代碼壓縮
    • 拆包,jsbundle包的提取(拆包)

前言

注意antd版本"antd": "^3.8.3",,高版本的antd官方把圖標庫也構建到release包,因此致使打包變得很大,僅僅icon圖標庫就有幾百KB,請看下圖。若是遇到這個問題,請下降antd的使用版本到3.8.3之前 node

QQ截圖20191010161431.png

下邊打包優化的基礎代碼請點擊 源碼1.0.3。有不熟悉的同窗能夠看一下,下載該版本1.0.3,在項目根目錄執行 npm run dev;同時切換到mock目錄,執行 npm run dev,打開http://localhost:8081便可看到效果 主要實現的功能以下圖:react

簡歷管理的查詢,刪除,修改:webpack

1567163086(1).jpg

用戶模塊的查詢,修改: ios

QQ截圖20190830190459.png

用戶模塊的添加:git

QQ截圖20190830190510.png

首先咱們看一下沒有優化前的js包大小,執行npm run buildgithub

QQ截圖20190830160102.png

這時候打包出的文件只有三個 index.html模板文件 reset.min.css是從靜態目錄copy進去的 app.1a9adec2b6012290869f.js是咱們利用webpack打包生成的。這裏邊包括項目中的全部js代碼,css代碼以及圖片data資源

工欲善其事必先利其器,咱們先安裝兩個很是有用的webpack插件

npm install -D clean-webpack-plugin webpack-bundle-analyzer
複製代碼
  • clean-webpack-plugin 在打包的時候會刪除以前的打包目錄
  • webpack-bundle-analyzer 在打包結束的時候,會啓動啓動一個服務在瀏覽器查看打包的大小和包含的內容等

修改webpack.prod.config.js,在plugins屬性下添加

new CleanWebpackPlugin(),
new BundleAnalyzerPlugin(),
複製代碼

開始打包優化

路由的異步加載

咱們知道想文件的異步加載須要使用import("xxx"),或者require.ensure這種方法適用webapck1.x 2.x。因此這裏採用import("xxx")

在vue中實現路由的異步加載很簡單,經過()=>import("xxx")就能夠,那麼在react中咱們也能夠這樣異步加載

咱們這裏實現路由的異步加載藉助react-loadable插件

詳細使用請點擊 react-loadable使用方法

一、首頁編寫一個loadable.js實現異步加載組件

import Loadable from 'react-loadable';

const LoadableComponent = (component) => Loadable({
  loader: component,
  loading: ()=>null,
});

export default LoadableComponent;
複製代碼

二、修改路由組件的加載方式

container/index.js文件組件的直接導入

import BlogIndex from "@/blog"
import ResumeIndex from "@/resume"
import UserIndex from "@/user"
複製代碼

改爲使用react-loadable插件包裝一層加載組件的方式

import LoadableComponent from "@/loadable"
const BlogListPage = LoadableComponent(()=>import("./pages/list"))
const AddBlogPage = LoadableComponent(()=>import("./pages/add"))
複製代碼

接着修改user/index.jsblog/index.js,把路由組件改爲異步加載,改完以後測試一下打包以下圖

QQ截圖20190830191951.png

這時候已經把異步加載的路由組件單獨打包到其它單獨的文件。從922k減少到487k。

你會發現1.a64085be1c517b7e1ef2.js單獨打包出來的200多k,能夠看到下圖包含了antd的組件,這也證實antd按需加載的使用成功

QQ截圖20190830193807.png

css處理

把css從bundle包中的抽取

在webpack4.x以前咱們使用extract-text-webpack-plugin壓縮抽取css。

在webpack4.x咱們須要使用mini-css-extract-plugin插件進行抽取css,mini-css-extract-plugin詳細使用文檔 修改webpack.base.config.jscssless文件的處理,以下

{
    test: /\.css$/,
    use:[
        {
            loader:MiniCssExtractPlugin.loader,
            options:{
                hmr: utils.isDev(), // 開發的時候,修改css熱更新,可是試了下不起做用
                reloadAll:true,
            }
        },
        // {
        //     loader: 'style-loader', // 建立 <style></style>  // MiniCssExtractPlugin 有衝突,因此刪掉
        // },
        { 
            loader: 'css-loader',  // 轉換css
            options: { importLoaders: 1 } 
        }
    ]
},
{
    test: /\.less$/,
    use: [
        {
            loader:MiniCssExtractPlugin.loader,
            options:{
                hmr: utils.isDev(), // 開發的時候,修改less熱更新可是試了下不起做用
                reloadAll:true,
            }
        },
        // {
        //     loader: 'style-loader', 
        // },
        {
            loader: 'css-loader',
        },
        {
            loader: 'less-loader', // 編譯 Less -> CSS
        }
    ],
},
複製代碼

由於style-loaderMiniCssExtractPlugin.loader有衝突,在配置的時刪除了style-loader對樣式的處理,測試打包結果以下

QQ截圖20190830202044.png

此時已經成功把css樣式從bundle抽離出。bundle包從487k減少到381k。

壓縮css代碼

咱們打開任意一個打包後的css文件,發現css代碼沒有壓縮。因此咱們須要對css壓縮。安裝optimize-css-assets-webpack-pluginoptimize-css-assets-webpack-plugin詳細使用文檔

npm install -D optimize-css-assets-webpack-plugin
複製代碼

在webpack.prod.config.js添加optimization`屬性(webpack4.x的代碼壓縮和拆包都在這裏處理,這是和webpack3.x的不一樣)

optimization: {
    // 壓縮css
    minimizer: [
        new OptimizeCSSAssetsPlugin({
            cssProcessorOptions: { 
                discardComments: { removeAll: true } // 移除註釋
            } 
        })
    ]
}
複製代碼

測試打包:

QQ截圖20190902093438.png

對比一下兩次打包的css文件大小,已經有必定的減少或者打開打包後的css文件代碼也壓縮了,這就表明壓縮成功。

可是咱們發現js的bundle包變大了,這是爲何呢? 由於咱們重寫了optimization屬性的minimizer,會把webpack自帶的壓縮方式給覆蓋掉,這裏須要咱們本身定義js的壓縮方式。

js代碼壓縮

這裏和webpack3.x同樣使用uglifyjs-webpack-plugin插件,uglifyjs-webpack-plugin詳細使用文檔,安裝

npm install -D uglifyjs-webpack-plugin
複製代碼

而後在minimizer屬性下添加下邊代碼

// 自定義js優化配置,將會覆蓋默認配置
new UglifyJsPlugin({
    parallel: true,  //使用多進程並行運行來提升構建速度
    sourceMap: false,
    uglifyOptions: {
        warnings: false,
        compress: {
            unused: true,
            drop_debugger: true,
            drop_console: true, 
        },
        output: {
            comments: false // 去掉註釋
        }
    }
})
複製代碼

從新測試打包比較和以前的js文件的大小同樣!到此咱們對css的處理告一段落!

js處理

代碼壓縮上邊已經講過啦,這裏不在贅述
拆包

在webpack3.x的時候咱們都是用webpack內置的CommonsChunkPlugin來拆包。webpack4.x發生了很大變化。 webpack4.x要想進行拆包,須要先對splitChunks有必定的瞭解。splitChunks就算你什麼配置都不作它也是生效的,源於webpack有一個默認配置,這也符合webpack4的開箱即用的特性。

splitChunks的默認配置以下

splitChunks: {
    // async表示只從異步加載得模塊(動態加載import())裏面進行拆分
    // initial表示只從入口模塊進行拆分
    // all表示以上二者都包括
    chunks: "async",
    minSize: 30000,   // 大於30k會被webpack進行拆包
    minChunks: 1,     // 被引用次數大於等於這個次數進行拆分
    // import()文件自己算一個
    // 只計算js,不算css
    // 若是同時有兩個模塊知足cacheGroup的規則要進行拆分,可是maxInitialRequests的值只能容許再拆分一個模塊,那尺寸更大的模塊會被拆分出來
    maxAsyncRequests: 5,  // 最大的按需加載(異步)請求次數
    // 最大的初始化加載請求次數,爲了對請求數作限制,不至於拆分出來過多模塊
    // 入口文件算一個
    // 若是這個模塊有異步加載的不算
    // 只算js,不算css
    // 經過runtimeChunk拆分出來的runtime不算在內
    // 若是同時又兩個模塊知足cacheGroup的規則要進行拆分,可是maxInitialRequests的值只能容許再拆分一個模塊,那尺寸更大的模塊會被拆分出來
    maxInitialRequests: 3,
    automaticNameDelimiter: '~', // 打包分隔符
    name:true,
    cacheGroups: {
        // 默認的配置
        vendors: {
            test: /[\\/]node_modules[\\/]/,
            priority: -10
        },
        // 默認的配置,vendors規則不命中的話,就會命中這裏
        default: {
            minChunks: 2, // 引用超過兩次的模塊 -> default
            priority: -20,
            reuseExistingChunk: true
        },
    },
}
複製代碼

打包測試,和不添加splitChunks打包結果一致

QQ截圖20190830202044.png

由於默認是async,只從異步加載的模塊拆分。能夠看到只有app.js是入口文件(同步加載)沒有對app.js進行拆分。這個項目使用react-loadable異步加載,本項目中有7個組件採用這種方式加載。可是從打包結果能夠看異步加載的組件拆分出來10個chunk。那麼是爲何呢?

使用webpack-bundle-analyzer分析能夠看出有三個chunk是異步組件引用antd中的組件進行拆分出來的chunk

假如咱們把chunks:async改爲chunks: "initial"進行打包測試:

打包結果以下圖

QQ截圖20191011111415.png

打包結果徹底不一樣,由於app.js是同步加載,app.js被拆分,此時發現app.js很是小。相反拆分出來的vendors~app.4fd9181b8f618e9fcac6.js比較大,這是由於這個chunk包含項目入口文件包含的全部第三方庫。而異步加載的7個組件最終打包出來的仍是7個chunk,這些異步加載的組件打包出來的每一個chunk包含除了vendors~app.4fd9181b8f618e9fcac6.js打包進去的第三方庫之外的代碼和組件自己代碼。

那麼同窗們會想了,把chunks:initial改爲chunks: "all"會是什麼結果呢?那麼咱們進行測試一下

打包結果以下圖

QQ截圖20191011112433.png

咱們知道all不只從同步組件拆分,還從異步加載中拆分。

從打包結果看,是對initialasync的合併。即把異步組件拆分,也把同步組件拆分。

那麼結論來了,由於本項目包含異步加載,須要對異步組件和同步組件同時拆分,因此此項目採用chunks: "all"進行bundle的拆分。若是項目中同步加載的組件chunk不大,能夠不對同步加載組件進行拆分,使用chunks:async。固然若是項目中異步加載的組件chunk不大,也能夠不對異步加載組件進行拆分,使用chunks:initial。固然也能夠混用,對於緩存組單獨設置

既使採用chunks: "all"的方式咱們發現。拆分出來venders~app.jschunk(node_modules的第三方庫)也比較大。隨着第三方插件使用的增多這個chunk會變的愈來愈大。因此咱們這裏對他進行拆分。也就是把node_modules中使用的插件也拆分紅不一樣的chunk

咱們拆包的策略是按照體積大小、共用率、更新頻率從新劃分咱們的包,使其儘量的利用瀏覽器緩存。

分析項目的插件,能夠按幾下分類插件

  • UI組件庫(antd)
  • 基礎插件(react,react-dom,react-router-dom,mobx,axios等等)

這些都是更新頻率很是低,公用率高,說起大,因此單獨抽取。只要這些包不更新,拆包的chunk文件名就不會變。就一直緩存在瀏覽器

接下來咱們根據這個分類拆包,增長cacheGroups茶包的規則

antdui: {
    priority: 2,  
    test: /[\\/]node_modules[\\/](antd)[\\/]/,  //(module) => (/antd/.test(module.context)),
},
// 拆分基礎插件
basic: {
    priority: 3, 
    test: /[\\/]node_modules[\\/](moment|react|react-dom|react-router|react-router-dom|mobx|mobx-react|axios)[\\/]/,
}
複製代碼

打包測試:

QQ截圖20191011140206.png

這裏已經成功拆分出來antdui~app.jsbasic~app.js。使用webpack-bundle-analyzer分析能夠精確的看到antd,moment|react|react-dom|react-router|react-router-dom|mobx|mobx-react|axios插件被拆分。

不知道同窗們有沒有發現問題? 一、打包的hash都是同樣的,並且每次打包的hash還都不同。 解決方法, output添加

chunkFilename: utils.assetsPath("js/[name].[chunkhash].js")
複製代碼

new MiniCssExtractPlugin(options)初始化參數

chunkFilename: utils.assetsPath('css/[id].[chunkhash].css'),
複製代碼

二、venders~app.js不見了 antdui~app.jsbasic~app.js只是拆分了antdui|moment|react|react-dom|react-router|react-router-dom|mobx|mobx-react|axios這些插件。還有其它的node_modules的插件會被拆分到venders~app.js。這裏須要知道maxInitialRequests這個屬性的做用了。

maxInitialRequests:最大的初始化加載請求次數,爲了對請求數作限制,不至於拆分出來過多模塊

  • 入口文件算一個
  • 若是這個模塊有異步加載的不算
  • 只算js,不算css
  • 經過runtimeChunk拆分出來的runtime不算在內
  • 若是同時又兩個模塊知足cacheGroup的規則要進行拆分,可是maxInitialRequests的值只能容許再拆分一個模塊,那尺寸更大的模塊會被拆分出來

由此發現,加載app.js的時候有三個文件會同步加載app.jsantdui~app.jsbasic~app.js

須要把maxInitialRequests的值修改爲更大,修改爲5

maxInitialRequests: 5,
複製代碼

打包測試以下圖:

QQ截圖20191011145144.png

查看結果venders~app.jsapp.js中拆分出來啦。

源碼以下:

打包優化的github源碼 1.0.4

github源碼

react+webpack4+react-router5+react-loadable+mobx系列文章

一、react+webpack4搭建前端項目(一)基礎項目搭建

二、react+webpack4搭建前端項目(二)react全家桶的使用

三、react+webpack4搭建前端項目(三)打包優化

相關文章
相關標籤/搜索