打包后的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 应用commonjsmodule.exports = "你好js"# index.js// 01 require引入commonjs const name = require('./login')console.log("name");
  • 如果ESM导出,CommonJS引入,d(),r(),['default']有应用

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

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

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

    # login.jsmodule.exports = "mc"# index.jslet 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 = 18console.log(obj)  // 输入看图

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