隨着前端項目日漸的複雜,咱們平常開發中使用的構建工具也不斷演變、進化,既要知足咱們開發時的便利,還要充當構建時各類資源優化、自動打包、自動測試的角色。目前,最受歡迎的當數webpack。知足複雜的功能的同時一定帶來必定的使用成本,繁雜的配置,構建的性能也成了咱們使用webpack時須要考慮的問題。javascript
在筆者看來,webpack有四大核心的配置:css
一、入口html
二、出口前端
三、loadervue
四、pluginsjava
這四個配置,缺乏任何同樣,都不能知足咱們平時構建的複雜的需求。插件更是打包優化的核心配置。本文主要從插件的角度,對webpack打包和構建作一些優化。node
這是webpack4升級的最重要的配置之一,它的做用是將動態加載的模塊打包成common chunks,它的做用至關於以前的commonChunksPlugin,可是比以前的插件功能更增強大。由於幾乎全部的項目打包都必備,因此webpack直接內置爲一個配置,配置在optimization這個選項下。下面咱們看下默認的配置:react
splitChunks: {
chunks: 'async', // 分割異步模塊
minSize: 30000, // 分割的文件最小大小
maxSize: 0,
minChunks: 1, // 引用次數
maxAsyncRequests: 5, // 最大異步請求數
maxInitialRequests: 3, // 最大初始化請求數
automaticNameDelimiter: '~', // 抽離的命名分隔符
automaticNameMaxLength: 30, // 名字最大長度
name: true,
cacheGroups: { // 緩存組
vendors: { // 先抽離第三方庫
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20, // 優先級
reuseExistingChunk: true
}
}
}
複製代碼
使用cacheGroups咱們能夠任意定製咱們想打包的common chunk,還能夠控制每一個chunk的大小,默認配置已經知足大多數開發的需求。webpack
接下來要介紹的是DllPlugin和DllReferencePlugin的結合,這兩個插件必須配合使用才能發揮其巨大的做用,主要的做用是提高你開發時的構建速度。它的思路是這樣的,首先使用DllPlugin預編譯全部你項目中幾乎不會變化的外部模塊,而後在構建以前,把這些資源打包成一個vendor,以後直接在項目中使用,每次修改文件這些資源將不用從新打包,下面咱們看下配置,單首創建webpack.dll.js:git
const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');
module.exports = {
entry:['vue','vue-router', 'vuex'],
mode:'production',
output:{
filename:'vue.dll.js',
path:path.resolve(__dirname,'dll'),
library:'vue'
},
plugins:[
new DllPlugin({
name:'vue',
path:path.resolve(__dirname,'dll/manifest.json')
})
]
}
複製代碼
執行webpack --config webpack.dll.js
命令 ,咱們將看到生成的dll目錄下有兩個文件:vue.dll.js和manifest.json
,manifest.json中保存了咱們打包到vue.dll.js中的模塊路徑,而後接下來咱們須要使用DllReferencePlugin插件經過manifest.json文件去找咱們須要的依賴:
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
// 構建時會引用動態連接庫的內容
new DllReferencePlugin({
manifest:path.resolve(__dirname,'dll/manifest.json')
}),
// 經過插件將vue.dll.js自動引入到咱們的index.html中
new AddAssetHtmlWebpackPlugin(
{ filepath: path.resolve(__dirname,'dll/vue.dll.js') }
)
複製代碼
到此,咱們完成了兩個插件的使用。
這裏是本文的一個特例,image-webpack-loader它不是一個插件,可是它的做用十分強大,像是插件的角色。前端開發中,咱們少不了對圖片資源的使用,那麼在繁雜的各類圖片資源中,咱們須要一個工具對不一樣的圖片格式進行壓縮處理,這樣構建處理的圖片才能在尺寸上也符合咱們的指望。image-webpack-loader能夠對各類圖片格式進行必定的壓縮。話很少說直接看配置:
loader: "image-webpack-loader",
options: {
mozjpeg: {
progressive: true,
quality: 65
},
// optipng.enabled: false will disable optipng
optipng: {
enabled: false,
},
pngquant: {
quality: [0.90, 0.95],
speed: 4
},
gifsicle: {
interlaced: false,
},
// the webp option will enable WEBP
webp: {
quality: 75
}
}
複製代碼
它可讓咱們在保證圖片清晰度的狀況下,對圖片進行壓縮,能夠自行根據項目的須要配置,圖片壓縮就是這麼簡單。
這是一個既包含loader又能夠做爲插件的插件,它的做用是將css單獨打包成一個文件。webpack通常經常使用的是經過style-loader將css經過style標籤插入到html中,可是這樣不利於緩存,也影響js的加載。因此,咱們通常須要將一些css單獨打包出來。下面看插件的使用:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
plugins: [
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// all options are optional
filename: '[name].css',
chunkFilename: '[id].css',
ignoreOrder: false, // Enable to remove warnings about conflicting order
}),
],
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
// you can specify a publicPath here
// by default it uses publicPath in webpackOptions.output
publicPath: '../',
hmr: process.env.NODE_ENV === 'development',
},
},
'css-loader',
],
},
],
},
};
複製代碼
相比於extract-text-webpack-plugin插件,它有一些優點:
有時候咱們參與開發的項目很大,參與的開發人員也不少,你們在開發的時候極可能寫了一些無用的樣式,或是由於需求變動忘記刪除。這時候咱們就須要一個插件:purgecss-webpack-plugin,幫助咱們自動清理掉一些無用的樣式:
const glob = require('glob');
const PurgecssPlugin = require('purgecss-webpack-plugin');
// 須要配合mini-css-extract-plugin插件
{
plugins: [
new PurgecssPlugin({
// 不匹配目錄,只匹配文件
paths: glob.sync(`${path.join(__dirname, "src")}/**/*`, { nodir: true })
})
]
}
複製代碼
固然咱們須要配合glob庫使用,告訴插件須要匹配哪些目錄。咱們看一段代碼:
import './style.css'
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(<div>hello</div>,document.getElementById('root'));
複製代碼
// style.css
body{
background: red
}
// 這個類的樣式是無用的,將會被清除
.class1{
background: red
}
複製代碼
使用webpack對項目進行構建時,會對大量的文件進行處理,文件越多構建速度越慢。其中一個緣由就是運行在Node上的webpack是單線程的,它只能一個個任務排隊處理。使用happypack能夠將解析文件的任務分紅多個子進程併發執行,每一個子進程處理完再將結果返回給主進程,從而能大大提高webpack的構建項目速度。下面來看下happypack的使用方法:
首先將你想使用多進程處理的loader作一個修改:
const HappyPack = require('happypack');
module.exports = {
...
module: {
rules: [
test: /\.js$/,
// use: ['babel-loader?cacheDirectory'] 以前是使用這種方式直接使用 loader
// 如今用下面的方式替換成 happypack/loader,並使用 id 指定建立的 HappyPack 插件
use: ['happypack/loader?id=babel'],
// 排除 node_modules 目錄下的文件
exclude: /node_modules/
]
}
}
複製代碼
而後在插件中配置happypack:
const HappyPack = require('happypack');
module.exports = {
...
module: {
rules: [
test: /\.js$/,
// use: ['babel-loader?cacheDirectory'] 以前是使用這種方式直接使用 loader
// 如今用下面的方式替換成 happypack/loader,並使用 id 指定建立的 HappyPack 插件
use: ['happypack/loader?id=babel'],
// 排除 node_modules 目錄下的文件
exclude: /node_modules/
]
},
plugins: [
...,
new HappyPack({
/* * 必須配置 */
// id 標識符,要和 rules 中指定的 id 對應起來
id: 'babel',
// 須要使用的 loader,用法和 rules 中 Loader 配置同樣
// 能夠直接是字符串,也能夠是對象形式
loaders: ['babel-loader?cacheDirectory']
})
]
}
複製代碼
這樣就完成了happypack的使用。happypack有不少配置項,詳細能夠查看happypack。
使用緩存來有優化性能在前端領域是一種很常見的手段,既然webpack每次都要構建大量的文件,是否是能夠經過一個插件對前一次的構建結果作一些緩存了?答案固然是能夠的,hard-source-webpack-plugin就是這樣一個webpack插件。既然是緩存,因此固然要第二次才能看到效果,第一次構建插件就會默認把緩存結果存到node_modules下的.cache目錄下,第二次構建的時候再取出緩存使用。它還能夠配置緩存目錄,緩存時間,緩存資源的最大尺寸等等。下面咱們看下它的一些配置和使用方法:
new HardSourceWebpackPlugin({
// Either an absolute path or relative to webpack's options.context.
cacheDirectory: 'node_modules/.cache/hard-source/[confighash]',
// Either a string of object hash function given a webpack config.
configHash: function(webpackConfig) {
// node-object-hash on npm can be used to build this.
return require('node-object-hash')({sort: false}).hash(webpackConfig);
},
// Either false, a string, an object, or a project hashing function.
environmentHash: {
root: process.cwd(),
directories: [],
files: ['package-lock.json', 'yarn.lock'],
},
// An object.
info: {
// 'none' or 'test'.
mode: 'none',
// 'debug', 'log', 'info', 'warn', or 'error'.
level: 'debug',
},
// Clean up large, old caches automatically.
cachePrune: {
// Caches younger than `maxAge` are not considered for deletion. They must
// be at least this (default: 2 days) old in milliseconds.
maxAge: 2 * 24 * 60 * 60 * 1000,
// All caches together must be larger than `sizeThreshold` before any
// caches will be deleted. Together they must be at least this
// (default: 50 MB) big in bytes.
sizeThreshold: 50 * 1024 * 1024
},
})
複製代碼
工欲善其事必先利其器,咱們想要對webpack構建或者構建的資源作一些優化,那咱們首先得知道,優化的方向在哪?不少的優化建議可能只是針對某些項目有效,不必定適合你的項目。因此,咱們須要藉助一些工具,經過工具產出的數據來指導咱們進行構建的優化。webpack-bundle-analyz,這個插件可讓咱們瞭解到構建出的文件的尺寸大小、依賴哪些模塊等等,下面是一張效果圖:
經過圖咱們能夠知道:
它的使用也很簡單:
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
}
複製代碼
固然它有一些的具體參數能夠配置,詳情參考webpack-bundle-analyz。
上面咱們介紹了一個能夠對webpack構建出的bundle進行分析的插件,從而找到合適的一些優化途徑。這裏要介紹的是另外一個測量插件--speed-measure-webpack-plugin,它的做用是測量webpack構建過程當中使用的loader和各插件所花費的時間,從而讓咱們清楚地知道webpack構建的時間主要花在什麼地方。下面是一張使用這個插件生成的效果圖:
經過圖,咱們能夠很清楚地瞭解到每一個插件和loader所花費的時間。咱們來看下它的使用:
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();
module.export = smp.wrap({
plugins: [
new MyPlugin(),
new MyOtherPlugin()
]
})
複製代碼
使用也很是簡單,若是你還想了解更多的配置項,能夠查看SpeedMeasurePlugin。
這個插件的做用是向html中注入<link rel='preload|prefetch'>
標籤,從而到達優化頁面靜態資源預加載的功能,並且支持異步的chunk。此插件必須配合html-webpack-plugin插件使用,並且配置的時候緊跟其後,下面是一個簡單的配置:
const PreloadWebpackPlugin = require('preload-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = [
plugins: [
new HtmlWebpackPlugin(),
new PreloadWebpackPlugin()
]
]
複製代碼
使用以後的效果:
在vue-cli3中就內置了這個插件,默認幫你將資源優先級高的資源加上preload的功能。更多配置參考:preload-webpack-plugin。