天天看點

說說如何借助webpack來優化前端性能?

作者:鹹魚養成記

一般項目在完成後,會通過webpack進行打包,利用webpack對前端項目性能優化是一個十分重要的環節

如何優化

通過webpack優化前端的手段有:

  • JS代碼壓縮
  • CSS代碼壓縮
  • Html檔案代碼壓縮
  • 檔案大小壓縮
  • 圖檔壓縮
  • Tree Shaking
  • 代碼分離
  • 内聯 chunk

JS代碼壓縮

terser是一個JavaScript的解釋、絞肉機、壓縮機的工具集,可以幫助我們壓縮、醜化我們的代碼,讓bundle更小

在production模式下,webpack 預設就是使用 TerserPlugin 來處理我們的代碼的。如果想要自定義配置它,配置方法如下:

const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
    ...
    optimization: {
        minimize: true,
        minimizer: [
            new TerserPlugin({
                parallel: true // 電腦cpu核數-1
            })
        ]
    }
}           

屬性介紹如下:

  • extractComments:預設值為true,表示會将注釋抽取到一個單獨的檔案中,開發階段,我們可設定為 false ,不保留注釋
  • parallel:使用多程序并發運作提高建構的速度,預設值是true,并發運作的預設數量: os.cpus().length - 1
  • terserOptions:設定我們的terser相關的配置:compress:設定壓縮相關的選項,mangle:設定醜化相關的選項,可以直接設定為truemangle:設定醜化相關的選項,可以直接設定為truetoplevel:底層變量是否進行轉換keep_classnames:保留類的名稱keep_fnames:保留函數的名稱

CSS代碼壓縮

CSS壓縮通常是去除無用的空格等,因為很難去修改選擇器、屬性的名稱、值等

CSS的壓縮我們可以使用另外一個插件:css-minimizer-webpack-plugin

npm install css-minimizer-webpack-plugin -D           

配置方法如下:

const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
    // ...
    optimization: {
        minimize: true,
        minimizer: [
            new CssMinimizerPlugin({
                parallel: true
            })
        ]
    }
}           

Html檔案代碼壓縮

使用HtmlWebpackPlugin插件來生成HTML的模闆時候,通過配置屬性minify進行html優化

module.exports = {
    ...
    plugin:[
        new HtmlwebpackPlugin({
            ...
            minify:{
                minifyCSS:false, // 是否壓縮css
                collapseWhitespace:false, // 是否折疊空格
                removeComments:true // 是否移除注釋
            }
        })
    ]
}           

設定了minify,實際會使用另一個插件html-minifier-terser

檔案大小壓縮

對檔案的大小進行壓縮,減少http傳輸過程中寬帶的損耗

npm install compression-webpack-plugin -D           
new ComepressionPlugin({
    test:/\.(css|js)$/,  // 哪些檔案需要壓縮
    threshold:500, // 設定檔案多大開始壓縮
    minRatio:0.7, // 至少壓縮的比例
    algorithm:"gzip", // 采用的壓縮算法
})           

圖檔壓縮

一般來說在打包之後,一些圖檔檔案的大小是遠遠要比 js 或者 css 檔案要來的大,是以圖檔壓縮較為重要

配置方法如下:

module: {
  rules: [
    {
      test: /\.(png|jpg|gif)$/,
      use: [
        {
          loader: 'file-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/',
          }
        },
        {
          loader: 'image-webpack-loader',
          options: {
            // 壓縮 jpeg 的配置
            mozjpeg: {
              progressive: true,
              quality: 65
            },
            // 使用 imagemin**-optipng 壓縮 png,enable: false 為關閉
            optipng: {
              enabled: false,
            },
            // 使用 imagemin-pngquant 壓縮 png
            pngquant: {
              quality: '65-90',
              speed: 4
            },
            // 壓縮 gif 的配置
            gifsicle: {
              interlaced: false,
            },
            // 開啟 webp,會把 jpg 和 png 圖檔壓縮為 webp 格式
            webp: {
              quality: 75
            }
          }
        }
      ]
    },
  ]
}            

Tree Shaking

Tree Shaking 是一個術語,在計算機中表示消除死代碼,依賴于ES Module的靜态文法分析(不執行任何的代碼,可以明确知道子產品的依賴關系)

在webpack實作Trss shaking有兩種不同的方案:

  • usedExports:通過标記某些函數是否被使用,之後通過Terser來進行優化的
  • sideEffects:跳過整個子產品/檔案,直接檢視該檔案是否有副作用

兩種不同的配置方案, 有不同的效果

usedExports

配置方法也很簡單,隻需要将usedExports設為true

module.exports = {
    ...
    optimization:{
        usedExports
    }
}           

使用之後,沒被用上的代碼在webpack打包中會加入unused harmony export mul注釋,用來告知 Terser 在優化時,可以删除掉這段代碼

sideEffects

sideEffects用于告知webpack compiler哪些子產品時有副作用,配置方法是在package.json中設定sideEffects屬性

如果sideEffects設定為false,就是告知webpack可以安全的删除未用到的exports

如果有些檔案需要保留,可以設定為數組的形式

"sideEffecis":[    "./src/util/format.js",    "*.css" // 所有的css檔案]           

上述都是關于javascript的tree shaking,css同樣也能夠實作tree shaking

css tree shaking

css進行tree shaking優化可以安裝PurgeCss插件

npm install purgecss-plugin-webpack -D           
const PurgeCssPlugin = require('purgecss-webpack-plugin')module.exports = {    ...    plugins:[        new PurgeCssPlugin({            path:glob.sync(`${path.resolve('./src')}/**/*`), {nodir:true}// src裡面的所有檔案            satelist:function(){                return {                    standard:["html"]                }            }        })    ]}           
  • paths:表示要檢測哪些目錄下的内容需要被分析,配合使用glob
  • 預設情況下,Purgecss會将我們的html标簽的樣式移除掉,如果我們希望保留,可以添加一個safelist的屬性

代碼分離

将代碼分離到不同的bundle中,之後我們可以按需加載,或者并行加載這些檔案

預設情況下,所有的JavaScript代碼(業務代碼、第三方依賴、暫時沒有用到的子產品)在首頁全部都加載,就會影響首頁的加載速度

代碼分離可以分出出更小的bundle,以及控制資源加載優先級,提供代碼的加載性能

這裡通過splitChunksPlugin來實作,該插件webpack已經預設安裝和內建,隻需要配置即可

預設配置中,chunks僅僅針對于異步(async)請求,我們可以設定為initial或者all

module.exports = {    ...    optimization:{        splitChunks:{            chunks:"all"        }    }}           

splitChunks主要屬性有如下:

  • Chunks,對同步代碼還是異步代碼進行處理
  • minSize: 拆分包的大小, 至少為minSize,如何包的大小不超過minSize,這個包不會拆分
  • maxSize: 将大于maxSize的包,拆分為不小于minSize的包
  • minChunks:被引入的次數,預設是1

内聯chunk

可以通過InlineChunkHtmlPlugin插件将一些chunk的子產品内聯到html,如runtime的代碼(對子產品進行解析、加載、子產品資訊相關的代碼),代碼量并不大,但是必須加載的

const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin')const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {    ...    plugin:[        new InlineChunkHtmlPlugin(HtmlWebpackPlugin,[/runtime.+\.js/]}