Webpack 编译后代码解读 模块原理 异步加载原理

webpack编译后源码分析

分模块

  • 定义缓存模块的数组为空
  • 有一个起始模块执行
  • 当请求(require)起始模块时,查找缓存是否存在,不存在需要从模块列表加载并缓存,并且通过call传递module, exports, __webpack_require__
// 01 自执行
(function (modules) {
  // 02 require缓存
  var installedModules = {}
  // 03 require函数封装
  function __webpack_require__(moduleId) {
    // 05 判断缓存
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports
    }
    // 06 封装需要传递到模块的module,export,require
    var module = installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    }
    // 07 call调用模块函数
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)
    module.l = true
    return module.exports
  }
  __webpack_require__.m = modules
  __webpack_require__.c = installedModules
  __webpack_require__.d = function (exports, name, getter) {
    if (!__webpack_require__.o(exports, name)) {
      Object.defineProperty(exports, name, {
        enumerable: true,
        get: getter
      })
    }
  }
  __webpack_require__.r = function (exports) {
    if (typeof Symbol !== ‘undefined‘ && Symbol.toStringTag) {
      Object.defineProperty(exports, Symbol.toStringTag, {
        value: ‘Module‘
      })
    }
    Object.defineProperty(exports, ‘__esModule‘, {
      value: true
    })
  }
  __webpack_require__.t = function (value, mode) {
    if (mode & 1) value = __webpack_require__(value)
    if (mode & 8) return value
    if ((mode & 4) && typeof value === ‘object‘ && value && value.__esModule) return value
    var ns = Object.create(null)
    __webpack_require__.r(ns)
    Object.defineProperty(ns, ‘default‘, {
      enumerable: true,
      value: value
    })
    if (mode & 2 && typeof value != ‘string‘) for (var key in value) __webpack_require__.d(ns, key,
      function (key) {
        return value[key]
      }.bind(null, key))
    return ns
  }
  __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
  }
  __webpack_require__.o = function (object, property) {
    return Object.prototype.hasOwnProperty.call(object, property)
  }
  __webpack_require__.p = ‘‘
  // 04 起始模块启动
  return __webpack_require__(__webpack_require__.s = ‘A7Gu‘)
})({
  ‘A7Gu‘: (function (module, exports, __webpack_require__) {
    // 08 请求react,react-dom
    var React = __webpack_require__(‘u6V5‘)
    var ReactDom = __webpack_require__(‘I7V5‘)
    // 09 起始方法的内容执行
    var app = React.createElement(‘div‘, {
      id: ‘app‘,
      onClick: function () {
        alert(‘message‘)
      }
    }, ‘\u4ECA\u5929\u5929\u6C14\u4E0D\u597D‘)
    ReactDom.render(app, document.getElementById(‘app‘))
  }),
  ‘u6V5‘: (function (module, exports, __webpack_require__) {
    var react = {
      createElement(type, attrs, children) {
        return { type, attrs, children }
      }
    }
    module.exports = react
  }),
  ‘I7V5‘: (function (module, exports, __webpack_require__) {
    var ReactDom = {
      render({ attrs }, container) {
        let ele = document.createElement(element.type)
        for (var attr in attrs) {
          if (attr === ‘onClick‘) {
            ele.onclick = attrs[attr]
          } else {
            ele.setAttribute(attr, attrs[attr])
          }
        }
        if (typeof element.children === ‘string‘) {
          let text = document.createTextNode(element.children)
          ele.appendChild(text)
        }
        if (container) {
          container.appendChild(ele)
        } else {
          document.body.appendChild(ele)
        }
      }
    }
    module.exports = ReactDom
  })
})

异步加载

  • 在上面的前提上
  • 当异步加载时,包装成promise方式,then之后require模块
  • 包装script标签加载
  • 请求完成自执行,添加到异步缓存标记表示已经加载(当下次异步请求直接返回)
  • 然后放到模块列表中,之后执行resove()到then中
  • then中在require模块,添加到缓存并传递module,export,require call调用当前函数
  • 产生闭包,后面使用模块下面的函数,属性即可

下面标记async是表示,和上面的区别

// main.js
(function (modules) {
  var installedModules = {}
  /*async*/ // 01 异步标记缓存
  var installedChunks = {
    ‘main‘: 0 // main是起始,可以删除
  }
  /*async*/
  function webpackJsonpCallback(data) {
    // 05 push到这里
    var chunkIds = data[0]
    var moreModules = data[1]
    var moduleId, chunkId, i = 0, resolves = []
    // 06 异步加载的模块可以是多个
    for (; i < chunkIds.length; i++) {
      chunkId = chunkIds[i]
      if (Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
        // 07 promise的resolve
        resolves.push(installedChunks[chunkId][0])
      }
      installedChunks[chunkId] = 0
    }
    for (moduleId in moreModules) {
      if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
        // 08 缓存到模块列表
        modules[moduleId] = moreModules[moduleId]
      }
    }
    if (parentJsonpFunction) parentJsonpFunction(data)
    // 09 依次调用resove,即then的逻辑
    while (resolves.length) {
      resolves.shift()()
    }
  }
  function __webpack_require__(moduleId) {
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports
    }
    var module = installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    }
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)
    module.l = true
    return module.exports
  }
  __webpack_require__.m = modules
  __webpack_require__.c = installedModules
  __webpack_require__.d = function (exports, name, getter) {
    if (!__webpack_require__.o(exports, name)) {
      Object.defineProperty(exports, name, {
        enumerable: true,
        get: getter
      })
    }
  }
  /*async*/
  function jsonpScriptSrc(chunkId) {
    return __webpack_require__.p + ‘‘ + ({}[chunkId] || chunkId) + ‘.js‘
  }

  /*async*/
  __webpack_require__.e = function requireEnsure(chunkId) {
    var promises = []
    var installedChunkData = installedChunks[chunkId]
    if (installedChunkData !== 0) {
      if (installedChunkData) {
        promises.push(installedChunkData[2])
      } else {
        // 03 创建promise
        var promise = new Promise(function (resolve, reject) {
          installedChunkData = installedChunks[chunkId] = [resolve, reject]
        })
        promises.push(installedChunkData[2] = promise)
        // 04 创建script标签,请求
        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)
        var error = new Error()
        onScriptComplete = function (event) {
          // 10 收尾
          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.\n(‘ + 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)
  }

  __webpack_require__.r = function (exports) {
    if (typeof Symbol !== ‘undefined‘ && Symbol.toStringTag) {
      Object.defineProperty(exports, Symbol.toStringTag, {
        value: ‘Module‘
      })
    }
    Object.defineProperty(exports, ‘__esModule‘, {
      value: true
    })
  }
  __webpack_require__.t = function (value, mode) {
    if (mode & 1) value = __webpack_require__(value)
    if (mode & 8) return value
    if ((mode & 4) && typeof value === ‘object‘ && value && value.__esModule) return value
    var ns = Object.create(null)
    __webpack_require__.r(ns)
    Object.defineProperty(ns, ‘default‘, {
      enumerable: true,
      value: value
    })
    if (mode & 2 && typeof value != ‘string‘) for (var key in value) __webpack_require__.d(ns, key,
      function (key) {
        return value[key]
      }.bind(null, key))
    return ns
  }
  __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
  }
  __webpack_require__.o = function (object, property) {
    return Object.prototype.hasOwnProperty.call(object, property)
  }
  __webpack_require__.p = ‘‘
  /*async*/
  __webpack_require__.oe = function (err) {
    console.error(err)
    throw err
  }
  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

  return __webpack_require__(__webpack_require__.s = ‘A7Gu‘)
})({
  ‘A7Gu‘: (function (module, exports, __webpack_require__) {
    var React = __webpack_require__(‘u6V5‘)
    var ReactDom = __webpack_require__(‘I7V5‘)
    var htdiv = React.createElement(‘div‘, {
      id: ‘my‘,
      onClick: function () {
        // 02 包装,请求异步模块,完成之后在require
        __webpack_require__.e(0).then((function () {
          var A = __webpack_require__(‘46Hr‘)
          A.log(‘哈哈哈‘)
          alert(A.message)
        }).bind(null, __webpack_require__)).catch(__webpack_require__.oe)
      }
    },‘\u4ECA\u5929\u5929\u6C14\u4E0D\u597D‘)
    ReactDom.render(htdiv, document.getElementById(‘app‘))
  }),
  ‘u6V5‘: (function (module, exports, __webpack_require__) {
    var react = {
      createElement(type, attrs, children) {
        return { type, attrs, children }
      }
    }
    module.exports = react
  }),
  ‘I7V5‘: (function (module, exports, __webpack_require__) {
    var ReactDom = {
      render(element, container) {
        let ele = document.createElement(element.type)
        for (var attr in element.attrs) {
          if (attr === ‘onClick‘) {
            ele.onclick = element.attrs[attr]
          } else {
            ele.setAttribute(attr, element.attrs[attr])
          }
        }
        if (typeof element.children === ‘string‘) {
          let text = document.createTextNode(element.children)
          ele.appendChild(text)
        }
        if (container) {
          container.appendChild(ele)
        } else {
          document.body.appendChild(ele)
        }
      }
    }
    module.exports = ReactDom
  })
})
// 0.js
// 04 push操作
(window[‘webpackJsonp‘] = window[‘webpackJsonp‘] || []).push([[0], {
  ‘46Hr‘: (function (module, exports) {
    var A = {
      log() {
        console.log(...arguments)
      },
      message: ‘hello ‘
    }
    module.exports = A
  })
}])

Webpack 编译后代码解读 模块原理 异步加载原理

上一篇:netty系列之:netty中的Channel详解


下一篇:C++ API 设计 10 第五章 设计风格