何爲插件(Plugin)?專一處理 webpack 在編譯過程當中的某個特定的任務的功能模塊,能夠稱爲插件。javascript
Plugin 是一個擴展器,它豐富了 webpack 自己,針對是 loader 結束後,webpack 打包的整個過程,它並不直接操做文件,而是基於事件機制工做,會監聽 webpack 打包過程當中的某些節點,執行普遍的任務。css
Plugin 的特色html
(prototype)
上定義了一個注入 compiler
對象的 apply
方法 apply
函數中須要有經過 compiler
對象掛載的 webpack
事件鉤子,鉤子的回調中能拿到當前編譯的 compilation
對象,若是是異步編譯插件的話能夠拿到回調 callback
complition
對象的內部數據callback
回調。模塊熱更新插件。Hot-Module-Replacement
的熱更新是依賴於 webpack-dev-server
,後者是在打包文件改變時更新打包文件或者 reload 刷新整個頁面,HRM
是隻更新修改的部分。vue
HotModuleReplacementPlugin
是webpack
模塊自帶的,因此引入webpack
後,在plugins
配置項中直接使用便可。java
const webpack = require('webpack') plugins: [ new webpack.HotModuleReplacementPlugin(), // 熱更新插件 ]
生成 html 文件。將 webpack 中entry
配置的相關入口 chunk
和 extract-text-webpack-plugin
抽取的 css 樣式 插入到該插件提供的template
或者templateContent
配置項指定的內容基礎上生成一個 html 文件,具體插入方式是將樣式link
插入到head
元素中,script
插入到head
或者body
中。node
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 有四個選項值react
script
標籤位於 html
文件的 body
底部script
標籤位於 html
文件的 body
底部(同 true)script
標籤位於 head
標籤內html
文件多頁應用打包jquery
有時,咱們的應用不必定是一個單頁應用,而是一個多頁應用,那麼如何使用 webpack 進行打包呢。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
。ios
可是有個問題,index.html
和 login.html
會發現,都同時引入了 index.f7d21a.js
和 login.f7d21a.js
,一般這不是咱們想要的,咱們但願 index.html
中只引入 index.f7d21a.js
,login.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
用於在打包前清理上一次項目生成的 bundle 文件,它會根據 output.path
自動清理文件夾;這個插件在生產環境用的頻率很是高,由於生產環境常常會經過 hash 生成不少 bundle 文件,若是不進行清理的話每次都會生成新的,致使文件夾很是龐大。
const { CleanWebpackPlugin } = require('clean-webpack-plugin') plugins: [ new HtmlWebpackPlugin({ template: path.join(__dirname, '/index.html'), }), new CleanWebpackPlugin(), // 所要清理的文件夾名稱 ]
將 css 成生文件,而非內聯 。該插件的主要是爲了抽離 css 樣式,防止將樣式打包在 js 中引發頁面樣式加載錯亂的現象
const ExtractTextPlugin = require('extract-text-webpack-plugin') plugins: [ // 將css分離到/dist文件夾下的css文件夾中的index.css new ExtractTextPlugin('css/index.css'), ]
將 CSS 提取爲獨立的文件的插件,對每一個包含 css 的 js 文件都會建立一個 CSS 文件,支持按需加載 css 和 sourceMap
。只能用在 webpack4 中,對比另外一個插件 extract-text-webpack-plugin 有如下特色:
這個插件應該只用在生產環境配置,而且在 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', }), ], }
有時候咱們 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')), }), ], })
咱們但願減少 css 打包後的體積,能夠用到 optimize-css-assets-webpack-plugin
。
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin") // 壓縮css代碼 optimization: { minimizer: [ // 壓縮css new OptimizeCSSAssetsPlugin({}) ]
uglifyJsPlugin
是 vue-cli
默認使用的壓縮代碼方式,用來對 js 文件進行壓縮,從而減少 js 文件的大小,加速 load 速度。它使用的是單線程壓縮代碼,打包時間較慢,因此能夠在開發環境將其關閉,生產環境部署時再把它打開。
const UglifyJsPlugin = require('uglifyjs-webpack-plugin') plugins: [ new UglifyJsPlugin({ uglifyOptions: { compress: { warnings: false } }, sourceMap: true, //是否啓用文件緩存 parallel: true //使用多進程並行運行來提升構建速度 })
開啓多個子進程,把對多個文件壓縮的工做分別給多個子進程去完成,每一個子進程其實仍是經過 UglifyJS
去壓縮代碼,可是變成了並行執行。
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin') plugins: [ new ParallelUglifyPlugin({ //cacheDir 用於配置緩存存放的目錄路徑。 cacheDir: '.cache/', sourceMap: true, uglifyJS: { output: { comments: false, }, compress: { warnings: false, }, }, }), ]
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, }), ] }
報錯但不退出 webpack 進程。編譯出現錯誤時,使用 NoEmitOnErrorsPlugin
來跳過輸出階段。這樣能夠確保輸出資源不會包含錯誤。
plugins: [new webpack.NoEmitOnErrorsPlugin()]
全部現代瀏覽器都支持 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
是 webpack
自帶的插件。
plugins: [ new webpack.DefinePlugin({ DESCRIPTION: 'This Is The Test Text.', }), ] // 直接引用 console.log(DESCRIPTION)
自動加載模塊。 任什麼時候候,當 identifier
被看成未賦值的變量時, module 就會自動被加載,而且 identifier
會被這個 module 輸出的內容所賦值。這是 webpack 自帶的插件。
module.exports = { resolve: { alias: { jquery: './lib/jquery', }, }, plugins: [ //提供全局的變量,在模塊中使用無需用require引入 new webpack.ProvidePlugin({ $: 'jquery', React: 'react', }), ], }
這是在一個額外的獨立的 webpack 設置中建立一個只有 dll 的 bundle(dll-only-bundle)
。 這個插件會生成一個名爲 manifest.json
的文件,這個文件是用來讓 DLLReferencePlugin
映射到相關的依賴上去的。
使用步驟以下
一、在 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, }, }), ], }
二、在 webpack.prod.conf.js
的 plugin 後面加入配置
new webpack.DllReferencePlugin({ manifest: require('../dist/vendor-manifest.json'), })
三、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
生產其餘文件。
四、根目錄下的入口 index.html
加入引用
<script type="text/javascript" src="./vendor.dll.js"></script>
HappyPack
能讓 webpack 把任務分解給多個子進程去併發的執行,子進程處理完後再把結果發送給主進程。要注意的是 HappyPack
對 file-loader
、url-loader
支持的不友好,因此不建議對該 loader 使用。
一、HappyPack 插件安裝
npm i -D happypack
二、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'], }, ] }
三、在生產環境 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, }), ]
注意,當項目較小時,多線程打包反而會使打包速度變慢。
咱們在 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, }, ], }), ], }
這是 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)