真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

深入理解webpack

webpack是目前最為流行的打包工具之一,其配置簡(jiǎn)單,功能強(qiáng)大,擁有豐富的加載器和插件系統(tǒng),為前端開(kāi)發(fā)者提供了諸多便利。筆者默認(rèn)各位看官在閱讀本章之前已經(jīng)有了一定的使用經(jīng)驗(yàn),所以對(duì)webpack的使用方式不做贅述。

創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供貢山網(wǎng)站建設(shè)、貢山做網(wǎng)站、貢山網(wǎng)站設(shè)計(jì)、貢山網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、貢山企業(yè)網(wǎng)站模板建站服務(wù),十載貢山做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。

閱讀本章,你可以了解到如下內(nèi)容:

Webpack打包后代碼結(jié)構(gòu)Webpack核心架構(gòu) —— TapableWebpack事件流Webpack插件實(shí)現(xiàn)機(jī)制Webpack加載器實(shí)現(xiàn)機(jī)制

Webpack打包后代碼結(jié)構(gòu)

簡(jiǎn)單打包
我們首先寫(xiě)一個(gè)最簡(jiǎn)單的方法,然后使用webpack進(jìn)行打包:

// /webpack/bundles/simple/moduleA.js
window.printA = function printA() {
    console.log(`This is module A!`);
}

一個(gè)比較基本的webpack配置文件:

// /webpack/bundles/simple/webpack.config.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: {
        main: './moduleA.js'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'simple.bundle.js'
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './index.html'
        })
    ]
}

創(chuàng)建一個(gè)HTML文件用于在瀏覽器環(huán)境下測(cè)試:




    
    
    
    Webpack - Simple Bundle


    

執(zhí)行打包命令webpack后我們獲得了一個(gè)dist目錄,我們打開(kāi)simple.bundle.js文件:

/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};
/******/
/******/    // The require function
/******/    function __webpack_require__(moduleId) {
/******/
/******/        // Check if module is in cache
/******/        if(installedModules[moduleId]) {
/******/            return installedModules[moduleId].exports;
/******/        }
/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            i: moduleId,
/******/            l: false,
/******/            exports: {}
/******/        };
/******/
/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/        // Flag the module as loaded
/******/        module.l = true;
/******/
/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/
/******/
/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;
/******/
/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;
/******/
/******/    // define getter function for harmony exports
/******/    __webpack_require__.d = function(exports, name, getter) {
/******/        if(!__webpack_require__.o(exports, name)) {
/******/            Object.defineProperty(exports, name, {
/******/                configurable: false,
/******/                enumerable: true,
/******/                get: getter
/******/            });
/******/        }
/******/    };
/******/
/******/    // getDefaultExport function for compatibility with non-harmony modules
/******/    __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;
/******/    };
/******/
/******/    // Object.prototype.hasOwnProperty.call
/******/    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";
/******/
/******/    // Load entry module and return exports
/******/    return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {

window.printA = function printA() {
    console.log(`This is module A!`);
}

/***/ })
/******/ ]);

主要看這段:

// ......
var installedModules = {};
/******/
/******/    // The require function
/******/    function __webpack_require__(moduleId) {
/******/
/******/        // Check if module is in cache
/******/        if(installedModules[moduleId]) {
/******/            return installedModules[moduleId].exports;
/******/        }
/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            i: moduleId,
/******/            l: false,
/******/            exports: {}
/******/        };
/******/
/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/        // Flag the module as loaded
/******/        module.l = true;
/******/
/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/
/******/
/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;
/******/
/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;
/******/
/******/    // define getter function for harmony exports
/******/    __webpack_require__.d = function(exports, name, getter) {
/******/        if(!__webpack_require__.o(exports, name)) {
/******/            Object.defineProperty(exports, name, {
/******/                configurable: false,
/******/                enumerable: true,
/******/                get: getter
/******/            });
/******/        }
/******/    };
/******/
/******/    // getDefaultExport function for compatibility with non-harmony modules
/******/    __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;
/******/    };
/******/
/******/    // Object.prototype.hasOwnProperty.call
/******/    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";
/******/
/******/    // Load entry module and return exports
/******/    return __webpack_require__(__webpack_require__.s = 0);
// ......

webpack內(nèi)部定義了一個(gè) webpack_require 的方法,這個(gè)方法的實(shí)質(zhì)很簡(jiǎn)單:

多模塊間存在簡(jiǎn)單依賴
例如 moduleB.js 依賴于 moduleA.js 文件。

// /webpack/bundles/simpleDependencies/moduleA.js
module.exports = window.printA = function printA() {
    console.log(`This is module A!`);
}
// /webpack/bundles/simpleDependencies/moduleB.js
const printA = require('./moduleA');

module.exports = window.printB = function printB() {
    printA();
    console.log('This is module B!');
}

將配置文件中的入口更改為

// /webpack/bundles/simpleDependencies/webpack.config.js
// ...
main: './moduleB.js'
// ...

再次打包,我們獲得如下代碼:

// /webpack/bundles/simpleDependencies/dist/bundle.js
/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};
/******/
/******/    // The require function
/******/    function __webpack_require__(moduleId) {
/******/
/******/        // Check if module is in cache
/******/        if(installedModules[moduleId]) {
/******/            return installedModules[moduleId].exports;
/******/        }
/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            i: moduleId,
/******/            l: false,
/******/            exports: {}
/******/        };
/******/
/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/        // Flag the module as loaded
/******/        module.l = true;
/******/
/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/
/******/
/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;
/******/
/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;
/******/
/******/    // define getter function for harmony exports
/******/    __webpack_require__.d = function(exports, name, getter) {
/******/        if(!__webpack_require__.o(exports, name)) {
/******/            Object.defineProperty(exports, name, {
/******/                configurable: false,
/******/                enumerable: true,
/******/                get: getter
/******/            });
/******/        }
/******/    };
/******/
/******/    // getDefaultExport function for compatibility with non-harmony modules
/******/    __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;
/******/    };
/******/
/******/    // Object.prototype.hasOwnProperty.call
/******/    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";
/******/
/******/    // Load entry module and return exports
/******/    return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

const printA = __webpack_require__(1);

module.exports = window.printB = function printB() {
    printA();
    console.log('This is module B!');
}

/***/ }),
/* 1 */
/***/ (function(module, exports) {

module.exports = window.printA = function printA() {
    console.log(`This is module A!`);
}

/***/ })
/******/ ]);

我們可以發(fā)現(xiàn)這塊有點(diǎn)變化:

/* 0 */
/***/ (function(module, exports, __webpack_require__) {

const printA = __webpack_require__(1);

module.exports = window.printB = function printB() {
    printA();
    console.log('This is module B!');
}

在 moduleB.js 中,需要依賴 moduleA ,因而需要先執(zhí)行 __webpack_require(1) 拿到模塊A后,再進(jìn)行下一步。

多入口
需要注意,打包的文件中moudleId是不會(huì)重復(fù)的,如果有兩個(gè)入口文件的情況,則入口模塊id都為0,其他依賴模塊id不重復(fù)。我們創(chuàng)建如下幾個(gè)文件,其中 index0.js 依賴于 common.js 與 dependency.js ,而 index1.js 依賴于 index0.js 和 common.js 兩個(gè)文件。

// /webpack/bundles/multi/common.js
module.exports = function() {
    console.log('This is common module!');
}
// /webpack/bundles/multi/dependency .js
module.exports = function() {
    console.log('This is dependency module!');
}
// /webpack/bundles/multi/index0.js
const common = require('./common');
const dependency = require('./dependency');

module.exports = window.print0 = function() {
    common();
    dependency();
    console.log('This is module 0!');
}
// /webpack/bundles/multi/index1.js
const common = require('./common');
const index0 = require('./index0');

module.exports = window.print1 = function() {
    common();
    console.log('This is module 1!');
}

修改 webpack.config.js 中的文件入口:

// /webpack/bundles/multi/webpack.config.js
// ...
entry: {
    index0: './index0.js',
    index1: './index1.js'
},
output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].bundle.js'
},
// ...

打包后的文件:

// /webpack/bundles/multi/dist/index0.bundle.js
/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};
/******/
/******/    // The require function
/******/    function __webpack_require__(moduleId) {
/******/
/******/        // Check if module is in cache
/******/        if(installedModules[moduleId]) {
/******/            return installedModules[moduleId].exports;
/******/        }
/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            i: moduleId,
/******/            l: false,
/******/            exports: {}
/******/        };
/******/
/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/        // Flag the module as loaded
/******/        module.l = true;
/******/
/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/
/******/
/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;
/******/
/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;
/******/
/******/    // define getter function for harmony exports
/******/    __webpack_require__.d = function(exports, name, getter) {
/******/        if(!__webpack_require__.o(exports, name)) {
/******/            Object.defineProperty(exports, name, {
/******/                configurable: false,
/******/                enumerable: true,
/******/                get: getter
/******/            });
/******/        }
/******/    };
/******/
/******/    // getDefaultExport function for compatibility with non-harmony modules
/******/    __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;
/******/    };
/******/
/******/    // Object.prototype.hasOwnProperty.call
/******/    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";
/******/
/******/    // Load entry module and return exports
/******/    return __webpack_require__(__webpack_require__.s = 1);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {

module.exports = function() {
    console.log('This is common module!');
}

/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

const common = __webpack_require__(0);
const dependency = __webpack_require__(2);

module.exports = window.print0 = function() {
    common();
    dependency();
    console.log('This is module 0!');
}

/***/ }),
/* 2 */
/***/ (function(module, exports) {

module.exports = function() {
    console.log('This is dependency module!');
}

/***/ })
/******/ ]);
// /webpack/bundles/multi/dist/index1.bundle.js
/******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};
/******/
/******/    // The require function
/******/    function __webpack_require__(moduleId) {
/******/
/******/        // Check if module is in cache
/******/        if(installedModules[moduleId]) {
/******/            return installedModules[moduleId].exports;
/******/        }
/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            i: moduleId,
/******/            l: false,
/******/            exports: {}
/******/        };
/******/
/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/        // Flag the module as loaded
/******/        module.l = true;
/******/
/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/
/******/
/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;
/******/
/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;
/******/
/******/    // define getter function for harmony exports
/******/    __webpack_require__.d = function(exports, name, getter) {
/******/        if(!__webpack_require__.o(exports, name)) {
/******/            Object.defineProperty(exports, name, {
/******/                configurable: false,
/******/                enumerable: true,
/******/                get: getter
/******/            });
/******/        }
/******/    };
/******/
/******/    // getDefaultExport function for compatibility with non-harmony modules
/******/    __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;
/******/    };
/******/
/******/    // Object.prototype.hasOwnProperty.call
/******/    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";
/******/
/******/    // Load entry module and return exports
/******/    return __webpack_require__(__webpack_require__.s = 3);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {

module.exports = function() {
    console.log('This is common module!');
}

/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

const common = __webpack_require__(0);
const dependency = __webpack_require__(2);

module.exports = window.print0 = function() {
    common();
    dependency();
    console.log('This is module 0!');
}

/***/ }),
/* 2 */
/***/ (function(module, exports) {

module.exports = function() {
    console.log('This is dependency module!');
}

/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {

const common = __webpack_require__(0);
const index0 = __webpack_require__(1);

module.exports = window.print1 = function() {
    common();
    console.log('This is module 1!');
}

/***/ })
/******/ ]);

顯然,在未使用 CommonsChunkPlugin 這個(gè)插件之前,這兩個(gè)文件是存在重復(fù)代碼的。也就是每個(gè)入口都會(huì)獨(dú)立進(jìn)行打包。
我們看如果添加了 CommonsChunkPlugin 這個(gè)插件后的情況(修改 webpack.config.js):

// /webpack/bundles/CommonsChunkPlugin/webpack.config.js
plugins: [
    // ...
    new webpack.optimize.CommonsChunkPlugin({
        name: 'common',
        filename: 'common.js'
    })
]

這樣一來(lái)會(huì)生成三個(gè)文件,index0.bundle.js ,index1.bundel.js 以及 common.js:

// /webpack/bundles/CommonsChunkPlugin/dist/common.js
/******/ (function(modules) { // webpackBootstrap
/******/    // install a JSONP callback for chunk loading
/******/    var parentJsonpFunction = window["webpackJsonp"];
/******/    window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
/******/        // add "moreModules" to the modules object,
/******/        // then flag all "chunkIds" as loaded and fire callback
/******/        var moduleId, chunkId, i = 0, resolves = [], result;
/******/        for(;i < chunkIds.length; i++) {
/******/            chunkId = chunkIds[i];
/******/            if(installedChunks[chunkId]) {
/******/                resolves.push(installedChunks[chunkId][0]);
/******/            }
/******/            installedChunks[chunkId] = 0;
/******/        }
/******/        for(moduleId in moreModules) {
/******/            if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/                modules[moduleId] = moreModules[moduleId];
/******/            }
/******/        }
/******/        if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);
/******/        while(resolves.length) {
/******/            resolves.shift()();
/******/        }
/******/        if(executeModules) {
/******/            for(i=0; i < executeModules.length; i++) {
/******/                result = __webpack_require__(__webpack_require__.s = executeModules[i]);
/******/            }
/******/        }
/******/        return result;
/******/    };
/******/
/******/    // The module cache
/******/    var installedModules = {};
/******/
/******/    // objects to store loaded and loading chunks
/******/    var installedChunks = {
/******/        2: 0
/******/    };
/******/
/******/    // The require function
/******/    function __webpack_require__(moduleId) {
/******/
/******/        // Check if module is in cache
/******/        if(installedModules[moduleId]) {
/******/            return installedModules[moduleId].exports;
/******/        }
/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            i: moduleId,
/******/            l: false,
/******/            exports: {}
/******/        };
/******/
/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/        // Flag the module as loaded
/******/        module.l = true;
/******/
/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/
/******/
/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;
/******/
/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;
/******/
/******/    // define getter function for harmony exports
/******/    __webpack_require__.d = function(exports, name, getter) {
/******/        if(!__webpack_require__.o(exports, name)) {
/******/            Object.defineProperty(exports, name, {
/******/                configurable: false,
/******/                enumerable: true,
/******/                get: getter
/******/            });
/******/        }
/******/    };
/******/
/******/    // getDefaultExport function for compatibility with non-harmony modules
/******/    __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;
/******/    };
/******/
/******/    // Object.prototype.hasOwnProperty.call
/******/    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";
/******/
/******/    // on error function for async loading
/******/    __webpack_require__.oe = function(err) { console.error(err); throw err; };
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {

module.exports = function() {
    console.log('This is common module!');
}

/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

const common = __webpack_require__(0);
const dependency = __webpack_require__(2);

module.exports = window.print0 = function() {
    common();
    dependency();
    console.log('This is module 0!');
}

/***/ }),
/* 2 */
/***/ (function(module, exports) {

module.exports = function() {
    console.log('This is dependency module!');
}

/***/ })
/******/ ]);

common.js 已經(jīng)包含了所有的公共方法,并且在瀏覽器 window 對(duì)象中創(chuàng)建了一個(gè)名為 webpackJsonp 的方法。

// /webpack/bundles/CommonsChunkPlugin/dist/common.js
// ...
/******/    var parentJsonpFunction = window["webpackJsonp"];
/******/    window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
/******/        // add "moreModules" to the modules object,
/******/        // then flag all "chunkIds" as loaded and fire callback
/******/        var moduleId, chunkId, i = 0, resolves = [], result;
/******/        for(;i < chunkIds.length; i++) {
/******/            chunkId = chunkIds[i];
/******/            if(installedChunks[chunkId]) {
/******/                resolves.push(installedChunks[chunkId][0]);
/******/            }
/******/            installedChunks[chunkId] = 0;
/******/        }
/******/        for(moduleId in moreModules) {
/******/            if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/                modules[moduleId] = moreModules[moduleId];
/******/            }
/******/        }
/******/        if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);
/******/        while(resolves.length) {
/******/            resolves.shift()();
/******/        }
/******/        if(executeModules) {
/******/            for(i=0; i < executeModules.length; i++) {
/******/                result = __webpack_require__(__webpack_require__.s = executeModules[i]);
/******/            }
/******/        }
/******/        return result;
/******/    };
// ...
/******/    // objects to store loaded and loading chunks
/******/    var installedChunks = {
/******/        2: 0
/******/    };
// ...

這個(gè)方法與 __webpack_require__ 較為類似,同樣也是將模塊緩存進(jìn)來(lái)。只不過(guò) webpack 會(huì)預(yù)先抽取公共模塊,先將其緩存進(jìn)來(lái),而后可以在其他的 bundle.js 中使用 webpackJsonp 方法進(jìn)行模塊加載。

// /webpack/bundles/CommonsChunkPlugin/dist/index0.bundle.js
webpackJsonp([1],[],[1]);
// /webpack/bundles/CommonsChunkPlugin/dist/index1.bundle.js
webpackJsonp([0],{

/***/ 3:
/***/ (function(module, exports, __webpack_require__) {

const common = __webpack_require__(0);
const index0 = __webpack_require__(1);

module.exports = window.print1 = function() {
    common();
    console.log('This is module 1!');
}

/***/ })

},[3]);

Webpack核心架構(gòu) —— Tapable

github上將webpack源碼克隆至本地,我們可以先了解到 webpack 的一個(gè)整體流程:

lib/webpack.js中返回一個(gè)compiler對(duì)象,并調(diào)用了compiler.run()lib/Compiler.js中,run方法觸發(fā)了before-run、run兩個(gè)事件,然后通過(guò)readRecords讀取文件,通過(guò)compile進(jìn)行打包,打包后觸發(fā)before-compile、compile、make等事件;compile是主要流程,該方法中實(shí)例化了一個(gè)Compilation類,并調(diào)用了其finish及seal方法。lib/Compilation.js中定義了finish及seal方法,還有一個(gè)重要方法addEntry。這個(gè)方法通過(guò)調(diào)用其私有方法_addModuleChain完成了兩件事:根據(jù)模塊的類型獲取對(duì)應(yīng)的模塊工廠并創(chuàng)建模塊;構(gòu)建模塊。lib/Compiler.js中沒(méi)有顯式調(diào)用addEntry,而是觸發(fā)make事件,lib/DllEntryPlugin.js為一個(gè)監(jiān)聽(tīng)make事件的插件,在回調(diào)函數(shù)中調(diào)用了addEntry。

具體分析_addModuleChain,其完成的第二件事構(gòu)建模塊又可以分為三部分:

調(diào)用loader處理模塊之間的依賴。將loader處理后的文件通過(guò)acorn抽象成抽象語(yǔ)法樹(shù)AST。遍歷AST,構(gòu)建該模塊的所有依賴。

具體看lib/webpack.js這個(gè)文件,此文件為webpack的入口文件。

const webpack = (options, callback) => {
    const webpackOptionsValidationErrors = validateSchema(
        webpackOptionsSchema,
        options
    );
    if (webpackOptionsValidationErrors.length) {
        throw new WebpackOptionsValidationError(webpackOptionsValidationErrors);
    }
    let compiler;
    if (Array.isArray(options)) {
        compiler = new MultiCompiler(options.map(options => webpack(options)));
    } else if (typeof options === "object") {
        options = new WebpackOptionsDefaulter().process(options);

        compiler = new Compiler(options.context);
        compiler.options = options;
        new NodeEnvironmentPlugin().apply(compiler);
        if (options.plugins && Array.isArray(options.plugins)) {
            for (const plugin of options.plugins) {
                plugin.apply(compiler);
            }
        }
        compiler.hooks.environment.call();
        compiler.hooks.afterEnvironment.call();
        compiler.options = new WebpackOptionsApply().process(options, compiler);
    } else {
        throw new Error("Invalid argument: options");
    }
    if (callback) {
        if (typeof callback !== "function")
            throw new Error("Invalid argument: callback");
        if (
            options.watch === true ||
            (Array.isArray(options) && options.some(o => o.watch))
        ) {
            const watchOptions = Array.isArray(options)
                ? options.map(o => o.watchOptions || {})
                : options.watchOptions || {};
            return compiler.watch(watchOptions, callback);
        }
        compiler.run(callback);
    }
    return compiler;
};

lib/webpack.js中流程大致如下:

參數(shù)驗(yàn)證創(chuàng)建Compiler(編譯器)對(duì)象注冊(cè)并執(zhí)行NodeEnvironmentPlugin執(zhí)行鉤子environment里的方法執(zhí)行鉤子afterEnvironment里的方法注冊(cè)并執(zhí)行各種插件將compiler向外導(dǎo)出

顯然,Compiler是我們需要深究的一個(gè)部分,因?yàn)?webpack 最終向外部返回也就是這個(gè) Compiler 實(shí)例。大致了解下Compiler的實(shí)現(xiàn):

class Compiler extends Tapable {
    constructor(context) {
        super();
        this.hooks = {
            // ...
        };
        this._pluginCompat.tap("Compiler", options => {
            // ...
        });
        // ... 
        this.resolvers = {
            normal: {
                // ...
            },
            loader: {
                // ...
            },
            context: {
                // ...
            }
        };
        // ...
    }
    watch(watchOptions, handler) {
        // ...
    }
    run(callback) {
        // ...
    }
    runAsChild(callback) {
        // ...
    }
    purgeInputFileSystem() {
        // ...
    }
    emitAssets(compilation, callback) {
        // ...
    }
    emitRecords(callback) {
        // ...
    }
    readRecords(callback) {
        // ...
    }
    createChildCompiler(
        compilation,
        compilerName,
        compilerIndex,
        outputOptions,
        plugins
    ) {
        // ...
    }
    isChild() {
        // ...
    }
    createCompilation() {
        // ...
    }
    newCompilation(params) {
        // ...
    }
    createNormalModuleFactory() {
        // ...
    }
    createContextModuleFactory() {
        // ...
    }
    newCompilationParams() {
        // ...
    }
    compile(callback) {
        // ...
    }
}

Compiler繼承自Tapable,在其構(gòu)造方法中,定義了一些事件鉤子(hooks)、一些變量以及一些方法。這些變量以及方法目前看來(lái)還是非常抽象的,所以我們有必要去了解下Tapable的實(shí)現(xiàn)。

Tapable的Github主頁(yè) 對(duì)Tapable的介紹如下:

The tapable packages exposes many Hook classes, which can be used to create hooks for plugins.

實(shí)際上,webpack基于事件流機(jī)制,它的工作流程就是將各個(gè)插件串聯(lián)起來(lái),而實(shí)現(xiàn)這一切的核心就是Tapable,webpack中最核心的負(fù)責(zé)編譯的Compiler和負(fù)責(zé)創(chuàng)建bundles的Compilation都是Tapable的實(shí)例。Tapable向外暴露許多的鉤子類,這些類可以很方便地為插件創(chuàng)建事件鉤子。Tapable中定義了如下幾種鉤子類:

SyncHookSyncBailHookSyncWaterfallHookSyncLoopHookAsyncParallelHookAsyncParallelBailHookAsyncSeriesHookAsyncSeriesBailHookAsyncSeriesWaterfallHook

所有鉤子類的構(gòu)造函數(shù)都接收一個(gè)可選的參數(shù),這個(gè)參數(shù)是一個(gè)由字符串參數(shù)組成的數(shù)組,如下:

const hook = new SyncHook(["arg1", "arg2", "arg3"]);

鉤子概覽

Tapable的鉤子分為兩類,同步和異步,其中異步又分為并行和串行:

每種鉤子都有各自的使用方式,如下表:

序號(hào)鉤子名執(zhí)行方式使用要點(diǎn)1SyncHook同步串行不關(guān)心監(jiān)聽(tīng)函數(shù)的返回值2SyncBailHook同步串行只要監(jiān)聽(tīng)函數(shù)中有一個(gè)函數(shù)的返回值不為 null,則跳過(guò)剩下所有的邏輯3SyncWaterfallHook同步串行上一個(gè)監(jiān)聽(tīng)函數(shù)的返回值可以傳給下一個(gè)監(jiān)聽(tīng)函數(shù)4SyncLoopHook同步循環(huán)當(dāng)監(jiān)聽(tīng)函數(shù)被觸發(fā)的時(shí)候,如果該監(jiān)聽(tīng)函數(shù)返回true時(shí)則這個(gè)監(jiān)聽(tīng)函數(shù)會(huì)反復(fù)執(zhí)行,如果返回 undefined 則表示退出循環(huán)5AsyncParallelHook異步并發(fā)不關(guān)心監(jiān)聽(tīng)函數(shù)的返回值6AsyncParallelBailHook異步并發(fā)只要監(jiān)聽(tīng)函數(shù)的返回值不為 null,就會(huì)忽略后面的監(jiān)聽(tīng)函數(shù)執(zhí)行,直接跳躍到callAsync等觸發(fā)函數(shù)綁定的回調(diào)函數(shù),然后執(zhí)行這個(gè)被綁定的回調(diào)函數(shù)7AsyncSeriesHook異步串行不關(guān)系callback()的參數(shù)8AsyncSeriesBailHook異步串行callback()的參數(shù)不為null,就會(huì)直接執(zhí)行callAsync等觸發(fā)函數(shù)綁定的回調(diào)函數(shù)9AsyncSeriesWaterfallHook異步串行上一個(gè)監(jiān)聽(tīng)函數(shù)的中的callback(err, data)的第二個(gè)參數(shù),可以作為下一個(gè)監(jiān)聽(tīng)函數(shù)的參數(shù)

Sync鉤子

同步串行
(1) SyncHook
不關(guān)心監(jiān)聽(tīng)函數(shù)的返回值

使用
const { SyncHook } = require("tapable");
let queue = new SyncHook(['name']); //所有的構(gòu)造函數(shù)都接收一個(gè)可選的參數(shù),這個(gè)參數(shù)是一個(gè)字符串的數(shù)組。

// 訂閱
queue.tap('1', function (name, name2) {// tap 的第一個(gè)參數(shù)是用來(lái)標(biāo)識(shí)訂閱的函數(shù)的
    console.log(name, name2, 1);
    return '1'
});
queue.tap('2', function (name) {
    console.log(name, 2);
});
queue.tap('3', function (name) {
    console.log(name, 3);
});

// 發(fā)布
queue.call('webpack', 'webpack-cli');// 發(fā)布的時(shí)候觸發(fā)訂閱的函數(shù) 同時(shí)傳入?yún)?shù)

// 執(zhí)行結(jié)果:
/*
webpack undefined 1 // 傳入的參數(shù)需要和new實(shí)例的時(shí)候保持一致,否則獲取不到多傳的參數(shù)
webpack 2
webpack 3
*/
原理
class SyncHook_MY{
    constructor(){
        this.hooks = [];
    }

    // 訂閱
    tap(name, fn){
        this.hooks.push(fn);
    }

    // 發(fā)布
    call(){
        this.hooks.forEach(hook => hook(...arguments));
    }
}

(2) SyncBailHook
只要監(jiān)聽(tīng)函數(shù)中有一個(gè)函數(shù)的返回值不為 null,則跳過(guò)剩下所有的邏輯

使用
const {
    SyncBailHook
} = require("tapable");

let queue = new SyncBailHook(['name']); 

queue.tap('1', function (name) {
    console.log(name, 1);
});
queue.tap('2', function (name) {
    console.log(name, 2);
    return 'wrong'
});
queue.tap('3', function (name) {
    console.log(name, 3);
});

queue.call('webpack');

// 執(zhí)行結(jié)果:
/* 
webpack 1
webpack 2
*/
原理
class SyncBailHook_MY {
    constructor() {
        this.hooks = [];
    }

    // 訂閱
    tap(name, fn) {
        this.hooks.push(fn);
    }

    // 發(fā)布
    call() {
        for (let i = 0, l = this.hooks.length; i < l; i++) {
            let hook = this.hooks[i];
            let result = hook(...arguments);
            if (result) {
                break;
            }
        }
    }
}

(3) SyncWaterfallHook
上一個(gè)監(jiān)聽(tīng)函數(shù)的返回值可以傳給下一個(gè)監(jiān)聽(tīng)函數(shù)

使用
const {
    SyncWaterfallHook
} = require("tapable");

let queue = new SyncWaterfallHook(['name']);

// 上一個(gè)函數(shù)的返回值可以傳給下一個(gè)函數(shù)
queue.tap('1', function (name) {
    console.log(name, 1);
    return 1;
});
queue.tap('2', function (data) {
    console.log(data, 2);
    return 2;
});
queue.tap('3', function (data) {
    console.log(data, 3);
});

queue.call('webpack');

// 執(zhí)行結(jié)果:
/* 
webpack 1
1 2
2 3
*/
原理
class SyncWaterfallHook_MY{
    constructor(){
        this.hooks = [];
    }
  // 訂閱
    tap(name, fn){
        this.hooks.push(fn);
    }

    // 發(fā)布
    call(){
        let result = null;
        for(let i = 0, l = this.hooks.length; i < l; i++) {
            let hook = this.hooks[i];
            result = i == 0 ? hook(...arguments): hook(result); 
        }
    }
}

(4) SyncLoopHook
當(dāng)監(jiān)聽(tīng)函數(shù)被觸發(fā)的時(shí)候,如果該監(jiān)聽(tīng)函數(shù)返回true時(shí)則這個(gè)監(jiān)聽(tīng)函數(shù)會(huì)反復(fù)執(zhí)行,如果返回 undefined 則表示退出循環(huán)。

使用
const {
    SyncLoopHook
} = require("tapable");

let queue = new SyncLoopHook(['name']); 

let count = 3;
queue.tap('1', function (name) {
    console.log('count: ', count--);
    if (count > 0) {
        return true;
    }
    return;
});

queue.call('webpack');

// 執(zhí)行結(jié)果:
/* 
count:  3
count:  2
count:  1
*/
原理
class SyncLoopHook_MY {
    constructor() {
        this.hook = null;
    }

    // 訂閱
    tap(name, fn) {
        this.hook = fn;
    }

    // 發(fā)布
    call() {
        let result;
        do {
            result = this.hook(...arguments);
        } while (result)
    }
}

Async鉤子

異步并行
(1) AsyncParallelHook
不關(guān)心監(jiān)聽(tīng)函數(shù)的返回值。有三種注冊(cè)/發(fā)布的模式,如下:

異步訂閱調(diào)用方法tapcallAsynctapAsynccallAsynctapPromisepromiseusage - tap
const {
    AsyncParallelHook
} = require("tapable");

let queue1 = new AsyncParallelHook(['name']);
console.time('cost');
queue1.tap('1', function (name) {
    console.log(name, 1);
});
queue1.tap('2', function (name) {
    console.log(name, 2);
});
queue1.tap('3', function (name) {
    console.log(name, 3);
});
queue1.callAsync('webpack', err => {
    console.timeEnd('cost');
});

// 執(zhí)行結(jié)果
/* 
webpack 1
webpack 2
webpack 3
cost: 4.520ms
*/
usage - tapAsync
let queue2 = new AsyncParallelHook(['name']);
console.time('cost1');
queue2.tapAsync('1', function (name, cb) {
    setTimeout(() => {
        console.log(name, 1);
        cb();
    }, 1000);
});
queue2.tapAsync('2', function (name, cb) {
    setTimeout(() => {
        console.log(name, 2);
        cb();
    }, 2000);
});
queue2.tapAsync('3', function (name, cb) {
    setTimeout(() => {
        console.log(name, 3);
        cb();
    }, 3000);
});

queue2.callAsync('webpack', () => {
    console.log('over');
    console.timeEnd('cost1');
});

// 執(zhí)行結(jié)果
/* 
webpack 1
webpack 2
webpack 3
over
time: 3004.411ms
*/
usage - promise
let queue3 = new AsyncParallelHook(['name']);
console.time('cost3');
queue3.tapPromise('1', function (name, cb) {
   return new Promise(function (resolve, reject) {
       setTimeout(() => {
           console.log(name, 1);
           resolve();
       }, 1000);
   });
});

queue3.tapPromise('1', function (name, cb) {
   return new Promise(function (resolve, reject) {
       setTimeout(() => {
           console.log(name, 2);
           resolve();
       }, 2000);
   });
});

queue3.tapPromise('1', function (name, cb) {
   return new Promise(function (resolve, reject) {
       setTimeout(() => {
           console.log(name, 3);
           resolve();
       }, 3000);
   });
});

queue3.promise('webpack')
   .then(() => {
       console.log('over');
       console.timeEnd('cost3');
   }, () => {
       console.log('error');
       console.timeEnd('cost3');
   });
/* 
webpack 1
webpack 2
webpack 3
over
cost3: 3007.925ms
*/

異步串行
(1) AsyncSeriesHook
不關(guān)心callback()的參數(shù)。

usage - tap
const {
    AsyncSeriesHook
} = require("tapable");

// tap
let queue1 = new AsyncSeriesHook(['name']);
console.time('cost1');
queue1.tap('1', function (name) {
    console.log(1);
    return "Wrong";
});
queue1.tap('2', function (name) {
    console.log(2);
});
queue1.tap('3', function (name) {
    console.log(3);
});
queue1.callAsync('zfpx', err => {
    console.log(err);
    console.timeEnd('cost1');
});
// 執(zhí)行結(jié)果
/* 
1
2
3
undefined
cost1: 3.933ms
*/
usage - tapAsync
let queue2 = new AsyncSeriesHook(['name']);
console.time('cost2');
queue2.tapAsync('1', function (name, cb) {
    setTimeout(() => {
        console.log(name, 1);
        cb();
    }, 1000);
});
queue2.tapAsync('2', function (name, cb) {
    setTimeout(() => {
        console.log(name, 2);
        cb();
    }, 2000);
});
queue2.tapAsync('3', function (name, cb) {
    setTimeout(() => {
        console.log(name, 3);
        cb();
    }, 3000);
});

queue2.callAsync('webpack', (err) => {
    console.log(err);
    console.log('over');
    console.timeEnd('cost2');
}); 
// 執(zhí)行結(jié)果
/* 
webpack 1
webpack 2
webpack 3
undefined
over
cost2: 6019.621ms
*/
usage - promise
let queue3 = new AsyncSeriesHook(['name']);
console.time('cost3');
queue3.tapPromise('1',function(name){
   return new Promise(function(resolve){
       setTimeout(function(){
           console.log(name, 1);
           resolve();
       },1000)
   });
});
queue3.tapPromise('2',function(name,callback){
    return new Promise(function(resolve){
        setTimeout(function(){
            console.log(name, 2);
            resolve();
        },2000)
    });
});
queue3.tapPromise('3',function(name,callback){
    return new Promise(function(resolve){
        setTimeout(function(){
            console.log(name, 3);
            resolve();
        },3000)
    });
});
queue3.promise('webapck').then(err=>{
    console.log(err);
    console.timeEnd('cost3');
});

// 執(zhí)行結(jié)果
/* 
webapck 1
webapck 2
webapck 3
undefined
cost3: 6021.817ms
*/
原理
class AsyncSeriesHook_MY {
    constructor() {
        this.hooks = [];
    }

    tapAsync(name, fn) {
        this.hooks.push(fn);
    }

    callAsync() {
        var slef = this;
        var args = Array.from(arguments);
        let done = args.pop();
        let idx = 0;

        function next(err) {
            // 如果next的參數(shù)有值,就直接跳躍到 執(zhí)行callAsync的回調(diào)函數(shù)
            if (err) return done(err);
            let fn = slef.hooks[idx++];
            fn ? fn(...args, next) : done();
        }
        next();
    }
}

(2) AsyncSeriesBailHook
callback()的參數(shù)不為null,就會(huì)直接執(zhí)行callAsync等觸發(fā)函數(shù)綁定的回調(diào)函數(shù)。

usage - tap
const {
    AsyncSeriesBailHook
} = require("tapable");

// tap
let queue1 = new AsyncSeriesBailHook(['name']);
console.time('cost1');
queue1.tap('1', function (name) {
    console.log(1);
    return "Wrong";
});
queue1.tap('2', function (name) {
    console.log(2);
});
queue1.tap('3', function (name) {
    console.log(3);
});
queue1.callAsync('webpack', err => {
    console.log(err);
    console.timeEnd('cost1');
});

// 執(zhí)行結(jié)果:
/* 
1
null
cost1: 3.979ms
*/
usage - tapAsync
let queue2 = new AsyncSeriesBailHook(['name']);
console.time('cost2');
queue2.tapAsync('1', function (name, callback) {
    setTimeout(function () {
        console.log(name, 1);
        callback();
    }, 1000)
});
queue2.tapAsync('2', function (name, callback) {
    setTimeout(function () {
        console.log(name, 2);
        callback('wrong');
    }, 2000)
});
queue2.tapAsync('3', function (name, callback) {
    setTimeout(function () {
        console.log(name, 3);
        callback();
    }, 3000)
});
queue2.callAsync('webpack', err => {
    console.log(err);
    console.log('over');
    console.timeEnd('cost2');
});
// 執(zhí)行結(jié)果

/* 
webpack 1
webpack 2
wrong
over
cost2: 3014.616ms
*/
usage - promise
let queue3 = new AsyncSeriesBailHook(['name']);
console.time('cost3');
queue3.tapPromise('1', function (name) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log(name, 1);
            resolve();
        }, 1000)
    });
});
queue3.tapPromise('2', function (name, callback) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log(name, 2);
            reject();
        }, 2000)
    });
});
queue3.tapPromise('3', function (name, callback) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            console.log(name, 3);
            resolve();
        }, 3000)
    });
});
queue3.promise('webpack').then(err => {
    console.log(err);
    console.log('over');
    console.timeEnd('cost3');
}, err => {
    console.log(err);
    console.log('error');
    console.timeEnd('cost3');
});
// 執(zhí)行結(jié)果:
/* 
webpack 1
webpack 2
undefined
error
cost3: 3017.608ms
*/

(3) AsyncSeriesWaterfallHook
上一個(gè)監(jiān)聽(tīng)函數(shù)的中的callback(err, data)的第二個(gè)參數(shù),可以作為下一個(gè)監(jiān)聽(tīng)函數(shù)的參數(shù)

usage - tap
const {
    AsyncSeriesWaterfallHook
} = require("tapable");

// tap
let queue1 = new AsyncSeriesWaterfallHook(['name']);
console.time('cost1');
queue1.tap('1', function (name) {
    console.log(name, 1);
    return 'lily'
});
queue1.tap('2', function (data) {
    console.log(2, data);
    return 'Tom';
});
queue1.tap('3', function (data) {
    console.log(3, data);
});
queue1.callAsync('webpack', err => {
    console.log(err);
    console.log('over');
    console.timeEnd('cost1');
});

// 執(zhí)行結(jié)果:
/* 
webpack 1
2 'lily'
3 'Tom'
null
over
cost1: 5.525ms
*/
usage - tapAsync
let queue2 = new AsyncSeriesWaterfallHook(['name']);
console.time('cost2');
queue2.tapAsync('1', function (name, callback) {
    setTimeout(function () {
        console.log('1: ', name);
        callback(null, 2);
    }, 1000)
});
queue2.tapAsync('2', function (data, callback) {
    setTimeout(function () {
        console.log('2: ', data);
        callback(null, 3);
    }, 2000)
});
queue2.tapAsync('3', function (data, callback) {
    setTimeout(function () {
        console.log('3: ', data);
        callback(null, 3);
    }, 3000)
});
queue2.callAsync('webpack', err => {
    console.log(err);
    console.log('over');
    console.timeEnd('cost2');
});
// 執(zhí)行結(jié)果:
/* 
1:  webpack
2:  2
3:  3
null
over
cost2: 6016.889ms
*/
usage - promise
let queue3 = new AsyncSeriesWaterfallHook(['name']);
console.time('cost3');
queue3.tapPromise('1', function (name) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log('1:', name);
            resolve('1');
        }, 1000)
    });
});
queue3.tapPromise('2', function (data, callback) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            console.log('2:', data);
            resolve('2');
        }, 2000)
    });
});
queue3.tapPromise('3', function (data, callback) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            console.log('3:', data);
            resolve('over');
        }, 3000)
    });
});
queue3.promise('webpack').then(err => {
    console.log(err);
    console.timeEnd('cost3');
}, err => {
    console.log(err);
    console.timeEnd('cost3');
});
// 執(zhí)行結(jié)果:
/* 
1: webpack
2: 1
3: 2
over
cost3: 6016.703ms
*/
原理
class AsyncSeriesWaterfallHook_MY {
    constructor() {
        this.hooks = [];
    }

    tapAsync(name, fn) {
        this.hooks.push(fn);
    }

    callAsync() {
        let self = this;
        var args = Array.from(arguments);

        let done = args.pop();
        console.log(args);
        let idx = 0;
        let result = null;

        function next(err, data) {
            if (idx >= self.hooks.length) return done();
            if (err) {
                return done(err);
            }
            let fn = self.hooks[idx++];
            if (idx == 1) {

                fn(...args, next);
            } else {
                fn(data, next);
            }
        }
        next();
    }
}

Tapable事件流

webpack中的事件歸納如下,這些事件出現(xiàn)的順序固定,但不一定每次打包所有事件都觸發(fā):

類型名字事件名[C]applyPluginsBailResultentry-option[A]applyPluginsafter-plugins[A]applyPluginsafter-resolvers[A]applyPluginsenvironment[A]applyPluginsafter-environment[D]applyPluginsAsyncSeriesrun[A]applyPluginsnormal-module-factory[A]applyPluginscontext-module-factory[A]applyPluginscompile[A]applyPluginsthis-compilation[A]applyPluginscompilation[F]applyPluginsParallelmake[E]applyPluginsAsyncWaterfallbefore-resolve[B]applyPluginsWaterfallfactory[B]applyPluginsWaterfallresolver[A]applyPluginsresolve[A]applyPluginsresolve-step[G]applyPluginsParallelBailResultfile[G]applyPluginsParallelBailResultdirectory[A]applyPluginsresolve-step[G]applyPluginsParallelBailResultresult[E]applyPluginsAsyncWaterfallafter-resolve[C]applyPluginsBailResultcreate-module[B]applyPluginsWaterfallmodule[A]applyPluginsbuild-module[A]applyPluginsnormal-module-loader[C]applyPluginsBailResultprogram[C]applyPluginsBailResultstatement[C]applyPluginsBailResultevaluate CallExpression[C]applyPluginsBailResultvar data[C]applyPluginsBailResultevaluate Identifier[C]applyPluginsBailResultevaluate Identifier require[C]applyPluginsBailResultcall require[C]applyPluginsBailResultevaluate Literal[C]applyPluginsBailResultcall require:amd:array[C]applyPluginsBailResultevaluate Literal[C]applyPluginsBailResultcall require:commonjs:item[C]applyPluginsBailResultstatement[C]applyPluginsBailResultevaluate MemberExpression[C]applyPluginsBailResultevaluate Identifier console.log[C]applyPluginsBailResultcall console.log[C]applyPluginsBailResultexpression console.log[C]applyPluginsBailResultexpression console[A]applyPluginssucceed-module[E]applyPluginsAsyncWaterfallbefore-resolve[B]applyPluginsWaterfallfactory[A]applyPluginsbuild-module[A]applyPluginssucceed-module[A]applyPluginsseal[A]applyPluginsoptimize[A]applyPluginsoptimize-modules[A]applyPluginsafter-optimize-modules[A]applyPluginsoptimize-chunks[A]applyPluginsafter-optimize-chunks[D]applyPluginsAsyncSeriesoptimize-tree[A]applyPluginsafter-optimize-tree[C]applyPluginsBailResultshould-record[A]applyPluginsrevive-modules[A]applyPluginsoptimize-module-order[A]applyPluginsbefore-module-ids[A]applyPluginsoptimize-module-ids[A]applyPluginsafter-optimize-module-ids[A]applyPluginsrecord-modules[A]applyPluginsrevive-chunks[A]applyPluginsoptimize-chunk-order[A]applyPluginsbefore-chunk-ids[A]applyPluginsoptimize-chunk-ids[A]applyPluginsafter-optimize-chunk-ids[A]applyPluginsrecord-chunks[A]applyPluginsbefore-hash[A]applyPluginshash[A]applyPluginshash-for-chunk[A]applyPluginschunk-hash[A]applyPluginsafter-hash[A]applyPluginsbefore-chunk-assets[B]applyPluginsWaterfallglobal-hash-paths[C]applyPluginsBailResultglobal-hash[B]applyPluginsWaterfallbootstrap[B]applyPluginsWaterfalllocal-vars[B]applyPluginsWaterfallrequire[B]applyPluginsWaterfallmodule-obj[B]applyPluginsWaterfallmodule-require[B]applyPluginsWaterfallrequire-extensions[B]applyPluginsWaterfallasset-path[B]applyPluginsWaterfallstartup[B]applyPluginsWaterfallmodule-require[B]applyPluginsWaterfallrender[B]applyPluginsWaterfallmodule[B]applyPluginsWaterfallrender[B]applyPluginsWaterfallpackage[B]applyPluginsWaterfallmodule[B]applyPluginsWaterfallrender[B]applyPluginsWaterfallpackage[B]applyPluginsWaterfallmodules[B]applyPluginsWaterfallrender-with-entry[B]applyPluginsWaterfallasset-path[B]applyPluginsWaterfallasset-path[A]applyPluginschunk-asset[A]applyPluginsadditional-chunk-assets[A]applyPluginsrecord[D]applyPluginsAsyncSeriesadditional-assets[D]applyPluginsAsyncSeriesoptimize-chunk-assets[A]applyPluginsafter-optimize-chunk-assets[D]applyPluginsAsyncSeriesoptimize-assets[A]applyPluginsafter-optimize-assets[D]applyPluginsAsyncSeriesafter-compile[C]applyPluginsBailResultshould-emit[D]applyPluginsAsyncSeriesemit[B]applyPluginsWaterfallasset-path[D]applyPluginsAsyncSeriesafter-emit[A]applyPluginsdone

幾個(gè)關(guān)鍵的事件對(duì)應(yīng)打包的階段:

entry-option:初始化optionsrun:開(kāi)始編譯make:從entry開(kāi)始遞歸分析依賴并對(duì)依賴進(jìn)行buildbuild-moodule:使用loader加載文件并build模塊normal-module-loader:對(duì)loader加載的文件用acorn編譯,生成抽象語(yǔ)法樹(shù)ASTprogram:開(kāi)始對(duì)AST進(jìn)行遍歷,當(dāng)遇到require時(shí)觸發(fā)call require事件seal:所有依賴build完成,開(kāi)始對(duì)chunk進(jìn)行優(yōu)化(抽取公共模塊、加hash等)optimize-chunk-assets:壓縮代碼emit:把各個(gè)chunk輸出到結(jié)果文件

了解以上事件,你可以很容易地寫(xiě)出一個(gè)插件。

...未完待續(xù)

引用

Webpack-源碼二,整體調(diào)用流程與Tapable事件流

webpack4.0源碼分析之Tapable

相關(guān)教程推薦:《Web pack入門視頻教程》
文章題目:深入理解webpack
路徑分享:http://weahome.cn/article/cpjjps.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部