【Webpack】631- 總結18個webpack插件,總會有你想要的!

何爲插件(Plugin)?專注處理 webpack 在編譯過程中的某個特定的任務的功能模塊,可以稱爲插件。

Plugin 是一個擴展器,它豐富了 webpack 本身,針對是 loader 結束後,webpack 打包的整個過程,它並不直接操作文件,而是基於事件機制工作,會監聽 webpack 打包過程中的某些節點,執行廣泛的任務。

Plugin 的特點

  • 是一個獨立的模塊

  • 模塊對外暴露一個 js 函數

  • 函數的原型 (prototype) 上定義了一個注入 compiler 對象的 apply方法 apply 函數中需要有通過 compiler 對象掛載的 webpack 事件鉤子,鉤子的回調中能拿到當前編譯的 compilation 對象,如果是異步編譯插件的話可以拿到回調 callback

  • 完成自定義子編譯流程並處理 complition 對象的內部數據

  • 如果異步編譯插件的話,數據處理完成後執行 callback 回調。

下面介紹 18 個常用的 webpack 插件。

本文在gitthub做了收錄:github.com/Michael-lzg…

HotModuleReplacementPlugin

模塊熱更新插件。Hot-Module-Replacement 的熱更新是依賴於 webpack-dev-server,後者是在打包文件改變時更新打包文件或者 reload 刷新整個頁面,HRM 是隻更新修改的部分。

HotModuleReplacementPluginwebpack模塊自帶的,所以引入webpack後,在plugins配置項中直接使用即可。

const webpack = require('webpack')

plugins: [
  new webpack.HotModuleReplacementPlugin(), // 熱更新插件
]

html-webpack-plugin

生成 html 文件。將 webpack 中entry配置的相關入口 chunkextract-text-webpack-plugin抽取的 css 樣式 插入到該插件提供的template或者templateContent配置項指定的內容基礎上生成一個 html 文件,具體插入方式是將樣式link插入到head元素中,script插入到head或者body中。

const HtmlWebpackPlugin = require('html-webpack-plugin')

plugins: [
  new HtmlWebpackPlugin({
    filename: 'index.html',
    template: path.join(__dirname, '/index.html'),
    minify: {
      // 壓縮HTML文件
      removeComments: true, // 移除HTML中的註釋
      collapseWhitespace: true, // 刪除空白符與換行符
      minifyCSS: true, // 壓縮內聯css
    },
    inject: true,
  }),
]

inject 有四個選項值

  • true:默認值,script 標籤位於 html 文件的 body 底部

  • body:script 標籤位於 html 文件的 body 底部(同 true)

  • head:script 標籤位於 head 標籤內

  • false:不插入生成的 js 文件,只是單純的生成一個 html 文件

多頁應用打包

有時,我們的應用不一定是一個單頁應用,而是一個多頁應用,那麼如何使用 webpack 進行打包呢。

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
  entry: {
    index: './src/index.js',
    login: './src/login.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[hash:6].js',
  },
  //...
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
      filename: 'index.html', //打包後的文件名
    }),
    new HtmlWebpackPlugin({
      template: './public/login.html',
      filename: 'login.html', //打包後的文件名
    }),
  ],
}

如果需要配置多個 HtmlWebpackPlugin,那麼 filename 字段不可缺省,否則默認生成的都是 index.html

但是有個問題,index.htmllogin.html 會發現,都同時引入了 index.f7d21a.jslogin.f7d21a.js,通常這不是我們想要的,我們希望 index.html 中只引入 index.f7d21a.jslogin.html 只引入 login.f7d21a.js

HtmlWebpackPlugin 提供了一個 chunks 的參數,可以接受一個數組,配置此參數僅會將數組中指定的 js 引入到 html 文件中

module.exports = {
  //...
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
      filename: 'index.html', //打包後的文件名
      chunks: ['index'],
    }),
    new HtmlWebpackPlugin({
      template: './public/login.html',
      filename: 'login.html', //打包後的文件名
      chunks: ['login'],
    }),
  ],
}

這樣執行 npm run build,可以看到 index.html 中僅引入了 index 的 js 文件,而 login.html 中也僅引入了 login 的 js 文件。

clean-webpack-plugin

clean-webpack-plugin 用於在打包前清理上一次項目生成的 bundle 文件,它會根據 output.path 自動清理文件夾;這個插件在生產環境用的頻率非常高,因爲生產環境經常會通過 hash 生成很多 bundle 文件,如果不進行清理的話每次都會生成新的,導致文件夾非常龐大。

const { CleanWebpackPlugin } = require('clean-webpack-plugin')

plugins: [
  new HtmlWebpackPlugin({
    template: path.join(__dirname, '/index.html'),
  }),
  new CleanWebpackPlugin(), // 所要清理的文件夾名稱
]

extract-text-webpack-plugin

將 css 成生文件,而非內聯 。該插件的主要是爲了抽離 css 樣式,防止將樣式打包在 js 中引起頁面樣式加載錯亂的現象

const ExtractTextPlugin = require('extract-text-webpack-plugin')

plugins: [
  // 將css分離到/dist文件夾下的css文件夾中的index.css
  new ExtractTextPlugin('css/index.css'),
]

mini-css-extract-plugin

將 CSS 提取爲獨立的文件的插件,對每個包含 css 的 js 文件都會創建一個 CSS 文件,支持按需加載 css 和 sourceMap。只能用在 webpack4 中,對比另一個插件 extract-text-webpack-plugin 有以下特點:

  • 異步加載

  • 不重複編譯,性能更好

  • 更容易使用

  • 只針對 CSS

這個插件應該只用在生產環境配置,並且在 loaders 鏈中不使用 style-loader, 而且這個插件暫時不支持 HMR

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  module: {
    rules: [
      {
        test: /\.(le|c)ss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: '../',
            },
          },
          'css-loader',
          'postcss-loader',
          'less-loader',
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/[name].[contenthash:8].css',
      chunkFilename: 'css/[id].[contenthash:8].css',
    }),
  ],
}

purifycss-webpack

有時候我們 css 寫得多了或者重複了,這就造成了多餘的代碼,我們希望在生產環境進行去除。

const path = require('path')
const PurifyCssWebpack = require('purifycss-webpack') // 引入PurifyCssWebpack插件
const glob = require('glob') // 引入glob模塊,用於掃描全部html文件中所引用的css

module.exports = merge(common, {
  plugins: [
    new PurifyCssWebpack({
      paths: glob.sync(path.join(__dirname, 'src/*.html')),
    }),
  ],
})

optimize-css-assets-webpack-plugin

我們希望減小 css 打包後的體積,可以用到 optimize-css-assets-webpack-plugin

const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin") // 壓縮css代碼

optimization: {
  minimizer: [
    // 壓縮css
    new OptimizeCSSAssetsPlugin({})
  ]
 }

UglifyJsPlugin

uglifyJsPluginvue-cli 默認使用的壓縮代碼方式,用來對 js 文件進行壓縮,從而減小 js 文件的大小,加速 load 速度。它使用的是單線程壓縮代碼,打包時間較慢,所以可以在開發環境將其關閉,生產環境部署時再把它打開。

const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

plugins: [
  new UglifyJsPlugin({
    uglifyOptions: {
      compress: {
        warnings: false
      }
    },
    sourceMap: true,  //是否啓用文件緩存
    parallel: true   //使用多進程並行運行來提高構建速度
  })

ParallelUglifyPlugin

開啓多個子進程,把對多個文件壓縮的工作分別給多個子進程去完成,每個子進程其實還是通過 UglifyJS 去壓縮代碼,但是變成了並行執行。

const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')

plugins: [
  new ParallelUglifyPlugin({
    //cacheDir 用於配置緩存存放的目錄路徑。
    cacheDir: '.cache/',
    sourceMap: true,
    uglifyJS: {
      output: {
        comments: false,
      },
      compress: {
        warnings: false,
      },
    },
  }),
]

terser-webpack-plugin

Webpack4.0 默認是使用 terser-webpack-plugin 這個壓縮插件,在此之前是使用 uglifyjs-webpack-plugin,兩者的區別是後者對 ES6 的壓縮不是很好,同時我們可以開啓 parallel 參數,使用多進程壓縮,加快壓縮。

const TerserPlugin = require('terser-webpack-plugin') // 壓縮js代碼

optimization: {
  minimizer: [
    new TerserPlugin({
      parallel: 4, // 開啓幾個進程來處理壓縮,默認是 os.cpus().length - 1
      cache: true, // 是否緩存
      sourceMap: false,
    }),
  ]
}

NoErrorsPlugin

報錯但不退出 webpack 進程。編譯出現錯誤時,使用 NoEmitOnErrorsPlugin 來跳過輸出階段。這樣可以確保輸出資源不會包含錯誤。

plugins: [new webpack.NoEmitOnErrorsPlugin()]

compression-webpack-plugin

所有現代瀏覽器都支持 gzip 壓縮,啓用 gzip 壓縮可大幅縮減傳輸資源大小,從而縮短資源下載時間,減少首次白屏時間,提升用戶體驗。

gzip 對基於文本格式文件的壓縮效果最好(如:CSS、JavaScript 和 HTML),在壓縮較大文件時往往可實現高達 70-90% 的壓縮率,對已經壓縮過的資源(如:圖片)進行 gzip 壓縮處理,效果很不好。

const CompressionPlugin = require('compression-webpack-plugin')

plugins: [
  new CompressionPlugin({
    // gzip壓縮配置
    test: /\.js$|\.html$|\.css/, // 匹配文件名
    threshold: 10240, // 對超過10kb的數據進行壓縮
    deleteOriginalAssets: false, // 是否刪除原文件
  }),
]

當然,這個方法還需要後端配置支持。

DefinePlugin

我們可以通過 DefinePlugin 可以定義一些全局的變量,我們可以在模塊當中直接使用這些變量,無需作任何聲明,DefinePluginwebpack 自帶的插件。

plugins: [
  new webpack.DefinePlugin({
    DESCRIPTION: 'This Is The Test Text.',
  }),
]

// 直接引用
console.log(DESCRIPTION)

ProvidePlugin

自動加載模塊。任何時候,當 identifier 被當作未賦值的變量時, module 就會自動被加載,並且 identifier 會被這個 module 輸出的內容所賦值。這是 webpack 自帶的插件。

module.exports = {
  resolve: {
    alias: {
      jquery: './lib/jquery',
    },
  },
  plugins: [
    //提供全局的變量,在模塊中使用無需用require引入
    new webpack.ProvidePlugin({
      $: 'jquery',
      React: 'react',
    }),
  ],
}

DLLPlugin

這是在一個額外的獨立的 webpack 設置中創建一個只有 dll 的 bundle(dll-only-bundle)。這個插件會生成一個名爲 manifest.json 的文件,這個文件是用來讓 DLLReferencePlugin 映射到相關的依賴上去的。

「使用步驟如下」

1、在 build 下創建 webpack.dll.config.js

const path = require('path')
const webpack = require('webpack')
module.exports = {
  entry: {
    vendor: [
      'vue-router',
      'vuex',
      'vue/dist/vue.common.js',
      'vue/dist/vue.js',
      'vue-loader/lib/component-normalizer.js',
      'vue',
      'axios',
      'echarts',
    ],
  },
  output: {
    path: path.resolve('./dist'),
    filename: '[name].dll.js',
    library: '[name]_library',
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.resolve('./dist', '[name]-manifest.json'),
      name: '[name]_library',
    }),
    // 建議加上代碼壓縮插件,否則dll包會比較大。
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false,
      },
    }),
  ],
}

2、在 webpack.prod.conf.js 的 plugin 後面加入配置

new webpack.DllReferencePlugin({
  manifest: require('../dist/vendor-manifest.json'),
})

3、package.json文件中添加快捷命令(build:dll)

  "scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    "start": "npm run dev",
    "lint": "eslint --ext .js,.vue src",
    "build": "node build/build.js",
    "build:dll": "webpack --config build/webpack.dll.conf.js"
  }

生產環境打包的時候先npm run build:dll命令會在打包目錄下生成 vendor-manifest.json 文件與 vendor.dll.js 文件。然後npm run build生產其他文件。

4、根目錄下的入口 index.html 加入引用

<script type="text/javascript" src="./vendor.dll.js"></script>

HappyPack

HappyPack 能讓 webpack 把任務分解給多個子進程去併發的執行,子進程處理完後再把結果發送給主進程。要注意的是 HappyPackfile-loaderurl-loader 支持的不友好,所以不建議對該 loader 使用。

1、HappyPack 插件安裝

npm i -D happypack

2、webpack.base.conf.js 文件對 module.rules 進行配置

module: {
  rules: [
    {
      test: /\.js$/,
      use: ['happypack/loader?id=babel'],
      include: [resolve('src'), resolve('test')],
      exclude: path.resolve(__dirname, 'node_modules'),
    },
    {
      test: /\.vue$/,
      use: ['happypack/loader?id=vue'],
    },
  ]
}

3、在生產環境 webpack.prod.conf.js 文件進行配置

const HappyPack = require('happypack')
// 構造出共享進程池,在進程池中包含5個子進程
const HappyPackThreadPool = HappyPack.ThreadPool({ size: 5 })
plugins: [
  new HappyPack({
    // 用唯一的標識符id,來代表當前的HappyPack是用來處理一類特定的文件
    id: 'babel',
    // 如何處理.js文件,用法和Loader配置中一樣
    loaders: ['babel-loader?cacheDirectory'],
    threadPool: HappyPackThreadPool,
  }),
  new HappyPack({
    id: 'vue', // 用唯一的標識符id,來代表當前的HappyPack是用來處理一類特定的文件
    loaders: [
      {
        loader: 'vue-loader',
        options: vueLoaderConfig,
      },
    ],
    threadPool: HappyPackThreadPool,
  }),
]

「注意,當項目較小時,多線程打包反而會使打包速度變慢。」

copy-webpack-plugin

我們在 public/index.html 中引入了靜態資源,但是打包的時候 webpack 並不會幫我們拷貝到 dist 目錄,因此 copy-webpack-plugin 就可以很好地幫我做拷貝的工作了。

const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
  plugins: [
    new CopyWebpackPlugin({
      patterns: [
        {
          from: 'public/js/*.js',
          to: path.resolve(__dirname, 'dist', 'js'),
          flatten: true,
        },
      ],
    }),
  ],
}

IgnorePlugin

這是 webpack 內置插件,它的作用是:忽略第三方包指定目錄,讓這些指定目錄不要被打包進去。

比如我們要使用 moment 這個第三方依賴庫,該庫主要是對時間進行格式化,並且支持多個國家語言。雖然我設置了語言爲中文,但是在打包的時候,是會將所有語言都打包進去的。這樣就導致包很大,打包速度又慢。對此,我們可以用 IgnorePlugin 使得指定目錄被忽略,從而使得打包變快,文件變小。

const Webpack = require('webpack')
plugins: [
  //moment這個庫中,如果引用了./locale/目錄的內容,就忽略掉,不會打包進去
  new Webpack.IgnorePlugin(/\.\/locale/, /moment/),
]

我們雖然按照上面的方法忽略了包含’./locale/'該字段路徑的文件目錄,但是也使得我們使用的時候不能顯示中文語言了,所以這個時候可以手動引入中文語言的目錄。

import moment from 'moment'

//手動引入所需要的語言包
import 'moment/locale/zh-cn'

moment.locale('zh-cn')

let r = moment().endOf('day').fromNow()
console.log(r)

推薦文章

  • 搭建一個 vue-cli4+webpack 移動端框架(開箱即用)

  • 從零構建到優化一個類似vue-cli的腳手架

  • 封裝一個toast和dialog組件併發布到npm

  • 從零開始構建一個webpack項目

  • 總結幾個webpack打包優化的方法

  • 總結vue知識體系之高級應用篇

  • 總結vue知識體系之實用技巧

  • 總結vue知識體系之基礎入門篇

作者:lzg9527

https://segmentfault.com/a/1190000022956602


1. JavaScript 重溫系列(22篇全)
2. ECMAScript 重溫系列(10篇全)
3. JavaScript設計模式 重溫系列(9篇全)4. 正則 / 框架 / 算法等 重溫系列(16篇全)5. Webpack4 入門(上)|| Webpack4 入門(下)6. MobX 入門(上) ||  MobX 入門(下)7. 59篇原創系列彙總回覆“加羣”與大佬們一起交流學習~點這,與大家一起分享本文吧~


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章