webpack基础源码 & 懒加载

(function (modules) {
  // 01 定义对象用于将来缓存被加载过的模块
  let installedModules = {}

  // 02 定义一个 __webpack_require__ 方法来替换 import require 加载操作
  function __webpack_require__(moduleId) {
    // 2-1 判断当前缓存中是否存在要被加载的模块内容,如果存在则直接返回
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports
    }

    // 2-2 如果当前缓存中不存在则需要我们自己定义{} 执行被导入的模块内容加载
    let module = installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    }

    // 2-3 调用当前 moduleId 对应的函数,然后完成内容的加载
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)

    // 2-4 当上述的方法调用完成之后,我们就可以修改 l 的值用于表示当前模块内容已经加载完成了
    module.l = true

    // 2-5 加载工作完成之后,要将拿回来的内容返回至调用的位置 
    return module.exports
  }

  // 03 定义 m 属性用于保存 modules 
  __webpack_require__.m = modules

  // 04 定义 c 属性用于保存 cache 
  __webpack_require__.c = installedModules

  // 05 定义 o 方法用于判断对象的身上是否存在指定的属性
  __webpack_require__.o = function (object, property) {
    return Object.prototype.hasOwnProperty(object, property)
  }

  // 06 定义 d 方法用于在对象的身上添加指定的属性,同时给该属性提供一个 getter 
  __webpack_require__.d = function (exports, name, getter) {
    if (!__webpack_require__.o(exports, name)) {
      Object.defineProperty(exports, name, { enumerable: true, get: getter })
    }
  }

  // 07 定义 r 方法用于标识当前模块是 es6 类型
  __webpack_require__.r = function (exports) {
    if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
      Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" })
    }
    Object.defineProperty(exports, '__esModule', { value: true })
  }

  // 08 定义 n 方法,用于设置具体的 getter 
  __webpack_require__.n = function (module) {
    let getter = module && module.__esModule ?
      function getDefault() { return module['default'] } :
      function getModuleExports() { return module }

    __webpack_require__.d(getter, 'a', getter)

    return getter
  }

  // 1 接收二个参数,f是value F用于表示被加载的模块id ,第二个值mode是一个二进制的数值
  // 2 t方法内部做的第T牛事情就是调用自定义的require方法加载value对应的模块导出,重新赋值给value
  // 3 当获取到了这个value值之后余下的8 4 ns 2都是对当前的内容进行加工处理,然后返回使用
  // 4 当mode & 8成立是直接将value返回(commonJS )
  // 5 当mode & 4成立时直接将value返回(esModule)
  // 6 如果都不成立,还是要继续处理value,定义一个 ns {}
  //  6-1如果拿到的value是一个可以直接使用的内容,例如是一个字符串,将它挂载到ns的default属性上
  //  6-2如果是一个对象,遍历key添加对应属性

  // 11 定义 t 方法,用于加载指定 value 的模块内容,之后对内容进行处理再返回
  __webpack_require__.t = function (value, mode) {
    // 01 加载 value 对应的模块内容( value 一般就是模块 id )
    // 加载之后的内容又重新赋值给 value 变量
    if (mode & 1) {
      value = __webpack_require__(value)
    }

    if (mode & 8) {  // 加载了可以直接返回使用的内容
      return value
    }

    if ((mode & 4) && typeof value === 'object' && value && value.__esModule) {
      return value
    }

    // 如果 8 和 4 都没有成立则需要自定义 ns 来通过 default 属性返回内容
    let 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
  }

  // 09 定义 P 属性,用于保存资源访问路径
  __webpack_require__.p = ""


    // 懒加载start======

    // 标识chunk是否完成加载
    // 0 代表加载完成
    // undefine 代表没有加载
    // Promise 代表加载中
    let installedChunks = {
      main: 0
  }

  // webpackJsonpCallBack实现:合并模块定义,改变promise状态执行后续行 
  function webpackJsonpCallBack(data) {
      // 获取需要被动态加载的模块id
      let chunkIds = data[0]
      // 获取需要被动态加载的模块依赖关系对象
      let moreModules = data[1]

      let chunkld, resolves =[]
      // 循环判断chunklds里对应的模块内容是否已经完成了加载
      for (let i = 0; i < chunklds.length; i++) {
          chunkld =  chunkld[i]
          if (Object.prototype.hasOwnProperty.call(inStalledChunksₜ, chunkld) && inStalledChunks[chunkld]) {
              // 0里面存储的是resolve 1是reject 2是promise本身
              resolves.push(installedChunks[chunkld][0])
          }
          // 加载完成
          installedChunks[chunkld] = 0
      }

      for (moduleId in moreModules) {
          if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
              // 合并modules
              modules[moduleId] = moreModules[moduleId]
          }
      }

      while (resolves.length) {
          resolves.shift()()
      }
  }

  // hook push方法
  let jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || []
  let oldJsonp = jsonpArray.push.bind(jsonpArray)
  jsonpArray.push = webpackJsonpCallBack


  // 生成路径
  function jsonScriptSrc(chunkId) {
      return __webpack_require__.p + "" + chunkId + '.built.js'
  }

  // 使用json加载内容,
  __webpack_require__.e = function(chunkId) {
      let promises = []

      let installedChunkData = installedChunks[chunkId]

      // 判断是否完成加载
      if (installedChunkData != 0) {
          // 非0, 非undefined, 代表 Promise 加载中
          if (installedChunkData) {
              // 2才是真正存储的promise
              promises.push(installedChunkData[2])
          } else {
              // 没有加载,就创建一个promise
              let promise = new Promise((resolve, reject) => {
                  installedChunkData = installedChunks[chunkId] = [resolve, reject]
              })
              
              promise.push(installedChunkData[2] = promise)

              // 创建标签
              let script = document.createElement('script')
              script.src = jsonScriptSrc(chunkId)
              document.head.appendChild(script)
          }
      }

      return Promise.all(promises);
  }
  // 懒加载end======

  // 10 调用 __webpack_require__ 方法执行模块导入与加载操作
  return __webpack_require__(__webpack_require__.s = './src/index.js')

})
  ({/**/})
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容