背景
說到構(gòu)建工具坦仍,我往往會在前面加「自動化」三個字贺氓,因?yàn)闃?gòu)建工具就是用來讓我們不再做機(jī)械重復(fù)的事情,解放我們的雙手的载绿。
什么是前端自動化粥诫?前端工程師需要維護(hù)的代碼極為龐大和復(fù)雜,代碼維護(hù)崭庸、打包怀浆、發(fā)布等流程也變得極為繁瑣谊囚,同時浪費(fèi)的時間和精力也越來越多,當(dāng)然人為的錯誤也隨著流程的增加而增加了更多的出錯率执赡。
致使每一個團(tuán)隊(duì)都希望有一種工具镰踏,能幫助整個團(tuán)隊(duì)在開發(fā)中能精簡流程、提高效率沙合、減少錯誤率奠伪。隨之討論自動化部署也越來越多,并且國內(nèi)很多大型團(tuán)隊(duì)也都有自己成熟的自動化部署工具首懈。
常用的構(gòu)建工具 gulp绊率,webpack,parcel究履,rollup滤否,vite ,fis最仑,grunt等
經(jīng)過多年的發(fā)展顽聂,Webpack 已經(jīng)成為構(gòu)建工具中的首選,這是因?yàn)椋?/p>
大多數(shù)團(tuán)隊(duì)在開發(fā)新項(xiàng)目時會采用緊跟時代的技術(shù)盯仪,這些技術(shù)幾乎都會采用“模塊化+新語言+新框架”紊搪,Webpack可以為這些新項(xiàng)目提供一站式的解決方案;
Webpack有良好的生態(tài)和維護(hù)團(tuán)隊(duì)全景,能提供良好的開發(fā)體驗(yàn)并保證質(zhì)量耀石;
Webpack 被全世界大量的Web開發(fā)者使用和驗(yàn)證,能找到各個層面所需要的教程和經(jīng)驗(yàn)分享爸黄。
webpack的構(gòu)成
說webpack是構(gòu)建工具并不確切滞伟,更確切的說法應(yīng)該是靜態(tài)模塊打包器,在webpack里包(bundle)的概念很重要炕贵,另一個重要的概念是依賴關(guān)系圖梆奈,這組成了這個打包器的重要核心。
webpack有四大核心要素称开,這也是跟我們使用配置webpack時十分密切的四個部分:
entry: 配置入口文件亩钟,即產(chǎn)生依賴關(guān)系圖的入口
output:文件的產(chǎn)出位置配置
loader:匹配文件的編譯過程
plugins:針對整個構(gòu)建打包流程的插件處理(文件壓縮,dev環(huán)境的熱加載)
出入口決定了我們需要將那些文件打包到哪里去鳖轰,而loader承接這里的匹配文件的編譯工作清酥,plugins則是針對整個構(gòu)建過程的操作,需要什么功能引入什么插件蕴侣。
webpack打包是如何運(yùn)行的
也可以稱為焰轻,webpack是如何實(shí)現(xiàn)模塊化的
CommonJS是同步加載模塊,一般用于node昆雀。因?yàn)閚ode應(yīng)用程序運(yùn)行在服務(wù)器上辱志,程序通過文件系統(tǒng)可以直接讀取到各個模塊的文件蝠筑,特點(diǎn)是響應(yīng)快速,不會因?yàn)橥蕉枞顺绦虻倪\(yùn)行揩懒;
AMD是異步加載模塊什乙,所以普遍用于前端。而前端項(xiàng)目運(yùn)行在瀏覽器中旭从,每個模塊都要通過http請求加載js模塊文件稳强,受到網(wǎng)絡(luò)等因素的影響如果同步的話就會使瀏覽器出現(xiàn)“假死”(卡死)的情況场仲,影響到了用戶體驗(yàn)和悦。
ESModule 旨在實(shí)現(xiàn)前后端模塊化的統(tǒng)一。而webpack就是把ES6的模塊化代碼轉(zhuǎn)碼成CommonJS的形式渠缕,從而兼容瀏覽器的鸽素。
為什么webpack打包后的文件,可以用在瀏覽器:此時webpack會將所有的js模塊打包到bundle.js中(異步加載的模塊除外亦鳞,異步模塊后面會講)馍忽,讀取到了內(nèi)存里,就不會再分模塊加載了燕差。
webpack對CommonJS的模塊化處理
舉例:
index.js文件遭笋,引入test.js文件
const test = require('./test');
console.log(test);
console.log('hello world');
test.js文件
module.exports = {
name: 'startdt',
age: '5',
};
當(dāng)我們執(zhí)行webpack之后,打包完成徒探,可以看到bundle.js內(nèi)的代碼
// modules 即為存放所有模塊的數(shù)組瓦呼,數(shù)組中的每一個元素都是一個函數(shù)
(function(modules) {
// 安裝過的模塊都存放在這里面
// 作用是把已經(jīng)加載過的模塊緩存在內(nèi)存中,提升性能
var installedModules = {};
// 去數(shù)組中加載一個模塊测暗,moduleId 為要加載模塊在數(shù)組中的 index
// __webpack_require__作用和 Node.js 中 require 語句相似
function __webpack_require__(moduleId) {
// require 模塊時先判斷是否已經(jīng)緩存, 已經(jīng)緩存的模塊直接返回
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// 如果緩存中不存在需要加載的模塊央串,就新建一個模塊,并把它存在緩存中
var module = installedModules[moduleId] = {
// 模塊在數(shù)組中的index
i: moduleId,
// 該模塊是否已加載完畢
l: false,
// 該模塊的導(dǎo)出值碗啄,也叫模塊主體內(nèi)容, 會被重寫
exports: {}
};
// 從 modules 中獲取 index 為 moduleId 的模塊對應(yīng)的函數(shù)
// 再調(diào)用這個函數(shù)质和,同時把函數(shù)需要的參數(shù)傳入,this指向模塊的主體內(nèi)容
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// 將模塊標(biāo)記為已加載
module.l = true;
// 返回模塊的導(dǎo)出值稚字,即模塊主體內(nèi)容
return module.exports;
}
// 向外暴露所有的模塊
__webpack_require__.m = modules;
// 向外暴露已緩存的模塊
__webpack_require__.c = installedModules;
...
...
// Webpack 配置中的 publicPath饲宿,用于加載被分割出去的異步代碼,這個暫時還沒有用到
__webpack_require__.p = "";
// Load entry module and return exports
// 準(zhǔn)備工作做完了, require 一下入口模塊, 讓項(xiàng)目跑起來
// 使用 __webpack_require__ 去加載 index 為 0 的模塊胆描,并且返回該模塊導(dǎo)出的內(nèi)容
// index 為 0 的模塊就是 index.js文件褒傅,也就是執(zhí)行入口模塊
// __webpack_require__.s 的含義是啟動模塊對應(yīng)的 index
return __webpack_require__(__webpack_require__.s = 0);
})
/***** 華麗的分割線 上邊時 webpack 初始化代碼, 下邊是我們寫的模塊代碼 *******/
// 所有的模塊都存放在了一個數(shù)組里,根據(jù)每個模塊在數(shù)組的 index 來區(qū)分和定位模塊
([
/* 模塊 0 對應(yīng) index.js */
(function(module, exports, __webpack_require__) {
// 通過 __webpack_require__ 規(guī)范導(dǎo)入 foo 函數(shù)袄友,foo.js 對應(yīng)的模塊 index 為 1
const test = __webpack_require__(1);
console.log(test);
console.log('hello world');
}),
/* 模塊 1 對應(yīng) foo.js */
(function(module, exports) {
// 通過 CommonJS 規(guī)范導(dǎo)出對象
module.exports = {
name: 'startdt',
age: '5',
};
})
]);
上面是一個立即執(zhí)行函數(shù)殿托,簡單點(diǎn)寫:
(function(modules) {
// 模擬 require 語句
function __webpack_require__(index) {
return [/*存放所有模塊的數(shù)組中,第index個模塊暴露的東西*/]
}
// 執(zhí)行存放所有模塊數(shù)組中的第0個模塊剧蚣,并且返回該模塊導(dǎo)出的內(nèi)容
return __webpack_require__(0);
})([/*存放所有模塊的數(shù)組*/])
bundle.js 能直接運(yùn)行在瀏覽器中的原因在于:
webpack通過 _webpack_require_ 函數(shù)(該函數(shù)定義了一個可以在瀏覽器中執(zhí)行的加載函數(shù))模擬了模塊的加載(類似于Node.js 中的 require 語句)支竹,把定義的模塊內(nèi)容掛載到module.exports上旋廷;
同時__webpack_require__函數(shù)中也對模塊緩存做了優(yōu)化,執(zhí)行加載過的模塊不會再執(zhí)行第二次礼搁,執(zhí)行結(jié)果會緩存在內(nèi)存中饶碘,當(dāng)某個模塊第二次被訪問時會直接去內(nèi)存中讀取被緩存的返回值。
原來一個個獨(dú)立的模塊文件被合并到了一個單獨(dú)的 bundle.js 的原因在于馒吴,瀏覽器不能像 Node.js 那樣快速地去本地加載一個個模塊文件扎运,而必須通過網(wǎng)絡(luò)請求去加載還未得到的文件。 如果模塊數(shù)量很多饮戳,加載時間會很長豪治,因此把所有模塊都存放在了數(shù)組中,執(zhí)行一次網(wǎng)絡(luò)加載扯罐。
這一部分负拟,我寫一了一個demo
webpack對es6 Module模塊化的處理
舉例
index.js文件,引入test.js文件
import test from './test';
console.log(test);
console.log('hello world');
test.js文件
export default {
name: 'startdt',
age: '5',
};
打包完后bundle.js代碼如下
(function(modules) {
var installedModules = {};
function __webpack_require__(moduleId) {
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
module.l = true;
return module.exports;
}
__webpack_require__.m = modules;
__webpack_require__.c = installedModules;
__webpack_require__.d = function(exports, name, getter) {
if(!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, {
configurable: false,
enumerable: true,
get: getter
});
}
};
__webpack_require__.n = function(module) {
var getter = module && module.__esModule ?
function getDefault() { return module['default']; } :
function getModuleExports() { return module; };
__webpack_require__.d(getter, 'a', getter);
return getter;
};
__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
__webpack_require__.p = "";
return __webpack_require__(__webpack_require__.s = 0);
})([相關(guān)模塊]);
打包好的內(nèi)容和commonjs模塊化方法差不多
function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// 在__webpack_exports__上定義__esModule為true歹河,表明是一個模塊對象
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
var __WEBPACK_IMPORTED_MODULE_0__foo__ = __webpack_require__(1);
console.log(__WEBPACK_IMPORTED_MODULE_0__foo__["a"]);
console.log('hello world');
},
function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_exports__["a"] = ({
name: 'startdt',
age: '5',
});
}
和 commonjs 不同的地方
首先, 包裝函數(shù)的參數(shù)之前的 module.exports 變成了_webpack_exports_
其次, 在使用了 es6 模塊導(dǎo)入語法(import)的地方, 給__webpack_exports__添加了屬性__esModule
其余的部分和 commonjs 類似
webpack文件的按需加載
以上webpack把所有模塊打包到主文件中掩浙,所以模塊加載方式都是同步方式。但在開發(fā)應(yīng)用過程中秸歧,按需加載(也叫懶加載)也是經(jīng)常使用的優(yōu)化技巧之一厨姚。
按需加載,通俗講就是代碼執(zhí)行到異步模塊(模塊內(nèi)容在另外一個js文件中)键菱,通過網(wǎng)絡(luò)請求即時加載對應(yīng)的異步模塊代碼谬墙,再繼續(xù)接下去的流程。
main.js 文件
window.document.getElementById('btn').addEventListener('click', function () {
// 當(dāng)按鈕被點(diǎn)擊后才去加載 show.js 文件纱耻,文件加載成功后執(zhí)行文件導(dǎo)出的函數(shù)
import(/* webpackChunkName: "show" */ './show').then((show) => {
show('Webpack');
})
});
show.js 文件
module.exports = function (content) {
window.alert('Hello ' + content);
};
代碼中最關(guān)鍵的一句是 import(/* webpackChunkName: “show” / ‘./show’)芭梯,Webpack 內(nèi)置了對 import() 語句的支持,當(dāng) Webpack 遇到了類似的語句時會這樣處理:
以 ./show.js 為入口新生成一個 Chunk弄喘;
當(dāng)代碼執(zhí)行到 import 所在語句時才會去加載由 Chunk 對應(yīng)生成的文件玖喘。
import 返回一個 Promise,當(dāng)文件加載成功時可以在 Promise 的 then 方法中獲取到 show.js 導(dǎo)出的內(nèi)容蘑志。
webpack有個require.ensure api語法來標(biāo)記為異步加載模塊累奈,webpack4推薦使用新的import() api(需要配合@babel/plugin-syntax-dynamic-import插件)。
因?yàn)閞equire.ensure是通過回調(diào)函數(shù)執(zhí)行接下來的流程急但,而import()返回promise澎媒,這意味著可以使用 async/await語法,使得可以像書寫同步代碼一樣波桩,執(zhí)行異步流程戒努。
上述內(nèi)容打包后會生成兩個chunk文件,分別是主文件執(zhí)行入口文件 bundle.js 和 異步加載文件 0.bundle.js 镐躲。
// 0.bundle.js
// 異步模塊
// window["webpackJsonp"]是連接多個chunk文件的橋梁
// window["webpackJsonp"].push = 主chunk文件.webpackJsonpCallback
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([
[0], // 異步模塊標(biāo)識chunkId,可判斷異步代碼是否加載成功
// 跟同步模塊一樣储玫,存放了{(lán)模塊路徑:模塊內(nèi)容}
{
"./src/async.js": (function(module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
__webpack_exports__["default"] = (function () {
return 'hello, aysnc module';
});
})
}
]);
異步模塊打包后的文件中保存著異步模塊源代碼侍筛,同時為了區(qū)分不同的異步模塊,還保存著該異步模塊對應(yīng)的標(biāo)識:chunkId撒穷。以上代碼主動調(diào)用window[“webpackJsonp”].push函數(shù)匣椰,該函數(shù)是連接異步模塊與主模塊的關(guān)鍵函數(shù),該函數(shù)定義在主文件中端礼,實(shí)際上window[“webpackJsonp”].push = webpackJsonpCallback
webpack異步加載模塊實(shí)現(xiàn)流程跟jsonp基本一致禽笑。
既然我們知道了打包的結(jié)果,那webpack的是怎么運(yùn)行的呢
webpack的核心對象
Tapable:控制鉤子的發(fā)布與訂閱蛤奥,Compiler和Compilation 對象都繼承于 Tapable
Compiler
Compiler 繼承 Tapable 對象佳镜,可以廣播和監(jiān)聽 webpack 事件。
Compiler 對象是 webpack 的編譯器喻括,webpack 周期中只存在一個 Compiler 對象邀杏。
Compiler 對象在 webpack 啟動時創(chuàng)建實(shí)例贫奠,compiler 實(shí)例中包含 webpack 的完整配置唬血,包括 loaders, plugins 信息。
Compilation
Compilation 繼承 Tapable 對象唤崭,可以廣播和監(jiān)聽 webpack 事件拷恨。
Compilation 實(shí)例僅代表一次 webpack 構(gòu)建和生成編譯資源的的過程。
webpack 開發(fā)模式開啟 watch 選項(xiàng)谢肾,每次檢測到入口文件模塊變化時腕侄,會創(chuàng)建一次新的編譯: 生成一次新的編譯資源和新的 compilation 對象,這個 compilation 對象包含了當(dāng)前編譯的模塊資源 module, 編譯生成的資源芦疏,變化的文件, 依賴的的狀態(tài)
webpack?的運(yùn)行流程是一個串行的過程冕杠,從啟動到結(jié)束會依次執(zhí)行以下流程:
初始化參數(shù):從配置文件和?Shell?語句中讀取與合并參數(shù),得出最終的參數(shù);
開始編譯:用上一步得到的參數(shù)初始化?Compiler?對象 (實(shí)例化complier對象)酸茴,加載所有配置的插件分预,執(zhí)行對象的?run?方法開始執(zhí)行編譯,生成Compilation對象 (實(shí)例化Compilation對象);
確定入口:根據(jù)配置中的 entry ,調(diào)用AST引擎(acorn)處理入口文件薪捍,生成抽象語法樹AST笼痹,根據(jù)AST構(gòu)建模塊的所有依賴;
編譯模塊:從入口文件出發(fā)酪穿,調(diào)用所有配置的 Loader 對模塊進(jìn)行翻譯凳干,再找出該模塊依賴的模塊,再遞歸本步驟直到所有入口依賴的文件都經(jīng)過了本步驟的處理被济;
完成模塊編譯:在經(jīng)過第4步使用 Loader 翻譯完所有模塊后救赐,得到了每個模塊被翻譯后的最終內(nèi)容以及它們之間的依賴關(guān)系;
輸出資源:根據(jù)入口和模塊之間的依賴關(guān)系只磷,組裝成一個個包含多個模塊的?Chunk经磅,再把每個?Chunk?轉(zhuǎn)換成一個單獨(dú)的文件加入到輸出列表少欺,這步是可以修改輸出內(nèi)容的最后機(jī)會;
輸出完成:在確定好輸出內(nèi)容后馋贤,根據(jù)配置確定輸出的路徑和文件名赞别,把文件內(nèi)容輸出到目錄。
編譯前準(zhǔn)備
此階段概述:在 compiler 的各種 hook 上注冊項(xiàng)目配置的 plugins配乓、注冊 webpack 默認(rèn)插件 ?? 注冊 resolverFactory.hooks 為 Factory.createResolver 方法提供參數(shù)對象仿滔。
webpack 的事件機(jī)制是基于 tapable 庫做的事件流控制,在整個編譯過程中暴露出各種hook犹芹,而 plugin 注冊監(jiān)聽了某個/某些 hook崎页,在這個 hook 觸發(fā)時,會執(zhí)行 plugin 里綁定的方法腰埂。
new NodeEnvironmentPlugin({
infrastructureLogging: options.infrastructureLogging
}).apply(compiler);
loader階段
遞歸編譯生成 module 實(shí)例
resolve 階段飒焦,解析返回包含當(dāng)前模塊所有信息的一個對象
此階段概述:利用 enhanced-resolve 庫,得到 resolve 解析方法 ?? 解析 inline loader 和它對應(yīng)資源的 resource屿笼,還有項(xiàng)目config的 loader牺荠,然后對所有 loader 進(jìn)行合并、排序 ?? 得到 module 對應(yīng)的 parser 和 generator驴一,用于后面的 ast 解析及模板生成 ?? 輸出一個包含當(dāng)前模塊上下文休雌、loaders、絕對路徑肝断、依賴等 module 所有信息的組合對象杈曲,提供給 afterResolve 鉤子觸發(fā)后的回調(diào)。這個對象下一步會被用來初始化當(dāng)前文件 的 module 實(shí)例胸懈。
比如
import Styles from style-loader!css-loader?modules!./styles.css
會被解析成:
{
"resource": "./styles.css",
"elements": [
{
"loader": "style-loader"
},
{
"loader": "css-loader",
"options": "modules"
}
]
}
然后并行處理參數(shù)數(shù)組各個任務(wù)担扑,完成之后都會返回一個 results 列表,列表順序?yàn)閰?shù)數(shù)組順序趣钱,與執(zhí)行順序無關(guān)涌献。
得到的 results:
{
"results": [
[
{
"loader": "loader的絕對路徑1",
"options": "loader參數(shù)1"
},
{
"loader": "loader的絕對路徑2",
"options": "loader參數(shù)2"
}
],
{
"resource": "模塊絕對路徑",
"resourceResolveData": "模塊基本信息(即enhanced-resolve執(zhí)行結(jié)果)"
}
]
}
解析 config module rules 里的 loader,遞歸過濾匹配出對應(yīng)的 loader:
{
"result": [
{ "type": "type", "value": "javascript/auto" },
{ "type": "resolve", "value": {} },
{ "type": "use", "value": { "loader": "babel-loader" } }
]
}
對 loader 進(jìn)行合并羔挡、排序:
接著處理inline loader帶有前綴!,!!,-!和result項(xiàng)帶有enforce參數(shù)的情況洁奈,用來決定懟 loader的禁用和排序。
并行處理上一步得到的useLoadersPost绞灼、useLoadersPre利术、useLoaders,拿到對應(yīng)的 resolve 結(jié)果即路徑信息低矮,再在回調(diào)里排序印叁、合并,
即 loaders 配置順序?yàn)?postLoader,inlineLoader轮蜕,loader(normal)昨悼,preLoader,執(zhí)行順序則相反跃洛。
執(zhí)行 loader 階段率触,初始化模塊 module,并用 loader 倒序轉(zhuǎn)譯
開啟構(gòu)建 module 流程汇竭。 new NormalModule(result)得到初始化的 module ?? 在 build 過程中執(zhí)行 runLoaders 處理源碼葱蝗,先正序讀取每個 loader 并執(zhí)行它的 pitch,再倒序執(zhí)行每個 loader 的 normal细燎,最后得到一個編譯后的字符串或 Buffer两曼。
runLoaders 方法來自 loader-runner,作用是按規(guī)定流程執(zhí)行各種 loader玻驻,將模塊源碼后處理成一個 String 或 Buffer 格式的 JavaScript (可能還有個 SourceMap)悼凑。
parse 階段,收集依賴
調(diào)用 parser 將上一步 runLoaders 的編譯結(jié)果利用 acorn 庫轉(zhuǎn)換為 ast璧瞬。生成的 AST 劃分為三部分:ImportDeclaration户辫、FunctionDeclaration和VariablesDeclaration。?? 遍歷 ast彪蓬,根據(jù)導(dǎo)入導(dǎo)出及異步的情況觸發(fā)相關(guān)鉤子插件來收集依賴寸莫,這些依賴用于解析遞歸依賴和模板操作 ?? 根據(jù)每個 module 的相關(guān)信息生成各自唯一的 buildHash捺萌。
遞歸處理依賴階段 (重復(fù)以上步驟)
根據(jù) module 間的相互依賴關(guān)系档冬,遞歸解析所有依賴 module。即 resolve ?? 執(zhí)行 loader ?? parse ?? 收集并處理該模塊依賴的模塊桃纯,直到所有入口依賴 (直接或間接) 的文件都經(jīng)過了這些步驟的處理酷誓。最終返回一個入口 module。
loader過程歸納
就這樣乾颁,從入口module開始展东,根據(jù)module之間的依賴關(guān)系慨畸,遞歸將所有的module都轉(zhuǎn)換編譯。
直到層層依賴都轉(zhuǎn)換完成玫氢,執(zhí)行return process.nextTick(callback);,將在下一次事件循環(huán)tick之前調(diào)用 callback谜诫,
此時返回一個入口 module:
{
"module": {
//...
//同步模塊
"dependencies": ["HarmonyImportSideEffectDependency", "HarmonyImportSpecifierDependency"],
//異步模塊
"blocks": ["ImportDependenciesBlock"]
}
}
無拋出錯誤則再觸發(fā) compilation.hooks:succeedEntry漾峡,到此 module 生成結(jié)束。
plugins
一個webpack的插件由以下幾方面組成:
一個非匿名的js函數(shù)
在它的原型對象上定義apply方法
指明掛載自身的webpack鉤子事件
操作webpack內(nèi)部情況的特定數(shù)據(jù)
方法完成時喚起webpack提供的回調(diào)
插件的基本結(jié)構(gòu)
插件是在原型中帶有一個apply方法的實(shí)例化對象喻旷,當(dāng)安裝插件的時候生逸,這個apply方法就會被webpack調(diào)用一次。apply方法提供一個指向當(dāng)前活動的webpack compiler的引用,該引用允許訪問compiler的回調(diào)
簡單的Plugin案例
function HelloWorldPlugin() {
//
};
HelloWorldPlugin.prototype.apply = function(compiler) {
compiler.plugin('webpacksEventHook', function(compilation, callback) {
console.log('Hello World!')
callback();
});
};
module.exports = HelloWorldPlugin;
function HelloCompilationPlugin(options) {}
HelloCompilationPlugin.prototype.apply = function(compiler) {
// Setup callback for accessing a compilation:
compiler.plugin("compilation", function(compilation) {
// Now setup callbacks for accessing compilation steps:
compilation.plugin("optimize", function() {
console.log("Assets are being optimized.");
});
});
};
module.exports = HelloCompilationPlugin;
插件的使用
const HelloWorldPlugin = require('hello-world');
const webpackConfig = {
// ... config settings here ...
plugins: [
new HelloWorldPlugin({options: true})
]
};