zoukankan      html  css  js  c++  java
  • webpack编译后的代码如何在浏览器执行

    浏览器是无法直接使用模块之间的commonjs或es6,webpack在打包时做了什么处理,才能让浏览器能够执行呢,往下看吧。

    使用commonjs语法

    先看下写的代码,
    app.js

    minus.js

    webpack.config.js

    代码非常简单,没啥可说的,直接上编译后的代码来分析,代码可以直接复制过来在浏览器执行调试

      // 一个IIFE, 方法的形参是一个对象,key是页面的路径,value是页面的代码
    ;(function (modules) {
        // 缓存已经读取过的module,避免重复执行
        var installedModules = {}
    
        // 核心方法。moduleId以页面的路径作为属性
        function __webpack_require__(moduleId) {
            // 如果缓存存有moduleId,代表已经执行过,直接把缓存里的值返回
            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
        }
        // Load entry module and return exports
        return __webpack_require__((__webpack_require__.s = "./app.js"))
    })(
        // IIFE的参数,把所有需要的页面转换为对象的key,先读取入口文件app.js,文件内部需要minus.js,所以再次调用__webpack_require__执行
        {
        "./app.js": function (module, exports, __webpack_require__) {
            var minus = __webpack_require__(
                /*! ./vendor/minus */ "./vendor/minus.js"
            )
            console.log("minus(1, 2) = ", minus(1, 2))
        },
    
        "./vendor/minus.js": function (module, exports) {
            module.exports = function (a, b) {
                return a - b
            }
        },
    })
    
    

    再来看下es6语法有什么区别

    ;(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__是一个方法,给这个方法加一个属性,内容是页面模块这个对象
        __webpack_require__.m = modules
    
        // expose the module cache
        // 同理也是加了一个读缓存模块的属性
        __webpack_require__.c = installedModules
    
        // define getter function for harmony exports
        // 给这个对象赋值,为什么不直接赋值呢,因为这样子,这个属性是不可变的,怎么改使用还是取的get
        __webpack_require__.d = function (exports, name, getter) {
            if (!__webpack_require__.o(exports, name)) {
                Object.defineProperty(exports, name, {
                    enumerable: true,
                    get: getter,
                })
            }
        }
    
        // define __esModule on exports
        // 给使用es6导出的模块打上标记,Symbol.toStringTag 
        // 请看https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag
        // 做这一步的原因是 其他页面导出的时候会判断有没有标记,有的话就会换另一种方式读取,导出看__webpack_require__.n
        __webpack_require__.r = function (exports) {
            if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
                Object.defineProperty(exports, Symbol.toStringTag, {
                    value: "Module",
                })
            }
            Object.defineProperty(exports, "__esModule", { value: true })
        }
    
        // getDefaultExport function for compatibility with non-harmony modules
        // 看导出时的读取方式是采用es6还是commonjs
        __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)
        }
    
        // Load entry module and return exports
        return __webpack_require__((__webpack_require__.s = "./app.js"))
    })({
        "./app.js": function (module, __webpack_exports__, __webpack_require__) {
            "use strict"
            __webpack_require__.r(__webpack_exports__)
            // 这里需要注意,使用es6导入的时候,webpack改变了原变量名字,所以这个变量可以使用可以修改,
            // 但是如果变量是对象的情况下,不允许修改变量指向的内存地址
    
            /* harmony import */ var _vendor_sum__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
                /*! ./vendor/sum */ "./vendor/sum.js"
            )
    
            console.log(
                "sum(1, 3) = ",
                Object(_vendor_sum__WEBPACK_IMPORTED_MODULE_0__["sum"])(1, 3)
            )
        },
    
        "./vendor/sum.js": function (
            module,
            __webpack_exports__,
            __webpack_require__
        ) {
            "use strict"
            // 打上标记是es6语法
            __webpack_require__.r(__webpack_exports__)
            // 属性赋值
            /* harmony export (binding) */ __webpack_require__.d(
                __webpack_exports__,
                "sum",
                function () {
                    return sum
                }
            )
            function sum(a, b) {
                return a + b
            }
        },
    })
    
    

    如何使用分包懒加载

    在某些特定环境比如点击某个按钮才加载,webpack会创建一个script标签来加载内容(vue-cli 有利用prefetch做优化)同时生成一个promise
    把resolve和reject放进installedChunks数组里,当页面加载好后会从数组取出resolve执行(利用webpackJsonp重写push方法)

    // 加载模块
     __webpack_require__.e = function requireEnsure(chunkId) {
            var promises = []
    
            // JSONP chunk loading for javascript
    
            var installedChunkData = installedChunks[chunkId]
            if (installedChunkData !== 0) {
                // 0 means "already installed".
    
                // a Promise means "currently loading".
                if (installedChunkData) {
                    promises.push(installedChunkData[2])
                } else {
                    // setup Promise in chunk cache
                    var promise = new Promise(function (resolve, reject) {
                        installedChunkData = installedChunks[chunkId] = [
                            resolve,
                            reject,
                        ]
                    })
                    promises.push((installedChunkData[2] = promise))
    
                    // start chunk loading
                    var script = document.createElement("script")
                    var onScriptComplete
    
                    script.charset = "utf-8"
                    script.timeout = 120
                    if (__webpack_require__.nc) {
                        script.setAttribute("nonce", __webpack_require__.nc)
                    }
                    script.src = jsonpScriptSrc(chunkId)
    
                    // create error before stack unwound to get useful stacktrace later
                    var error = new Error()
                    onScriptComplete = function (event) {
                        // avoid mem leaks in IE.
                        script.onerror = script.onload = null
                        clearTimeout(timeout)
                        var chunk = installedChunks[chunkId]
                        if (chunk !== 0) {
                            if (chunk) {
                                var errorType =
                                    event &&
                                    (event.type === "load" ? "missing" : event.type)
                                var realSrc =
                                    event && event.target && event.target.src
                                error.message =
                                    "Loading chunk " +
                                    chunkId +
                                    " failed.
    (" +
                                    errorType +
                                    ": " +
                                    realSrc +
                                    ")"
                                error.name = "ChunkLoadError"
                                error.type = errorType
                                error.request = realSrc
                                chunk[1](error)
                            }
                            installedChunks[chunkId] = undefined
                        }
                    }
                    var timeout = setTimeout(function () {
                        onScriptComplete({ type: "timeout", target: script })
                    }, 120000)
                    script.onerror = script.onload = onScriptComplete
                    document.head.appendChild(script)
                }
            }
            return Promise.all(promises)
        }
    
    // 设置webpackJsonp
    var jsonpArray = (window["webpackJsonp"] = window["webpackJsonp"] || [])
        var oldJsonpFunction = jsonpArray.push.bind(jsonpArray)
        jsonpArray.push = webpackJsonpCallback
        jsonpArray = jsonpArray.slice()
        for (var i = 0; i < jsonpArray.length; i++)
            webpackJsonpCallback(jsonpArray[i])
        var parentJsonpFunction = oldJsonpFunction
    
    // 异步加载的模块,加载好后,执行webpackJsonp的push方法
    ;(window["webpackJsonp"] = window["webpackJsonp"] || []).push([
        [0],
        {
            /***/ "./vendor/sum.js": /***/ function (
                module,
                __webpack_exports__,
                __webpack_require__
            ) {
                "use strict"
                __webpack_require__.r(__webpack_exports__)
                /* harmony export (binding) */ __webpack_require__.d(
                    __webpack_exports__,
                    "sum",
                    function () {
                        return sum
                    }
                )
                function sum(a, b) {
                    return a + b
                }
    
                /***/
            },
        },
    ])
    
  • 相关阅读:
    jQuery图片翻转弹出动画特效
    HTML5来了,7个混合式移动开发框架
    10款很好用的 jQuery 图片滚动插件
    JS图片自动和可控的轮播切换特效
    CSS3扇形动画菜单 鼠标滑过扇形展开动画
    css里面的几个你不知道的属性
    JS判断是否是微信打开页面
    js的escape()、encodeURI()、encodeURIComponent()区别详解
    使用HTML5的十大原因
    Hybrid App开发 四大主流移平台分析
  • 原文地址:https://www.cnblogs.com/wzcsqaws/p/12869611.html
Copyright © 2011-2022 走看看