webpak深入浅出(一)[打包原理]

102 阅读1分钟

前言

webpack可以将模块化代码转成浏览器所识别的模块化代码

你将学到的知识点

  • webpack模块化原理
  • webpack3异步加载模块原理

webpack模块化原理

解读:通过一个自执行函数传入一个模块化数组,通过数组索引来调用该模块函数。

//从零实现一个模块化函数
(function(modules) {
    var installModules = {}

    function __webpack_require__(moduleId) {
        if(installModules[moduleId]) {
            return installModules.exports
        }

        var module = installModules[moduleId] = {
            i: moduleId,
            l: false,
            exports: {}
        }

        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)
        module.l = true
        return module.exports
    }

    return __webpack_require__(0)
})([
    (function(module, exports, __webpack_require__) {
        //访问自身变量
        var log = __webpack_require__(1)
        log(1)
    }),
    (function(module, exports, __webpack_require__) {
        module.exports = function(str) {
            console.log(str)
        }
    })
])

webpack3异步加载模块原理

源代码实现异步加载模块

main.js

import('./util/index').then(_ => {
    console.log(_.add(3,3))
})

util/index.js

module.exports = {
    add: function(a, b) {
        return a + b
    }
}

webpack3源码实现异步加载模块(一)

解读:追加一个异步加载script标签,将Promise对象放到installedChunks对象,返回Promise实例对象。

main.js


(function(modules) {
    var installedChunks = {
        0: 0  //已经安装过的模块
    }

    var installedModule = {}

    //导出一个模块
    function __webpack_require__(moduleId) {
        //...省略代码
    }

    //异步加载模块(按需加载)
    __webpack_require__.e = function requireEnsure(chunkId) {
        //加载模块
        var moduleCurrent = installedChunks[chunkId]
        if(moduleCurrent === 0) {
            return new Promise(function(resolve) { resolve() })
        }

        if(moduleCurrent) {
            return moduleCurrent[2]
        }

        var promise = new Promise(function(resolve, reject) {
            moduleCurrent = installedChunks[chunkId] = [resolve, reject]
        })
        moduleCurrent[2] = promise
        
        var head = document.getElementsByTagName('head')[0],
            script = document.createElement('script');

        script.src = chunkId + '.bundle.js'
        script.async = true;
        
        head.appendChild(script)
        return promise
    }
})([
    (function(module, exports, __webpack_require__) {
        __webpack_require__.e(1).then(__webpack_require__.bind(null, 1)).then(_ => {
            _.add(3,3)
        })
    })
])

webpack3源码实现异步加载模块(二)

解读:通过以下步骤来执行代码,实现异步加载模块

1.bundle.js

//异步加载的文件
webpackJsonp(
    [1],
    [
        ,
        (function(module, exports) {
            function add(a,b) {
                return a + b
            }

            module.exports = add
        })
    ]
)

main.js

(function(modules) {
    //异步加载模块
    window['webpackJsonp'] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
        var moduleId, chunkId, i = 0, resolve = [], result;

        //循环拿到所有Promise
        for(; i < chunkIds.length; i++) {
            chunkId = chunkIds[i]
            if(installedChunks[chunkId]) {
                resolve.push(installedChunks[chunkId][0])
            }
            installedChunks[chunkId] = 0 //已经加载过了
        }

        //循环调用所有模块
        for(moduleId in moreModules) {
            if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
                modules[moduleId] = moreModules[moduleId]
            }

            while(resolve.length) {
                resolve.shift()()
            }
        }
    }
    
    var installedChunks = {
        0: 0  //已经安装过的模块
    }

    var installedModule = {}

    //导出一个模块
    function __webpack_require__(moduleId) {
        //...省略代码
    }

    //异步加载模块(按需加载)
    __webpack_require__.e = function requireEnsure(chunkId) {
       //...省略代码
    }
})([
    (function(module, exports, __webpack_require__) {
        __webpack_require__.e(1).then(__webpack_require__.bind(null, 1)).then(_ => {
            _.add(3,3)
        })
    })
])

结语

我们从零实现了

  • webpack模块化代码
  • webpack异步加载模块