webpack补充

198 阅读4分钟

webpack知识拓展

devtool

  • 开发工具,默认为true,会将编译的代码使用eval包裹
  • 可以设置为false,来关闭

mode

  • 可以通过package命令设置mode,如webpack --mode=development
    • 可以通过process.env.NODE_ENV获取到
    • 在config.js中通过这个拿不到,执行代码中可以拿到
    • 但往往我们是需要判断某个值,执行某种打包方式,一些参数也依赖于某个变量,所以会通过设置环境变量的方式来实现

跨平台赋值

  • 因为mac与window的设置环境变量方法不一样,为了保证协同开发,所以我们使用一个第三方库来设置
  • 第三方库:cross-env
//json
"build": "cross-env NODE_ENV=development webpack"

//webpack.config.js
console.log(process.env.NODE_ENV);// 这样就可以拿到啦

plugins

自定义环境变量

  • 如process.env.NODE_ENV这种变量,实际上是webpack在打包时根据key进行字符串替换
    • 所以我们再编译后的代码中可以看到直接输出的是某个值
  • 通常我们也需要自己定义一些变量,可以通过webpack.DefinePlugin来管理一些key和value,在打包时由webpack进行查询并替换
    • 使用方法:直接再代码里使用diy即可
  • 例子:
  plugins: [
    new webpack.DefinePlugin({
      // 这块我们会使用JSON.stringify包裹一下,因为它自己默认会当做变量来替换,所以有时候会出现一些异常
      diy: JSON.stringify("我是自定义的值"),
    }),
  ],

  //使用
  console.log(diy);

  //编译结果
  console.log("我是自定义的值");
  • 我们已经知道了环境变量是进行替换的,那么如果没有相对于的变量,则不会替换,那么代码就会报错,如
console.log(process.env.asda);//编译完依然如此

使用外置文件.env设置

  • 有一些情况,我们的变量只想在conf文件中使用,并不希望打包像上述例子中打包进代码
  • 第三方库dotenv
  • 创建一个.env文件,在webpack.config.js中导入,然后执行.config()方法即可,会默认将.env文件中的变量放入到环境变量process.env
    • 在任意文件中引入都可以,不过我们当前期望在conf文件使用,就在conf中导入
  • 当然,我们也可以配合webpack.DefinePlugin来使用,毕竟好多变量写在命令行中会比较丑
// .env
C="崔崔崔"
ALD="阿拉丁"

// webpack.config.js
require("dotenv").config();
console.log(process.env);

...
new webpack.DefinePlugin({
    C: JSON.stringify(process.env.C),//组合使用
}),

devServer

  • 在5版本中,可设置webpack serve
  • 会自动查找webpack-dev-server,所以手动安装下
  • 它会将编译内容写到内存中,我们可以直接访问
  • 还可以通过static属性设置一个静态资源目录,默认为public
// package.json.scripts
 "dev": "webpack serve"

history路由需要在服务端接收,否则404

  devServer: {
    historyApiFallback: true,
  },

自定义mock

  • onBeforeSetupMiddleware为一个函数,形参为一个server对象,上面的app可以理解为route对象,我们可以同express一样写一些接口
devServer: {
static: path.resolve(__dirname, "public"),
onBeforeSetupMiddleware(devServer) {
    devServer.app.get("/users", (req, res) => {
    res.json({
        id: 1,
        name: "cc",
    });
    });
},
},

loader

eslint

  • 代码检测
  • npm i eslint eslint-loader babel-eslint -D
  • 如果想要在ide中高亮错误,那么需要下载ESLint插件
  • 如果说只想要提示,但不想阻塞编译,那么久不要加eslint-loader
//webpack.config.js
module: {
rules: [
    {
    test: /\.js$/,
    loader: "eslint-loader",
    exclude: /node_modules/, //排除node_modules
    options: { fix: true }, //如果可以自动修复的话,会自动修复
    enforce: "pre", //前置检查
    }]
}

//.eslintrc.js
module.exports = {
    root: true,
    extends: "xxx", // 继承xxx的规则
    parserOptions: { // 解析器选项
        sourceType: 'module', // 模块
        ecmaVersion: 2015, // es6
    },
    env: { // 运行环境
        browser: true, // 浏览器环境
        node: true, // node环境
    },
    // 自定义规则
    rules: {
        indent: "off", // 缩进
        quotes: "off", // 引号
        "no-console": "error", // 不允许console.log
    },
    // 不检查的文件或目录
    ignorePatterns: ["/dist"],
};

自动修复

  • 新建.vscode文件夹
  • 进入文件夹,新建settings.json文件
{
    // 校验内容的类型
    "eslint.validate":[
        "javascript",
        "javascriptreact",
        "typescript",
        "typescriptreact"
    ],
    // 保存的时候执行,达到自动格式化
    "editor.codeActionsOnSave":{
        "source.fixAll.eslint":true
    }
}

编译分析

  • 打包完成是一个执行函数
  • 里面有一个__webpack_modules__对象,它的key就是文件地址,也就是导入路径,value是一个函数,内容是文件内容
    • 这里面存储的是处主入口文件外的其他模块
  • 之所以webpack的打包文件可以在浏览器中执行,是因为它将导入导出模块进行了一个封装
    • 我们可以看到有一个__webpack_require__模块,它代表的就是commonjs的require,执行逻辑与node中的require一致
    • 区别在于__dirname以一个变量的形式存储在函数内部
    • 内置了缓存机制,读过的文件放在__webpack_module_cache__里,再次查找会拿路径先找缓存
  • 主文件以一个自执行函数放在底部

commonjs示例

  • 新建index.js
  • 新建title.js
  • 按下方示例输入内容,然后build打包
// index.js
const title = require("./title");
console.log("打印", title);

//title.js
console.log(__dirname);
module.exports = "title";

//产出文件,自执行函数
(() => {
  // 文件,展现形式为 路径:()=>内容
  var __webpack_modules__ = {
    "./src/title.js": (module) => {
      var __dirname = "/";
      console.log(__dirname);
      module.exports = "title";
    },
  };
  // 缓存机制
  var __webpack_module_cache__ = {};
  // 封装的require函数
  function __webpack_require__(moduleId) {
    var cachedModule = __webpack_module_cache__[moduleId];
    // 查看缓存
    if (cachedModule !== undefined) {
      return cachedModule.exports;
    }
    // 没走缓存,就执行函数,且赋值到缓存中
    var module = (__webpack_module_cache__[moduleId] = {
      exports: {},
    });
    // 执行,将module、exports、require传递进去
    __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
    // 返回结果
    return module.exports;
  }
  // 主文件,直接执行
  (() => {
    const title = __webpack_require__("./src/title.js");
    console.log("打印", title);
  })();
})();

es6Modul导出,commonjs导入

  • 在common基础上拓展,但原理是相同的,封装方法,执行方法
  • 这也是在webpack中导入规则可以混用的原因

前置知识补充

  • Object.prototype.toString会读取值的Symbol.toStringTag属性,如果有的话就直接返回
  • webpack封装的时候,将其设置为Modul,表示当前模块为es6Modul
    let exports = {};
    Object.defineProperty(exports, Symbol.toStringTag, {
        value: "Module",
    });
    console.log(Object.prototype.toString.call(exports));

示例

  • 将title.js修改下
// title.js
export default "title";
export const age = "title_age";

// 产出文件
(() => {
  var __webpack_modules__ = {
    "./src/title.js": (
      __unused_webpack_module,
      __webpack_exports__,
      __webpack_require__
    ) => {
      "use strict";
      __webpack_require__.r(__webpack_exports__);
      __webpack_require__.d(__webpack_exports__, {
        age: () => age,
        default: () => __WEBPACK_DEFAULT_EXPORT__,
      });
      const __WEBPACK_DEFAULT_EXPORT__ = "title";
      const age = "title_age";
    },
  };
 // 一样的套路  
  var __webpack_module_cache__ = {};
  function __webpack_require__(moduleId) {
    var cachedModule = __webpack_module_cache__[moduleId];
    if (cachedModule !== undefined) {
      return cachedModule.exports;
    }
    var module = (__webpack_module_cache__[moduleId] = {
      exports: {},
    });
    __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
    return module.exports;
  }
  // 在__webpack_require__上拓展一些api
  (() => {
    // 数据劫持,做一层代理
    __webpack_require__.d = (exports, definition) => {
      for (var key in definition) {
        if (
          __webpack_require__.o(definition, key) &&
          !__webpack_require__.o(exports, key)
        ) {
          Object.defineProperty(exports, key, {
            enumerable: true,
            // 导出的是一个引用,函数执行获取的还是本身的变量
            get: definition[key],
          });
        }
      }
    };
  })();
  // 工具方法,查看是否为私有属性
  (() => {
    __webpack_require__.o = (obj, prop) =>
      Object.prototype.hasOwnProperty.call(obj, prop);
  })();
  // 标注模块类型  
  (() => {
    __webpack_require__.r = (exports) => {
      if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
        Object.defineProperty(exports, Symbol.toStringTag, {
          value: "Module",
        });
      }
      // 本次没用到,如果__esModule为true代表是esModule
      Object.defineProperty(exports, "__esModule", {
        value: true,
      });
    };
  })();
  var __webpack_exports__ = {};
  (() => {
    const title = __webpack_require__("./src/title.js");
    console.log("打印", title);
    console.log("age", title.age);
  })();
})();


为什么使用getter呢
  • commonjs导出的是一个值,而es6modul导出的则是一个引用
  • 那么在函数执行的时候,将值封装成一个function,fn返回当前作用域中的变量,则可以实现es6modul的这一特性
  • commonjs示例
    • 让其在1秒后修改当前作用域的值
    • 结果:1、1
// age.js
let age = 1;
exports.age = age;
setTimeout(() => {
  age = 200;
}, 1000);

//index.js
const title = require("./age");

console.log("age", title.age);

setTimeout(() => {
  console.log("age", title.age);
}, 2000);
  • es6Modul示例
    • 结果1、200
export let age = 1;

setTimeout(() => {
  age = 200;
}, 1000);
  • 理解
let age = 1;
let es6modul = {};
Object.defineProperty(es6modul, 'age', { get: () => age });
let commonjsModul = { age: age };

age = 200;

console.log(es6modul.age); // 200
console.log(commonjsModul.age); // 1

es6Modul示例

  • 同上,依然是监听exports
//title.js
export default "title";
export const age = "title_age";

//index.js
import title, { age } from "./title";

console.log(title);
console.log(age);

//编译结果
(() => {
  "use strict";
  var __webpack_modules__ = {
    "./src/title.js": (
      __unused_webpack_module,
      __webpack_exports__,
      __webpack_require__
    ) => {
      __webpack_require__.r(__webpack_exports__);
      __webpack_require__.d(__webpack_exports__, {
        age: () => age,
        default: () => __WEBPACK_DEFAULT_EXPORT__,
      });
      const __WEBPACK_DEFAULT_EXPORT__ = "title";
      const age = "title_age";
    },
  };
  var __webpack_module_cache__ = {};
  function __webpack_require__(moduleId) {
    var cachedModule = __webpack_module_cache__[moduleId];
    if (cachedModule !== undefined) {
      return cachedModule.exports;
    }
    var module = (__webpack_module_cache__[moduleId] = {
      exports: {},
    });
    __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
    return module.exports;
  }
  (() => {
    __webpack_require__.d = (exports, definition) => {
      for (var key in definition) {
        if (
          __webpack_require__.o(definition, key) &&
          !__webpack_require__.o(exports, key)
        ) {
          Object.defineProperty(exports, key, {
            enumerable: true,
            get: definition[key],
          });
        }
      }
    };
  })();
  (() => {
    __webpack_require__.o = (obj, prop) =>
      Object.prototype.hasOwnProperty.call(obj, prop);
  })();
  (() => {
    __webpack_require__.r = (exports) => {
      if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
        Object.defineProperty(exports, Symbol.toStringTag, {
          value: "Module",
        });
      }
      Object.defineProperty(exports, "__esModule", {
        value: true,
      });
    };
  })();
  var __webpack_exports__ = {};
  (() => {
    __webpack_require__.r(__webpack_exports__);
    var _title__WEBPACK_IMPORTED_MODULE_0__ =
      __webpack_require__("./src/title.js");
    console.log(_title__WEBPACK_IMPORTED_MODULE_0__["default"]);
    console.log(_title__WEBPACK_IMPORTED_MODULE_0__.age);
  })();
})();

commonjs导出,es6Modul导入

  • 会增加n方法,会通过__esModule来判断导入是否为es6Modul,如果是为true,那么default为本身的default,否则就是全部的export
//index.js
import title, { age } from "./title";

console.log(title);
console.log(age);

//title.js
module.exports = {
  name: "title_name",
  age: "title_age",
};


// 编译结果
(() => {
  var __webpack_modules__ = {
    "./src/title.js": (module) => {
      module.exports = {
        name: "title_name",
        age: "title_age",
      };
    },
  };
  var __webpack_module_cache__ = {};
  function __webpack_require__(moduleId) {
    var cachedModule = __webpack_module_cache__[moduleId];
    if (cachedModule !== undefined) {
      return cachedModule.exports;
    }
    var module = (__webpack_module_cache__[moduleId] = {
      exports: {},
    });
    __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
    return module.exports;
  }
  (() => {
    // 根据模块类型决定default的数据
    __webpack_require__.n = (module) => {
      var getter =
        module && module.__esModule ? () => module["default"] : () => module;
      // 这块没用到,可以忽略
    //   __webpack_require__.d(getter, {
    //     a: getter,
    //   });
      return getter;
    };
  })();
  (() => {
    __webpack_require__.d = (exports, definition) => {
      for (var key in definition) {
        if (
          __webpack_require__.o(definition, key) &&
          !__webpack_require__.o(exports, key)
        ) {
          Object.defineProperty(exports, key, {
            enumerable: true,
            get: definition[key],
          });
        }
      }
    };
  })();
  (() => {
    __webpack_require__.o = (obj, prop) =>
      Object.prototype.hasOwnProperty.call(obj, prop);
  })();
  (() => {
    __webpack_require__.r = (exports) => {
      if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
        Object.defineProperty(exports, Symbol.toStringTag, {
          value: "Module",
        });
      }
      Object.defineProperty(exports, "__esModule", {
        value: true,
      });
    };
  })();
  var __webpack_exports__ = {};
  (() => {
    "use strict";
    __webpack_require__.r(__webpack_exports__);
    var _title__WEBPACK_IMPORTED_MODULE_0__ =
      __webpack_require__("./src/title.js");
    // 在这里调用
    var _title__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(
      _title__WEBPACK_IMPORTED_MODULE_0__
    );
    console.log(_title__WEBPACK_IMPORTED_MODULE_0___default());
    console.log(_title__WEBPACK_IMPORTED_MODULE_0__.age);
  })();
})();

资源懒加载分析

  • 使用import(xxx),会自动分割代码,形成新的文件
  • 在入口文件中,只会引入主文件,然后调用api去引入上述分割文件,形成资源的异步加载,因为初始没有加载全部文件,所以也可以称作懒加载按需加载
    • 好处:spa带来了一个问题,大项目的代码量过大,一次性获取会造成长时间的白屏,分割后白屏情况明显减轻

逻辑走向

  • 先加载文件,调用封装好的push方法,将脚本模块内容放到全局modal里,后续引入使用,使promise成功,且删除script标签
  • 加载刚才插入的模块,然后将value返回
  • 可以获取值了
// index.js
import("./title").then((res) => {
  console.log(res.default);
});
console.log("我是主文件");

// 编译结果
// src_title_js.main.js
(self["webpackChunkwebpack"] = self["webpackChunkwebpack"] || []).push([
  ["src_title_js"],
  {
    "./src/title.js": (module) => {
      module.exports = {
        name: "title_name",
        age: "title_age",
      };
      console.log("我是异步文件");
    },
  },
]);

//main.js,好多自执行函数,看着不舒服,先删掉了
let __webpack_modules__ = {}; // 模块映射,{地址:()=>{文件内容}}
let __webpack_module_cache__ = {}; // 缓存文件
// require函数,导入文件
function __webpack_require__(moduleId) {
  let cachedModule = __webpack_module_cache__[moduleId];
  if (cachedModule !== undefined) {
    return cachedModule.exports;
  }
  let module = (__webpack_module_cache__[moduleId] = {
    exports: {},
  });
  __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
  return module.exports;
}
// 模块映射的简写
__webpack_require__.m = __webpack_modules__;
// 获取原型对象
let getProto = Object.getPrototypeOf
  ? (obj) => Object.getPrototypeOf(obj)
  : (obj) => obj.__proto__;

let leafPrototypes;

__webpack_require__.t = function (value, mode) {
  console.log(__webpack_modules__);//在这里
  console.log(__webpack_module_cache__);//这时候缓存文件中还没有
  debugger;
  //this是__webpack_require__,在这里调用的执行操作,将函数结果赋值给value
  if (mode & 1) value = this(value);
  if (mode & 8) return value;
  if (typeof value === "object" && value) {
    if (mode & 4 && value.__esModule) return value;
    if (mode & 16 && typeof value.then === "function") return value;
  }
  let ns = Object.create(null);
  __webpack_require__.r(ns);
  let def = {};
  leafPrototypes = leafPrototypes || [
    null,
    getProto({}),
    getProto([]),
    getProto(getProto),
  ];
  for (
    let current = mode & 2 && value;
    typeof current === "object" && !~leafPrototypes.indexOf(current);
    current = getProto(current)
  ) {
    Object.getOwnPropertyNames(current).forEach(
      (key) => (def[key] = () => value[key]),
    );
  }
  def.default = () => value;
  // 合并modal类型和值,然后返回
  __webpack_require__.d(ns, def);
  return ns;
};
__webpack_require__.d = (exports, definition) => {
  for (let key in definition) {
    if (
      __webpack_require__.o(definition, key)
      && !__webpack_require__.o(exports, key)
    ) {
      Object.defineProperty(exports, key, {
        enumerable: true,
        get: definition[key],
      });
    }
  }
};
__webpack_require__.f = {};
__webpack_require__.e = (chunkId) => {
  // return Promise.all(
  //   // 拿到j方法,然后将[]传过去,可以简写下
  //   Object.keys(__webpack_require__.f).reduce((promises, key) => {
  //       __webpack_require__.f[key](chunkId, promises);
  //       return promises;
  //     }, [])
  // );
  // 简写
  const promises = [];
  __webpack_require__.f.j(chunkId, promises);
  return Promise.all(promises);
};
// 补后缀
__webpack_require__.u = (chunkId) => "" + chunkId + ".main.js";
__webpack_require__.g = (function () {
  if (typeof globalThis === "object") return globalThis;
  try {
    return this || new Function("return this")();
  } catch (e) {
    if (typeof window === "object") return window;
  }
}());
// 查询是否存在
__webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
// 正在加载中的文件集合
let inProgress = {};
let dataWebpackPrefix = "webpack:";
__webpack_require__.l = (url, done, key, chunkId) => {
  // 查看是否正在加载中,本次没有走进去,可忽略
  if (inProgress[url]) {
    inProgress[url].push(done);
    return;
  }
  let script; var 
needAttach;
  // 防止有相同路径的资源加载,会补一个前缀,本次不存在该问题,可忽略
  if (key !== undefined) {
    // 获取所有script标签
    let scripts = document.getElementsByTagName("script");
    for (let i = 0; i < scripts.length; i++) {
      let s = scripts[i];
      if (
        s.getAttribute("src") == url
        || s.getAttribute("data-webpack") == dataWebpackPrefix + key
      ) {
        script = s;
        break;
      }
    }
  }
  // 创建节点,设置行间属性:字符集、src
  if (!script) {
    needAttach = true;
    // 创建dom节点
    script = document.createElement("script");
    script.charset = "utf-8";
    script.timeout = 120;
    // 不存在,不走,可忽略
    if (__webpack_require__.nc) {
      script.setAttribute("nonce", __webpack_require__.nc);
    }
    script.setAttribute("data-webpack", dataWebpackPrefix + key);
    script.src = url;
  }
  // 准备加载了,所以将集合中对应的url变为done函数,12秒后会执行
  inProgress[url] = [done];
  // 验证加载逻辑
  let onScriptComplete = (prev, event) => {
    script.onerror = script.onload = null;
    clearTimeout(timeout);
    let doneFns = inProgress[url];
    // 加载完了,删除正在加载状态
    delete inProgress[url];
    // 删除节点
    script.parentNode && script.parentNode.removeChild(script);
    // 执行done函数
    doneFns && doneFns.forEach((fn) => fn(event));
    if (prev) return prev(event);
  };
  // 12秒后验证是否加载成功
  var timeout = setTimeout(
    onScriptComplete.bind(null, undefined, {
      type: "timeout",
      target: script,
    }),
    120000,
  );
  script.onerror = onScriptComplete.bind(null, script.onerror);
  script.onload = onScriptComplete.bind(null, script.onload);
  // 将标签追加到heade中
  needAttach && document.head.appendChild(script);
};
__webpack_require__.r = (exports) => {
  if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
    Object.defineProperty(exports, Symbol.toStringTag, {
      value: "Module",
    });
  }
  Object.defineProperty(exports, "__esModule", {
    value: true,
  });
};
let scriptUrl;
if (__webpack_require__.g.importScripts) {scriptUrl = __webpack_require__.g.location + "";}
var {document} = __webpack_require__.g;
if (!scriptUrl && document) {
  if (document.currentScript) scriptUrl = document.currentScript.src;
  if (!scriptUrl) {
    let scripts = document.getElementsByTagName("script");
    if (scripts.length) scriptUrl = scripts[scripts.length - 1].src;
  }
}
if (!scriptUrl) {throw new Error("Automatic publicPath is not supported in this browser");}
scriptUrl = scriptUrl
  .replace(/#.*$/, "")
  .replace(/\?.*$/, "")
  .replace(/\/[^\/]+$/, "/");
__webpack_require__.p = scriptUrl;
// 已加载成功的资源,前缀就是路径,main是当前的mian.js,0代表加载过啦
let installedChunks = {
  main: 0,
};
__webpack_require__.f.j = (chunkId, promises) => {
  // 查看当前chunkId是否存在installedChunks中,存在返回,否则返回undefined
  let installedChunkData = __webpack_require__.o(installedChunks, chunkId)
    ? installedChunks[chunkId]
    : undefined;

  if (installedChunkData !== 0) {
    // 本次没走这里的逻辑,可以忽略
    if (installedChunkData) {
      promises.push(installedChunkData[2]);
    } else if (true) {
        // 走这里
        // 创建一个promise
        // 然后给installedChunks[chunkId]赋值为[resolve, reject],installedChunkData相同
        var promise = new Promise(
          (resolve, reject) =>(installedChunkData = installedChunks[chunkId] = [resolve, reject])
        );
        // 然后给[resolve, reject]追加当前的pending态promise,并放到promises中
        // [resolve, reject, promise]
        promises.push((installedChunkData[2] = promise));
        // 拿到起始路径,__webpack_require__.p,可以理解为pubilcPath,静态资源目录前缀,避免找不到文件
        // 然后执行__webpack_require__.u补齐后缀,因为当前的chunkId为src_title_js,而文件为src_title_js.mian.js,所以我们需要补一下.mian.js
        // 然后相加,得到完整的资源路径
        var url = __webpack_require__.p + __webpack_require__.u(chunkId);
        // 创建error实例
        var error = new Error();
        var loadingEnded = (event) => {
          if (__webpack_require__.o(installedChunks, chunkId)) {
            // 查看是否存在,上述一系列走完,肯定是存在了,不过不一定0,而是[resolve, reject, promise]
            // 那么如果载完成了就会为0,转布尔就是false,所以也不会进来
            installedChunkData = installedChunks[chunkId]; //[resolve, reject, promise]
            // 如果不是0,代表还没加载过,结束函数,并将installedChunks[chunkId]置为undefined
            if (installedChunkData !== 0) installedChunks[chunkId] = undefined;
            // 异常逻辑,抛出异常
            if (installedChunkData) {
              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;
              installedChunkData[1](error);
            }
          }
        };
        __webpack_require__.l(url, loadingEnded, "chunk-" + chunkId, chunkId);
      } else installedChunks[chunkId] = 0;
  }
};
let webpackJsonpCallback = (parentChunkLoadingFunction, data) => {
  let [chunkIds, moreModules, runtime] = data;
  let moduleId;
    var chunkId;
    var i = 0;
    console.log('进来');
  // 看加载过没有
  if (chunkIds.some((id) => installedChunks[id] !== 0)) {
    for (moduleId in moreModules) {
      if (__webpack_require__.o(moreModules, moduleId)) {
        __webpack_require__.m[moduleId] = moreModules[moduleId];
      }
    }
    if (runtime) var result = runtime(__webpack_require__);
  }
  if (parentChunkLoadingFunction) parentChunkLoadingFunction(data);
  for (; i < chunkIds.length; i++) {
    chunkId = chunkIds[i];
    if (
      __webpack_require__.o(installedChunks, chunkId)
      && installedChunks[chunkId]
    ) {
      // 在这里执行
      installedChunks[chunkId][0]();
    }
    installedChunks[chunkId] = 0;
  }
};
// self就是window
// window.webpackChunkwebpack为[]
let chunkLoadingGlobal = (self.webpackChunkwebpack =  self.webpackChunkwebpack || []);
// 忽略
chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));
// script加载完成,会执行内部逻辑,然后会走push方法
chunkLoadingGlobal.push = webpackJsonpCallback.bind(
  null,
  chunkLoadingGlobal.push.bind(chunkLoadingGlobal),
);
let __webpack_exports__ = {};
// 插入脚本,然后代码执行,会走webpackJsonpCallback逻辑,将封装好的内容和key插入到__webpack_modules__中,等后续执行调用
// 可以理解为初始化
// 加载完成删除script标签
__webpack_require__.e("src_title_js") 
// 在这里才会真正的执行导入的文件内容,拿到执行结果,然后返回,使其可以在下一步拿到
  .then(__webpack_require__.t.bind(__webpack_require__, "./src/title.js", 23)) 
  .then((res) => {
    console.log(res.default);
  });
console.log("我是主文件");


简化版伪代码

let module = {};// 盒子

function require (path) => {
    return module[path]();
};
// 导入模块的逻辑
require.e = (path) => {
    new Promise((resolve,reject) => {
    const script = document.createElement("src");
        script.src = path + ".main.js";
        script.onload = () => {
            //插入逻辑抽象到这里,源码是放在[]中然后调用插入
            module[path] = ()=>{我是异步文件内容} 
            //加载完成删除自身
            script.remove();
            resolve();
        };
        document.head.append(script);
    })
    
}
require.e("src_title_js")
    .then(require("src_title_js"))
    .then(res=>{console.log(res.default,"结果")})