Skip to content
导航

webpack

webpack 是一个 JavaScript 应用程序的模块打包工具。 和 rollup 不同的是,webpack 更关注应用程序的开发体验,不断丰富开发特性,例如模块热替换、Module Federation。

webpack 打包产物

webpack 5 打包区分mode配置:

  • development模式可以查看打包产物和源文件的关系,并使用内置的模块加载器来加载这些产物代码。
  • production模式去除了不必要的模块加载器,默认开启代码压缩等代码优化。

下面以这份源码展示webpack 5默认配置的打包产物

js
// index.js
import { sayHello } from './mod'
sayHello()

// mod.js
export function sayHello(){
    console.log('hello')
}

mode: development

js
(() => {
    // webpackBootstrap
    'use strict'
    var __webpack_modules__ = {
        './src/index.js':
        /*!**********************!*\
        !*** ./src/index.js ***!
        \**********************/
            (
                __unused_webpack_module,
                __webpack_exports__,
                __webpack_require__
            ) => {
                eval(
                    '__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _mod__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./mod */ "./src/mod.js");\n\n(0,_mod__WEBPACK_IMPORTED_MODULE_0__.sayHello)()\n\n//# sourceURL=webpack://wbpack/./src/index.js?'
                )
            },

        './src/mod.js':
        /*!********************!*\
        !*** ./src/mod.js ***!
        \********************/
            (
                __unused_webpack_module,
                __webpack_exports__,
                __webpack_require__
            ) => {
                eval(
                    "__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   sayHello: () => (/* binding */ sayHello)\n/* harmony export */ });\nfunction sayHello(){\n    console.log('hello')\n}\n\n//# sourceURL=webpack://wbpack/./src/mod.js?"
                )
            },
    }
    /************************************************************************/
    // The module cache
    var __webpack_module_cache__ = {}

    // The require function
    function __webpack_require__(moduleId) {
        // Check if module is in cache
        var cachedModule = __webpack_module_cache__[moduleId]
        if (cachedModule !== undefined) {
            return cachedModule.exports
        }
        // Create a new module (and put it into the cache)
        var module = (__webpack_module_cache__[moduleId] = {
            // no module.id needed
            // no module.loaded needed
            exports: {},
        })

        // Execute the module function
        __webpack_modules__[moduleId](
            module,
            module.exports,
            __webpack_require__
        )

        // Return the exports of the module
        return module.exports
    }

    /************************************************************************/
    /* webpack/runtime/define property getters */
    ;(() => {
        // define getter functions for harmony exports
        __webpack_require__.d = (exports, definition) => {
            for (var key in definition) {
                if (
                    __webpack_require__.o(definition, key) &&
                    !__webpack_require__.o(exports, key)
                ) {
                    Object.defineProperty(exports, key, {
                        enumerable: true,
                        get: definition[key],
                    })
                }
            }
        }
    })()

    /* webpack/runtime/hasOwnProperty shorthand */
    ;(() => {
        __webpack_require__.o = (obj, prop) =>
            Object.prototype.hasOwnProperty.call(obj, prop)
    })()

    /* webpack/runtime/make namespace object */
    ;(() => {
        // define __esModule on exports
        __webpack_require__.r = (exports) => {
            if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
                Object.defineProperty(exports, Symbol.toStringTag, {
                    value: 'Module',
                })
            }
            Object.defineProperty(exports, '__esModule', { value: true })
        }
    })()

    /************************************************************************/

    // startup
    // Load entry module and return exports
    // This entry module can't be inlined because the eval devtool is used.
    var __webpack_exports__ = __webpack_require__('./src/index.js')
})()

mode: production

js
(()=>{"use strict";console.log("hello")})();

默认打包产物如上,关闭默认开启的代码压缩可以往前一步查看打包细节

js
(() => {// webpackBootstrap
    'use strict'
    var __webpack_exports__ = {}

    // CONCATENATED MODULE: ./src/mod.js
    function sayHello() {
        console.log('hello')
    }

    // CONCATENATED MODULE: ./src/index.js
    sayHello()
})()

代码分割

使用这份代码触发webpack 5的代码分割

js
// index.js
import('./mod').then( m => m.sayHello() )

// mod.js
export function sayHello(){
    console.log('hello')
}

由于动态导入需要模块加载器,development打包产物和production打包产物的区别只有是否使用eval的区别。

production打包产物如下:

index.js

js
;(() => {
    // webpackBootstrap
    var __webpack_modules__ = {}
    /************************************************************************/
    // The module cache
    var __webpack_module_cache__ = {}

    // The require function
    function __webpack_require__(moduleId) {
        // Check if module is in cache
        var cachedModule = __webpack_module_cache__[moduleId]
        if (cachedModule !== undefined) {
            return cachedModule.exports
        }
        // Create a new module (and put it into the cache)
        var module = (__webpack_module_cache__[moduleId] = {
            // no module.id needed
            // no module.loaded needed
            exports: {},
        })

        // Execute the module function
        __webpack_modules__[moduleId](
            module,
            module.exports,
            __webpack_require__
        )

        // Return the exports of the module
        return module.exports
    }

    // expose the modules object (__webpack_modules__)
    __webpack_require__.m = __webpack_modules__

    /************************************************************************/
    /* webpack/runtime/define property getters */
    ;(() => {
        // define getter functions for harmony exports
        __webpack_require__.d = (exports, definition) => {
            for (var key in definition) {
                if (
                    __webpack_require__.o(definition, key) &&
                    !__webpack_require__.o(exports, key)
                ) {
                    Object.defineProperty(exports, key, {
                        enumerable: true,
                        get: definition[key],
                    })
                }
            }
        }
    })()

    /* webpack/runtime/ensure chunk */
    ;(() => {
        __webpack_require__.f = {}
        // This file contains only the entry chunk.
        // The chunk loading function for additional chunks
        __webpack_require__.e = (chunkId) => {
            return Promise.all(
                Object.keys(__webpack_require__.f).reduce((promises, key) => {
                    __webpack_require__.f[key](chunkId, promises)
                    return promises
                }, [])
            )
        }
    })()

    /* webpack/runtime/get javascript chunk filename */
    ;(() => {
        // This function allow to reference async chunks
        __webpack_require__.u = (chunkId) => {
            // return url for filenames based on template
            return '' + chunkId + '.js'
        }
    })()

    /* webpack/runtime/global */
    ;(() => {
        __webpack_require__.g = (function () {
            if (typeof globalThis === 'object') return globalThis
            try {
                return this || new Function('return this')()
            } catch (e) {
                if (typeof window === 'object') return window
            }
        })()
    })()

    /* webpack/runtime/hasOwnProperty shorthand */
    ;(() => {
        __webpack_require__.o = (obj, prop) =>
            Object.prototype.hasOwnProperty.call(obj, prop)
    })()

    /* webpack/runtime/load script */
    ;(() => {
        var inProgress = {}
        var dataWebpackPrefix = 'wbpack:'
        // loadScript function to load a script via script tag
        __webpack_require__.l = (url, done, key, chunkId) => {
            if (inProgress[url]) {
                inProgress[url].push(done)
                return
            }
            var script, needAttach
            if (key !== undefined) {
                var scripts = document.getElementsByTagName('script')
                for (var i = 0; i < scripts.length; i++) {
                    var s = scripts[i]
                    if (
                        s.getAttribute('src') == url ||
                        s.getAttribute('data-webpack') ==
                            dataWebpackPrefix + key
                    ) {
                        script = s
                        break
                    }
                }
            }
            if (!script) {
                needAttach = true
                script = document.createElement('script')

                script.charset = 'utf-8'
                script.timeout = 120
                if (__webpack_require__.nc) {
                    script.setAttribute('nonce', __webpack_require__.nc)
                }
                script.setAttribute('data-webpack', dataWebpackPrefix + key)

                script.src = url
            }
            inProgress[url] = [done]
            var onScriptComplete = (prev, event) => {
                // avoid mem leaks in IE.
                script.onerror = script.onload = null
                clearTimeout(timeout)
                var doneFns = inProgress[url]
                delete inProgress[url]
                script.parentNode && script.parentNode.removeChild(script)
                doneFns && doneFns.forEach((fn) => fn(event))
                if (prev) return prev(event)
            }
            var timeout = setTimeout(
                onScriptComplete.bind(null, undefined, {
                    type: 'timeout',
                    target: script,
                }),
                120000
            )
            script.onerror = onScriptComplete.bind(null, script.onerror)
            script.onload = onScriptComplete.bind(null, script.onload)
            needAttach && document.head.appendChild(script)
        }
    })()

    /* webpack/runtime/make namespace object */
    ;(() => {
        // define __esModule on exports
        __webpack_require__.r = (exports) => {
            if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
                Object.defineProperty(exports, Symbol.toStringTag, {
                    value: 'Module',
                })
            }
            Object.defineProperty(exports, '__esModule', { value: true })
        }
    })()

    /* webpack/runtime/publicPath */
    ;(() => {
        var scriptUrl
        if (__webpack_require__.g.importScripts)
            scriptUrl = __webpack_require__.g.location + ''
        var document = __webpack_require__.g.document
        if (!scriptUrl && document) {
            if (document.currentScript) scriptUrl = document.currentScript.src
            if (!scriptUrl) {
                var scripts = document.getElementsByTagName('script')
                if (scripts.length) {
                    var i = scripts.length - 1
                    while (i > -1 && !scriptUrl) scriptUrl = scripts[i--].src
                }
            }
        }
        // When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration
        // or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.
        if (!scriptUrl)
            throw new Error(
                'Automatic publicPath is not supported in this browser'
            )
        scriptUrl = scriptUrl
            .replace(/#.*$/, '')
            .replace(/\?.*$/, '')
            .replace(/\/[^\/]+$/, '/')
        __webpack_require__.p = scriptUrl
    })()

    /* webpack/runtime/jsonp chunk loading */
    ;(() => {
        // no baseURI

        // object to store loaded and loading chunks
        // undefined = chunk not loaded, null = chunk preloaded/prefetched
        // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded
        var installedChunks = {
            179: 0,
        }

        __webpack_require__.f.j = (chunkId, promises) => {
            // JSONP chunk loading for javascript
            var installedChunkData = __webpack_require__.o(
                installedChunks,
                chunkId
            )
                ? installedChunks[chunkId]
                : undefined
            if (installedChunkData !== 0) {
                // 0 means "already installed".

                // a Promise means "currently loading".
                if (installedChunkData) {
                    promises.push(installedChunkData[2])
                } else {
                    if (true) {
                        // all chunks have JS
                        // setup Promise in chunk cache
                        var promise = new Promise(
                            (resolve, reject) =>
                                (installedChunkData = installedChunks[chunkId] =
                                    [resolve, reject])
                        )
                        promises.push((installedChunkData[2] = promise))

                        // start chunk loading
                        var url =
                            __webpack_require__.p +
                            __webpack_require__.u(chunkId)
                        // create error before stack unwound to get useful stacktrace later
                        var error = new Error()
                        var loadingEnded = (event) => {
                            if (
                                __webpack_require__.o(installedChunks, chunkId)
                            ) {
                                installedChunkData = installedChunks[chunkId]
                                if (installedChunkData !== 0)
                                    installedChunks[chunkId] = undefined
                                if (installedChunkData) {
                                    var errorType =
                                        event &&
                                        (event.type === 'load'
                                            ? 'missing'
                                            : event.type)
                                    var realSrc =
                                        event &&
                                        event.target &&
                                        event.target.src
                                    error.message =
                                        'Loading chunk ' +
                                        chunkId +
                                        ' failed.\n(' +
                                        errorType +
                                        ': ' +
                                        realSrc +
                                        ')'
                                    error.name = 'ChunkLoadError'
                                    error.type = errorType
                                    error.request = realSrc
                                    installedChunkData[1](error)
                                }
                            }
                        }
                        __webpack_require__.l(
                            url,
                            loadingEnded,
                            'chunk-' + chunkId,
                            chunkId
                        )
                    }
                }
            }
        }

        // no prefetching

        // no preloaded

        // no HMR

        // no HMR manifest

        // no on chunks loaded

        // install a JSONP callback for chunk loading
        var webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
            var [chunkIds, moreModules, runtime] = data
            // add "moreModules" to the modules object,
            // then flag all "chunkIds" as loaded and fire callback
            var moduleId,
                chunkId,
                i = 0
            if (chunkIds.some((id) => installedChunks[id] !== 0)) {
                for (moduleId in moreModules) {
                    if (__webpack_require__.o(moreModules, moduleId)) {
                        __webpack_require__.m[moduleId] = moreModules[moduleId]
                    }
                }
                if (runtime) var result = runtime(__webpack_require__)
            }
            if (parentChunkLoadingFunction) parentChunkLoadingFunction(data)
            for (; i < chunkIds.length; i++) {
                chunkId = chunkIds[i]
                if (
                    __webpack_require__.o(installedChunks, chunkId) &&
                    installedChunks[chunkId]
                ) {
                    installedChunks[chunkId][0]()
                }
                installedChunks[chunkId] = 0
            }
        }

        var chunkLoadingGlobal = (self['webpackChunkwbpack'] =
            self['webpackChunkwbpack'] || [])
        chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0))
        chunkLoadingGlobal.push = webpackJsonpCallback.bind(
            null,
            chunkLoadingGlobal.push.bind(chunkLoadingGlobal)
        )
    })()

    /************************************************************************/
    var __webpack_exports__ = {}
    __webpack_require__
        .e(/* import() */ 227)
        .then(__webpack_require__.bind(__webpack_require__, 227))
        .then((m) => m.sayHello())
})()

227.js

js
'use strict';
(self['webpackChunkwbpack'] = self['webpackChunkwbpack'] || []).push([
    [227],
    {
        227: (
            __unused_webpack_module,
            __webpack_exports__,
            __webpack_require__
        ) => {
            __webpack_require__.r(__webpack_exports__)
            __webpack_require__.d(__webpack_exports__, {
                sayHello: () => /* binding */ sayHello,
            })
            function sayHello() {
                console.log('hello')
            }
        },
    },
])

webpack除了负责打包器的工作,还内置了模块加载器。
通常开发者无需关心打包细节,只需要按照配置文档即可满足生产环境需求。

示例代码依赖版本

  • "webpack": "5.88.1"

相关文章