[webpack] webpack打包加载机制学习

一堆的webpack配置教程看腻了?这里有webpack4的打包及加载机制,要不了解一下?

commonJs 的文件

// index.js
const name = require('./name.js');
console.log(name);

// name.js
module.exports = 'elson';

es module

import * as all from './name-es.js';
console.log(all);
window.all = all;

// name-es.js
export let obj =  {a: 1, b: 2};
export function getName() {return 'elson';};
let name;
export default name = 'elson'
function run(modules) {
  // 传入的参数为所有模块,key为路径名,val为模块function
  var installedModules = {} 

  // The require function
  function __webpack_require__(moduleId) {
    // 检查是否存在模块
    if (installedModules[moduleId]) {
      return installedModules[moduleId].exports // 返回模块的exports对象
    } 
    
    // Create a new module (and put it into the cache)
    // 注册模块,key为moduleId 
    var module = (installedModules[moduleId] = {
      i: moduleId, // 注册对象id
      l: false, // 是否已加载模块 
      exports: {} // module.exports 对象,即commonjs中的导出方式
    }) 
    
    // Execute the module function
    // 执行模块
    modules[moduleId].call(
      module.exports, // this传 exports
      module,  //  第一个值为模块
      module.exports, // 第二个值为模块的exports
      __webpack_require__ // 传入访问webpack_require方法,用于给模块提供require函数
    ) 
    
    // Flag the module as loaded
    module.l = true 
    
    // Return the exports of the module
    return module.exports
  } 
  
  // expose the modules object (__webpack_modules__)
  // 传入的模块指向m
  __webpack_require__.m = modules 
  
  // expose the module cache 
  // 已注册号的模块指向c
  __webpack_require__.c = installedModules 
  
  // define getter function for harmony exports
  // 实现es6模块的动态绑定,模块export出来的每个接口,webpack编译后会为这些接口变量进行绑定
  // 这样,注册好的模块内部变化都会反映在接口上
  __webpack_require__.d = function(exports, name, getter) {
    // 调用检查对象是否有属性校验,传入exports 和name 
    // 如果返回false,则为exports 对象写入namekey 并且绑定getter访问器属性
    if (!__webpack_require__.o(exports, name)) {
      Object.defineProperty(exports, name, {
        enumerable: true,
        get: getter
      })
    }
  } // define __esModule on exports

  // 检查对象是否存在属性 // Object.prototype.hasOwnProperty.call
  __webpack_require__.o = function(object, property) {
    return Object.prototype.hasOwnProperty.call(object, property)
  } 

  // 标识exports对象,支持es6 symbol就写为symbol,
  // 否则写入key = __esModule ,用于标识export对象
  __webpack_require__.r = function(exports) {
    if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
      Object.defineProperty(exports, Symbol.toStringTag, {
        value: 'Module'
      })
    }
    Object.defineProperty(exports, '__esModule', { value: true })
  } 

  // create a fake namespace object 
  // mode & 1: value is a module id, require it 
  // mode & 2: merge all properties of value into the ns 
  // mode & 4: return value when already ns object 
  // mode & 8|1: behave like require
  __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
  } 
  
  // getDefaultExport function for compatibility with non-harmony modules
  __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_public_path__
  __webpack_require__.p = '' // Load entry module and return exports
  
  // 设置入口
  var entry = './index.js'
  __webpack_require__.s = entry
  return __webpack_require__(entry) // 执行入口id
}


/**
 * webpack boost 启动流程
 * 入参为模块对象集,对象属性为 模块名,即 路径名
 * 匿名函数执行
 * 1. 声明模块注册的对象
 * 2. 声明require方法
 * 3. 声明其他辅助函数
 * 4. 设置入口模块参数,比如 index.js
 * 5. 调用require方法,传入入口模块id,比如 index.js
 * 6. 由入口模块开始执行整个流程,当遇到require 引入其他模块时,查找是否已注册
 * 模块的key为文件路径名,模块为function,有自己的作用域,入参为 (module, exports, require)
 * this 指向module.exports
 * module 注册好的模块对象(即首次访问模块时会执行模块,并缓存到module中)
 * exports  module.exports
 * require 方法 (用于访问模块,如果模块未注册会先执行注册流程,返回注册好的module.exports)
 * 
 * 执行顺序:
 * 传入入口的模块进行执行,模块内通过require方法(__webpack_require__) 访问其他模块
 * 
 */

//   commonjs打包后的文件
const modules = {
  // module 为注册模块是绑定的module对象,指向installedModules['./index.js']
  // exports 指向module.exports
  './index.js': function(module, exports, __webpack_require__) {
    // this 指向 module.exports
    const name = __webpack_require__('./name.js') // 访问后拿到的是'./name.js'模块的注册后的module.exports对象

    console.log(name)
  },

  './name.js': function(module, exports) {
    module.exports = 'elson'
  }
}


//  es module 模块打包后的文件
const esModules = {
  './index-es.js': function(module, __webpack_exports__, __webpack_require__) {
    'use strict'
    // Object.defineProperty 定义模块的exports为 es6模块
    __webpack_require__.r(__webpack_exports__)

    /* harmony import */
    var _name_es_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
      './name-es.js'
    )
    console.log(_name_es_js__WEBPACK_IMPORTED_MODULE_0__)
    window.all = _name_es_js__WEBPACK_IMPORTED_MODULE_0__
  },

  './name-es.js':
    /*! exports provided: obj, getName, default */
    function(module, __webpack_exports__, __webpack_require__) {
      'use strict'
      __webpack_require__.r(__webpack_exports__)
      
      // 定义exports的key为访问器属性, 返回对应的值
      __webpack_require__.d(__webpack_exports__, 'obj', function() {
        return obj
      })

      /* harmony export (binding) */
      __webpack_require__.d(__webpack_exports__, 'getName', function() {
        return getName
      })
      let obj = { a: 1, b: 2 }
      function getName() {
        return 'elson'
      }
      let name

      // 定义default 对象接口属性
      __webpack_exports__['default'] = name = 'elson'
    }
}

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

推荐阅读更多精彩内容