专业编程教程与实战项目分享平台

网站首页 > 技术文章 正文

前端面试-工程化-webpack模块加载机制分析

ins518 2025-05-02 18:54:10 技术文章 20 ℃ 0 评论

1. 同步加载

没有依赖

src\index.js

Bash
console.log(title)
复制代码

bundle.js

Bash
//导出对象
var exports = {}
//模块内容
console.log(title)
复制代码

打包模块分析

src\index.js

let title = require("./title.js");
console.log(title);
复制代码

src\title.js

module.exports = "title"
复制代码

bundle.js

未加入缓存

//模块定义
//key是模块ID,也就是模块相对于相前根目录的相对路径
var modules = {
  "./src/title.js": (module) => {
    module.exports = "title"
  },
}
//加载模块,执行 modules 对应的函数
function require(moduleId) {
  var module = {
    exports: {},
  }
  modules[moduleId](module, module.exports, require)
  return module.exports
}

//入口
var exports = {}
let title = require("./src/title.js")
console.log(title)
复制代码

2. 兼容性实现

2.1 common.js 加载 common.js

2.1.1 index.js

let title = require("./title")
console.log(title.name)
console.log(title.age)
复制代码

2.1.2 title.js

exports.name = "title_name"
exports.age = "title_age"
复制代码

2.1.3 bundle.js

;(() => {
  //需要加载的模块
  var modules = {
    "./src/title.js": (module, exports) => {
      exports.name = "title_name"
      exports.age = "title_age"
    },
  }
  //缓存
  var cache = {}
  //require 方法
  function require(moduleId) {
    var cachedModule = cache[moduleId]
    if (cachedModule !== undefined) {
      return cachedModule.exports
    }
    var module = (cache[moduleId] = {
      exports: {},
    })
    modules[moduleId](module, module.exports, require)
    return module.exports
  }
  // 入口
  var exports = {}
  ;(() => {
    let title = require("./src/title.js")
    console.log(title.name)
    console.log(title.age)
  })()
})()
复制代码

2.2 common.js 加载 ES6 modules

2.2.1 index.js

let title = require("./title")
console.log(title)
console.log(title.age)
复制代码

2.2.2 title.js

export default "title_name"
export const age = "title_age"
复制代码

2.2.3 bundle.js

去除了自执行函数和模块缓存

  • 打包前面是 commonjs 打包后不需要变,打包前是 esmodule 打包后得变
/**
 * 如果原模块是esmodule
 * 先执行require.r
 * 再执行require.d
 */
var modules = {
  "./src/title.js": (module, exports, require) => {
    //1.声明或者说表示当前的模块原来是一个es module
    require.r(exports)
    //2. 定义属性
    require.d(exports, {
      age: () => age,
      default: () => DEFAULT_EXPORTS, //值是一个getter
    })
    //默认导出
    const DEFAULT_EXPORTS = "title_name"
    //命名导出
    const age = "title_age"
  },
}
/**
 * 执行modules对象对应的模块函数
 * @param {*} moduleId 模块Id
 * @returns module.exports
 */
function require(moduleId) {
  var module = {
    exports: {},
  }
  modules[moduleId](module, module.exports, require)
  return module.exports
}
/**
 * 给exports 上面定义属性
 * @param {*} exports 导出对象
 * @param {*} definition 定义的属性
 */
require.d = (exports, definition) => {
  //遍历key
  for (var key in definition) {
    //在 definition 上不在 exports 上就赋值
    if (require.o(definition, key) && !require.o(exports, key)) {
      // 给exports 上面定义属性 geT 获取
      Object.defineProperty(exports, key, {
        enumerable: true,
        get: definition[key],
      })
    }
  }
}
//对象自身属性中是否具有指定的属性
require.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
/**
 * 给exports 声明 Symbol.toStringTag为Module ,__esModule 未true
 * @param {*} exports
 */
require.r = (exports) => {
  if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
    Object.defineProperty(exports, Symbol.toStringTag, {
      value: "Module",
    })
  }
  Object.defineProperty(exports, "__esModule", {
    value: true,
  })
}

var exports = {}

let title = require("./src/title.js")
console.log(title)
console.log(title.default)
console.log(title.age)
复制代码

2.3 ES6 modules 加载 ES6 modules

2.3.1 index.js

import name, { age } from "./title"
console.log(name)
console.log(age)
复制代码

2.3.2 title.js

export default name = "title_name"
export const age = "title_age"
复制代码

2.3.3 bundle.js

/**
 * 如果原模块是esmodule
 * 先执行require.r
 * 再执行require.d
 */
var modules = {
  "./src/title.js": (module, exports, require) => {
    //1.声明或者说表示当前的模块原来是一个es module

    require.r(exports)
    //2. 定义属性
    require.d(exports, {
      age: () => age,
      default: () => _DEFAULT_EXPORT__,
    })
    // 此处为了实现Livbinding做准备
    const _DEFAULT_EXPORT__ = (name = "title_name")
    const age = "title_age"
  },
}
var cache = {}
/**
 * 执行modules对象对应的模块函数
 * @param {*} moduleId 模块Id
 * @returns module.exports
 */
function require(moduleId) {
  var cachedModule = cache[moduleId]
  if (cachedModule !== undefined) {
    return cachedModule.exports
  }
  var module = (cache[moduleId] = {
    exports: {},
  })
  modules[moduleId](module, module.exports, require)
  return module.exports
}
/**
 * 给exports 上面定义属性
 * @param {*} exports 导出对象
 * @param {*} definition 定义的属性
 */
require.d = (exports, definition) => {
  for (var key in definition) {
    if (require.o(definition, key) && !require.o(exports, key)) {
      Object.defineProperty(exports, key, {
        enumerable: true,
        get: definition[key],
      })
    }
  }
}

require.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
/**
 * 给exports 声明 Symbol.toStringTag为Module ,__esModule 未true
 * @param {*} exports
 */
require.r = (exports) => {
  if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
    Object.defineProperty(exports, Symbol.toStringTag, {
      value: "Module",
    })
  }
  Object.defineProperty(exports, "__esModule", {
    value: true,
  })
}

//入口
var exports = {}
//标明是esModule模块
require.r(exports)
//加载对应的模块
var _title_0__ = require("./src/title.js")
//取值
console.log(_title_0__["default"])
console.log(_title_0__.age)
复制代码

2.4 ES6 modules 加载 common.js

2.4.1 index.js

import name, { age } from "./title"
console.log(name)
console.log(age)
复制代码

2.4.2 title.js

module.exports = {
  name: "title_name",
  age: "title_age",
}
复制代码

2.4.3 bundle.js

/**
 * 如果原模块是esmodule
 * 先执行require.r
 * 再执行require.d
 */
var modules = {
  "./src/title.js": (module, exports, require) => {
    //1.声明或者说表示当前的模块原来是一个es module

    require.r(exports)
    //2. 定义属性
    require.d(exports, {
      age: () => age,
      default: () => _DEFAULT_EXPORT__,
    })
    // 此处为了实现Livbinding做准备
    const _DEFAULT_EXPORT__ = (name = "title_name")
    const age = "title_age"
  },
}
var cache = {}
/**
 * 执行modules对象对应的模块函数
 * @param {*} moduleId 模块Id
 * @returns module.exports
 */
function require(moduleId) {
  var cachedModule = cache[moduleId]
  if (cachedModule !== undefined) {
    return cachedModule.exports
  }
  var module = (cache[moduleId] = {
    exports: {},
  })
  modules[moduleId](module, module.exports, require)
  return module.exports
}
/**
 * 给exports 上面定义属性
 * @param {*} exports 导出对象
 * @param {*} definition 定义的属性
 */
require.d = (exports, definition) => {
  for (var key in definition) {
    if (require.o(definition, key) && !require.o(exports, key)) {
      Object.defineProperty(exports, key, {
        enumerable: true,
        get: definition[key],
      })
    }
  }
}

require.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
/**
 * 给exports 声明 Symbol.toStringTag为Module ,__esModule 未true
 * @param {*} exports
 */
require.r = (exports) => {
  if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
    Object.defineProperty(exports, Symbol.toStringTag, {
      value: "Module",
    })
  }
  Object.defineProperty(exports, "__esModule", {
    value: true,
  })
}

//入口
var exports = {}
//标明是esModule模块
require.r(exports)
//加载对应的模块
var _title_0__ = require("./src/title.js")
//取值
console.log(_title_0__["default"])
console.log(_title_0__.age)
复制代码

3 总结

核心方法

  • **modules 对象 ** key 是模块 ID,也就是模块相对于相前根目录的相对路径 值为对应加载模块的内容函数
  • require 方法 执行 modules 对象对应的模块函数 返回 modules.exports 对象
  • require.d 方法 通过 defineProperty 给 exports 上设置属性 get 获取
  • require.o 方法 对象自身属性中是否具有指定的属性
  • require.r 方法 标明该模块是 esModele 模块
  • require.n 方法 返回函数兼容性处理默认值 ,esModule 模块 是的返回 module["default"] 否则 commonjs 模块返回本身

兼容处理

  • common.js 加载 common.js 直接调用 require 方法 执行 modules 对象对应的函数返回 modules.exports 对象
  • common.js 加载 ES6 modules
  1. 直接调用 require 方法
  2. 执行 modules 对象对应的函数 调用 require.r 方法 标明该模块为 esModule 调用 require.d 方法 给 export 对象赋值
  3. 返回 modules.exports
  • ES6 modules 加载 ES6 modules
  • 模块入口 调用 require.r 标明是 esModule 模块
  • 调用 require 方法 加载模块 调用 require.r 标明被加载的模块是 esModule 调用 require.d 方法 给 export 对象赋值
  • 返回 加载的内容 modules.exports
  • ES6 modules 加载 common.js
  • 模块入口 调用 require.r 标明是 esModule 模块、
  • 调用 require 方法 加载模块 返回对应模块内容
  • 兼容处理返回的默认值 调用 require.n

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表