Webpack-源码一,使用require加载并打包模块

来源:互联网 发布:python append extend 编辑:程序博客网 时间:2024/06/17 22:24

最近有同学致力于写一个脚手架工具,在研究webpack源码,问了我几个问题,然而我完全不能解答。于是开始研究webpack。

webpack做的事情主要是实现前端模块化(即:让前端也可以像node端一样适用require方法加载模块)和借助插件实现编译、热加载等功能。webpack源码系列第一部分,就分享最简单的内容——如何使用require方法加载模块并打包。

__webpack_require__方法

待打包的文件bundle_require.js

// bundle_require.jsconsole.log('success');

仅使用最简单的打包,不使用插件。打包后的文件index.bundle.js

// index.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] = {/******/            exports: {},/******/            id: moduleId,/******/            loaded: false/******/        };/******/        // Execute the module function/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);/******/        // Flag the module as loaded/******/        module.loaded = 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;/******/    // __webpack_public_path__/******/    __webpack_require__.p = "/bundle";/******/    // Load entry module and return exports/******/    return __webpack_require__(0);/******/ })/************************************************************************//******/ ([/* 0 *//***/ function(module, exports, __webpack_require__) {    'use strict';    __webpack_require__(1);/***/ },/* 1 *//***/ function(module, exports) {    'use strict';    /*(function(a){        console.log(a.name);    })    ({name: 'hello'})*/    console.log('success');/***/ }/******/ ]);

bundle文件中是一个立即执行函数,形如(function(modules){})([module_1, module_2, ...])

形参modules对应的实参为一个模块数组[module_1, module_2, ...],该模块数组的每个成员都是使用require
加载的一个模块,每个被加载的模块都被封装成一个函数。

var installedModules = {};是加载模块的缓存,如果已经加载过无需再次加载。

__webpack_require__方法通过installedModules对象缓存第一次加载的模块,通过modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);执行形参中的各个模块。使用call是因为为了确保每个module中的this指向的是module本身。然后给它传__webpack_require__函数是想让module有加载其他module的能力。

模块间有简单依赖的情况

模块依赖:bundle_require.js依赖dependency.js

// bundle_require.jsvar dependency = require('./dependency.js');console.log(dependency.name);console.log('success');
// dependency.jsmodule.exports = {    name: 'hello'}

打包后的文件index.bundle.js

// index.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] = {/******/            exports: {},/******/            id: moduleId,/******/            loaded: false/******/        };/******/        // Execute the module function/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);/******/        // Flag the module as loaded/******/        module.loaded = 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;/******/    // __webpack_public_path__/******/    __webpack_require__.p = "/bundle";/******/    // Load entry module and return exports/******/    return __webpack_require__(0);/******/ })/************************************************************************//******/ ([/* 0 *//***/ function(module, exports, __webpack_require__) {    'use strict';    __webpack_require__(1);/***/ },/* 1 *//***/ function(module, exports, __webpack_require__) {    'use strict';    var dependency = __webpack_require__(2);    console.log(dependency.name);    console.log('success');/***/ },/* 2 *//***/ function(module, exports) {    'use strict';    module.exports = {        name: 'hello'    };/***/ }/******/ ]);

实参数组的第一个成员是bundle_require.js,第二个成员是dependency.js。同时,在第一个成员函数中,使用__webpack_require__(2)加载下一个模块,形成链式调用。

多个入口文件的情况

需要注意,打包的文件中moudleId是不会重复的,如果有两个入口文件的情况,则入口模块id都为0,其他依赖模块id不重复。

index.js为入口文件一,index_two.js为入口文件二,它们共同引用common.js。此外,入口文件一还引用dependency.js

// index.jsvar dependency = require('./src/require/dependency.js');var common = require('./src/require/common.js');console.log('index: ', dependency.name, common.name);
// index_two.jsvar common = require('./src/require/common.js');console.log('index_two', common.name);
// common.jsmodule.exports = {    name: 'common'}
// dependency.jsmodule.exports = {    name: 'dependency'}

打包以后

// index.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] = {/******/            exports: {},/******/            id: moduleId,/******/            loaded: false/******/        };/******/        // Execute the module function/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);/******/        // Flag the module as loaded/******/        module.loaded = 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;/******/    // __webpack_public_path__/******/    __webpack_require__.p = "/bundle";/******/    // Load entry module and return exports/******/    return __webpack_require__(0);/******/ })/************************************************************************//******/ ([/* 0 *//***/ function(module, exports, __webpack_require__) {    'use strict';    var dependency = __webpack_require__(1);    var common = __webpack_require__(2);    console.log('index: ', dependency.name, common.name);/***/ },/* 1 *//***/ function(module, exports) {    'use strict';    module.exports = {        name: 'dependency'    };/***/ },/* 2 *//***/ function(module, exports) {    'use strict';    module.exports = {        name: 'common'    };/***/ }/******/ ]);

分别加载两个模块,id为1和2,入口文件id为0.

// index_two.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] = {/******/            exports: {},/******/            id: moduleId,/******/            loaded: false/******/        };/******/        // Execute the module function/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);/******/        // Flag the module as loaded/******/        module.loaded = 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;/******/    // __webpack_public_path__/******/    __webpack_require__.p = "/bundle";/******/    // Load entry module and return exports/******/    return __webpack_require__(0);/******/ })/************************************************************************//******/ ([/* 0 *//***/ function(module, exports, __webpack_require__) {    'use strict';    var common = __webpack_require__(2);    console.log('index_two', common.name);/***/ },/* 1 */,/* 2 *//***/ function(module, exports) {    'use strict';    module.exports = {        name: 'common'    };/***/ }/******/ ]);

只加载一个模块,其id为2,入口文件id为0。

这就是说,每个模块都有一个全局唯一的id,当重复require模块时,会使用第一次加载时的id。

其实,入口参数是字符串不管是多入口还是单入口,最后都会将入口模块的导出项导出,没有导出项就导出{},而入口参数是数组,就会将最后一个模块导出(webpackg官网有说明)。

使用CommonsChunkPlugin的情况

上面的依赖情况下,index.jsindex_two.js有公共依赖 common.js,这种情况在开发中我们会使用一个插件CommonsChunkPlugin,使用该插件的情况下,打包又是怎样的呢。

// index.bundle.jswebpackJsonp([0],[/* 0 *//***/ function(module, exports, __webpack_require__) {    'use strict';    var dependency = __webpack_require__(1);    var common = __webpack_require__(2);    console.log('index: ', dependency.name, common.name);/***/ },/* 1 *//***/ function(module, exports) {    'use strict';    module.exports = {        name: 'dependency'    };/***/ }]);
// index_two.bundle.jswebpackJsonp([1],[/* 0 *//***/ function(module, exports, __webpack_require__) {    'use strict';    var common = __webpack_require__(2);    console.log('index_two', common.name);/***/ }]);
//common.js (打包公共模块的common.js)/******/ (function(modules) { // webpackBootstrap/******/    // install a JSONP callback for chunk loading/******/    var parentJsonpFunction = window["webpackJsonp"];/******/    window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) {/******/        // add "moreModules" to the modules object,/******/        // then flag all "chunkIds" as loaded and fire callback/******/        var moduleId, chunkId, i = 0, callbacks = [];/******/        for(;i < chunkIds.length; i++) {/******/            chunkId = chunkIds[i];/******/            if(installedChunks[chunkId])/******/                callbacks.push.apply(callbacks, installedChunks[chunkId]);/******/            installedChunks[chunkId] = 0;/******/        }/******/        for(moduleId in moreModules) {/******/            modules[moduleId] = moreModules[moduleId];/******/        }/******/        if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules);/******/        while(callbacks.length)/******/            callbacks.shift().call(null, __webpack_require__);/******/        if(moreModules[0]) {/******/            installedModules[0] = 0;/******/            return __webpack_require__(0);/******/        }/******/    };/******/    // The module cache/******/    var installedModules = {};/******/    // object to store loaded and loading chunks/******/    // "0" means "already loaded"/******/    // Array means "loading", array contains callbacks/******/    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] = {/******/            exports: {},/******/            id: moduleId,/******/            loaded: false/******/        };/******/        // Execute the module function/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);/******/        // Flag the module as loaded/******/        module.loaded = true;/******/        // Return the exports of the module/******/        return module.exports;/******/    }/******/    // This file contains only the entry chunk./******/    // The chunk loading function for additional chunks/******/    __webpack_require__.e = function requireEnsure(chunkId, callback) {/******/        // "0" is the signal for "already loaded"/******/        if(installedChunks[chunkId] === 0)/******/            return callback.call(null, __webpack_require__);/******/        // an array means "currently loading"./******/        if(installedChunks[chunkId] !== undefined) {/******/            installedChunks[chunkId].push(callback);/******/        } else {/******/            // start chunk loading/******/            installedChunks[chunkId] = [callback];/******/            var head = document.getElementsByTagName('head')[0];/******/            var script = document.createElement('script');/******/            script.type = 'text/javascript';/******/            script.charset = 'utf-8';/******/            script.async = true;/******/            script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"index","1":"index_two"}[chunkId]||chunkId) + ".bundle.js";/******/            head.appendChild(script);/******/        }/******/    };/******/    // expose the modules object (__webpack_modules__)/******/    __webpack_require__.m = modules;/******/    // expose the module cache/******/    __webpack_require__.c = installedModules;/******/    // __webpack_public_path__/******/    __webpack_require__.p = "/bundle";/******/ })/************************************************************************//******/ ({/***/ 2:/***/ function(module, exports) {    'use strict';    module.exports = {        name: 'common'    };/***/ }/******/ });

index.bundle.jsindex_two.bundle.js都使用了webpackJsonp方法来加载模块。下面我们具体看看这个函数。

var parentJsonpFunction = window["webpackJsonp"];/******/    window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) {/******/        // add "moreModules" to the modules object,/******/        // then flag all "chunkIds" as loaded and fire callback/******/        var moduleId, chunkId, i = 0, callbacks = [];/******/        for(;i < chunkIds.length; i++) {/******/            chunkId = chunkIds[i];/******/            if(installedChunks[chunkId])/******/                callbacks.push.apply(callbacks, installedChunks[chunkId]);/******/            installedChunks[chunkId] = 0;/******/        }/******/        for(moduleId in moreModules) {/******/            modules[moduleId] = moreModules[moduleId];/******/        }/******/        if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules);/******/        while(callbacks.length)/******/            callbacks.shift().call(null, __webpack_require__);/******/        if(moreModules[0]) {/******/            installedModules[0] = 0;/******/            return __webpack_require__(0);/******/        }/******/    };/******/    // The module cache/******/    var installedModules = {};/******/    // object to store loaded and loading chunks/******/    // "0" means "already loaded"/******/    // Array means "loading", array contains callbacks/******/    var installedChunks = {/******/        2:0/******/    };

chunkIds是待加载模块的id组成的数组,moreModules是待加载模块封装的函数组成的数组。webpackJsonp的作用就是把installedChunks //存放公共模块中的模块通过callbacks.shift().call(null, __webpack_require__);加载,并把其余模块写进modules对象,然后通过__webpack_require__(0),也就是上文中方式加载。

2 0
原创粉丝点击