关于webpack:webpack打包后的文件分析

52次阅读

共计 4415 个字符,预计需要花费 12 分钟才能阅读完成。

打包后的 bundle.js 源码剖析

ctrl+k ctrl+0 缩进

  • 打包后的函数是个函数自调用。以后函数调用时传入一个对象
  • 这个对象为了不便称之为模块定义,是个键值对
  • 对象键是入口文件门路拼接
  • 对象键值是个函数,相似于 commonjs 模块加载,包裹模块被加载的内容
  • 这个函数在未来某工夫下会被调用,函数能够接管参数,利用参数能够实现模块的加载
  • 将对象实参传给 modules 形参
# bundle.js

(function(modules){
  ...

  // 缓存被加载过的模块
  var installedModules = {};

  // 外围函数,webpack 中自定义的. 作用是返回传入的模块的导出内容 module.exports
  // 这个 moduleId 是 iife 传入参数对象的 key
  function __webpack_require__(moduleId){if(installedModules[moduleId]) {return installedModules[moduleId].exports;
         }

    var module = installedModules[moduleId] = {
             i: moduleId,
             l: false,
             exports: {}};
    
    // 模块内的 this 指向 module.exports
    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

    module.l = true;

    return module.exports
  }

  ...

  return __webpack_require__(__webpack_require__.s = "./src/index.js");

})({

    "./src/index.js":

    (function(module, exports) {console.log("index.js content");
    })

})

一些属性办法

// 将模块定义保留一份,通过 m 属性挂载到自定义办法上
__webpack_require__.m = modules



// 挂载缓存的
__webpack_require__.c = installedModules




// 判断被传入的对象 object 是否具备指定的属性,有返回 true
__webpack_require__.o = function(object, property) {return Object.prototype.hasOwnProperty.call(object, property); 
};




// 给对象人为增加属性,内部能够通过 getter 拜访这个属性
__webpack_require__.d = function(exports, name, getter) {if(!__webpack_require__.o(exports, name)) {Object.defineProperty(exports, name, { enumerable: true, get: getter});
  }
};




// 给 exports 强行加标记
__webpack_require__.r = function(exports) {

  // 如果成立阐明是个 ESModule
  if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {

    // 向对象 exports 增加一个惟一字符串 Symbol.toStringTag 标记,值人为定义成 Module  
    // Object.prototype.toString.call(exports) === Module

    Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module'});
  }

  // 向对象 exports 上增加一个__esModule 属性,值是 true
  Object.defineProperty(exports, '__esModule', { value: true})
};




// 提供一个 getter 办法,返回一个 getter 办法,如果是 ESM 返回默认值,否则返回模块
__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.config 中的 publicPath,根目录,默认 "/"
// index.html 不主动生成时,找不到 index 内引入的内容,增加 'dist/'
__webpack_require__.p = ""



// 标记入口文件
__webpack_require__.s = "./src/index.js"


// 调用 t 时,咱们会拿到被加载模块得内容 value
// 对于 value 可能间接返回,或者解决之后返回
// 接管两个参数,一个 value 示意模块 id,第二个值 mode 是一个十进制数
// &1 位运算获取 模块 id 的返回值
// 8,4,ns,2 是对模块返回值进行的额定解决,而后返回应用
// 1,8 成立相当于加载了 CJS 标准间接返回 value
// 1,4 成立相当于加载了 ESM 间接返回 value
// 如果上述都不成立,持续解决 value,将其挂载到 ns 的 default 属性上
// 如果 value 是非根本类型,例如{name:xx,age:10}, 则能够通过 ns.name,ns.age 触发 getter 办法获取

__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;
};

备注:t 办法中的 bind() 办法创立一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时应用。

  • 如果 CommonJS 导出,CommonJS 导入,这些办法不会调用

    # login.js
    // 01 应用 commonjs
    module.exports = "你好 js"
    
    # index.js
    // 01 require 引入 commonjs 
    const name = require('./login')
    console.log("name");
  • 如果 ESM 导出,CommonJS 引入,d(),r(),['default']有应用

    # login.js
    // 02 应用 ESM
    export default '你好 ESM'
    export const age =18
    
    # index.js
    // 02 require 引入 ESM
    const obj = require('./login')
    console.log(obj.default,"------",obj.age);
  • 如果 CommonJS 导出,ESM 引入,r(),n()有应用 (import 会走 r())

    # login.js
    // 03 应用 commonjs
    module.exports = "你好 Commonjs"
    
    # index.js
    // 03 ESM 引入 commonjs 
    import name from './login'
    console.log(name);
  • 如果 ESM 导出,ESM 引入

    # login.js
    // 04 应用 ESM
    export default '你好 ESM'
    export const age =18
    
    # index.js
    // 04 ESM 引入 ESM
    import defalutname,{age} from './login'
    console.log(defalutname,"------",age);
  • 如果 import()动静导入.t() e()

    # login.js
    module.exports = "mc"
    
    # index.js
    let btn = document.getElementById("btn")
    btn.addEventListener('click',function(){import(/*webpackChunkName: "login1"*/'./login.js').then((login)=>{console.log(login)
    })
    })

    增加 webpackChunkName 会生成 login1.bundle.js 文件,否则是 0.bundle.js, 查看bundle.js 源码

点击时减少 script 标签, 懒加载的外围原理是 jsonpt 办法能够依据文件内容不同进行解决, 取决于传入数值(8,7,6,3,2,1)

有个小问题插播, 为什么 age 未定义先返回了?

"./src/login.js" :

(function(module, __webpack_exports__, __webpack_require__) {

    "use strict";
    __webpack_require__.r(__webpack_exports__);
    __webpack_require__.d(__webpack_exports__, "age", function() {return age;});
    __webpack_exports__["default"] = ('你好 ESM');
    const age =18

 })

看上面代码

let obj={}
Object.defineProperty(obj,"age",{get:()=>{console.log(1)
        return age
    }
})
const age = 18

console.log(obj)  // 输入看图

解答:输入看图,get 办法尽管返回了一个未定义的常量 age,然而只有在应用 Obj.age 触发 get 办法之前,把 get 内返回的 age 定义了就能够取到。因而,尽管 const age 在前面定义,然而在这个定义之后再触发get 办法,都是可行的,办法只在调用时候才会去确定是否定义了。

正文完
 0