YBFACC / blog

仅记录个人学习使用
3 stars 0 forks source link

CommonJS和ES Modules #36

Open YBFACC opened 3 years ago

YBFACC commented 3 years ago

CommonJS和ES Modules

导出的差异

CommonJs是对值的浅拷贝

ES Modules是对值对引用

参照参考文章中的方法使用webpack进行打包

准备

Npm 下载webpack@5.0.0-beta.26和webpack-cli@bate

按照webpack官网的配置Mode:none,不使用任何优化措施

module.exports = {
  mode: 'none',
  performance: {
    hints: false
  },
  optimization: {
    flagIncludedChunks: false,

    concatenateModules: false,
    splitChunks: {
      hidePathInfo: false,
      minSize: 10000,
      maxAsyncRequests: Infinity,
      maxInitialRequests: Infinity
    },
    noEmitOnErrors: false,
    checkWasmTypes: false,
    minimize: false
  },
  plugins: []
}

拷贝

我们使用CommonJS来进行打包

在index.js中写入

const { obj, child, change } = require('./obj')

console.log(obj, child)
change()
console.log(obj, child)

在obj.js中写入

let obj = {
  id: 'obj',
  child: {
    id: 'child'
  }
}

function change() {
  obj.child = {}
}

module.exports = { obj, child: obj.child, change }

然后我们执行得到以下结果

123

可以对象中的子对象发生了改变,证明了CommonJs是对其进行浅拷贝

使用webpack对文件进行打包,大概可以简化为以下代码

 ;(() => {
  // webpackBootstrap
  /******/ var __webpack_modules__ = [
    ,
    /* 0 */ /* 1 */
    /***/ module => {
      let obj = {
        id: 'obj',
        child: {
          id: 'child'
        }
      }

      function change() {
        obj.child = {}
      }

      module.exports = { obj, child: obj.child, change }

      /***/
    }
  ]

  // The module cache
  var __webpack_module_cache__ = {}

  // The require function
  function __webpack_require__(moduleId) {
    // Check if module is in cache
    if (__webpack_module_cache__[moduleId]) {
      return __webpack_module_cache__[moduleId].exports
    }
    // Create a new module (and put it into the cache)
    var module = (__webpack_module_cache__[moduleId] = {
      // no module.id needed
      // no module.loaded needed
      exports: {}
    })

    // Execute the module function
    __webpack_modules__[moduleId](module, module.exports, __webpack_require__)

    // Return the exports of the module
    return module.exports
  }

  ;(() => {
    const { obj, child, change } = __webpack_require__(1)

    console.log(obj, child)
    change()
    console.log(obj, child)
  })()

  /******/
})()

webpack_module_cache 存储已经导出的结果,防止二次倒出

__webpack_requirewebpack_module_cache__中的数据返回

__webpack_modules__将原来的模块文件中的代码,使用箭头函数进行包裹。(第一项为空)

PS:Js中是词法作用域,在声明时就确定了作用域

看到module.exports只是对值进行浅拷贝

引用

我们使用CommonJS来进行打包

在index.js中写入

import { obj, change, child } from './obj.js'

console.log(obj, child)
change()
console.log(obj, child)

在obj.js中写入

export let child = {
  id: 'child'
}
export let obj = {
  id: 'obj',
  child
}
export function change() {
  child = {}
}

然后我们执行得到以下结果

444

使用webpack对文件进行打包,大概可以简化为以下代码

/******/ ;(() => {
  // webpackBootstrap
  /******/ 'use strict'
  /******/ var __webpack_modules__ = [
    /* 0 */
    /***/ (
      __unused_webpack_module,
      __webpack_exports__,
      __webpack_require__
    ) => {
      __webpack_require__.r(__webpack_exports__)
      /* harmony import */ var _obj_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
        1
      )

      console.log(
        _obj_js__WEBPACK_IMPORTED_MODULE_0__.obj,
        _obj_js__WEBPACK_IMPORTED_MODULE_0__.child
      )
      Object(_obj_js__WEBPACK_IMPORTED_MODULE_0__.change)()
      console.log(
        _obj_js__WEBPACK_IMPORTED_MODULE_0__.obj,
        _obj_js__WEBPACK_IMPORTED_MODULE_0__.child
      )

      /***/
    },
    /* 1 */
    /***/ (
      __unused_webpack_module,
      __webpack_exports__,
      __webpack_require__
    ) => {
      __webpack_require__.r(__webpack_exports__)
      __webpack_require__.d(__webpack_exports__, {
        child: () => /* binding */ child,
        obj: () => /* binding */ obj,
        change: () => /* binding */ change
      })
      let child = {
        id: 'child'
      }
      let obj = {
        id: 'obj',
        child
      }
      function change() {
        child = {}
      }

      /***/
    }
  ]

  // The module cache
  var __webpack_module_cache__ = {}

  // The require function
  function __webpack_require__(moduleId) {
    // Check if module is in cache
    if (__webpack_module_cache__[moduleId]) {
      return __webpack_module_cache__[moduleId].exports
    }
    // Create a new module (and put it into the cache)
    var module = (__webpack_module_cache__[moduleId] = {
      // no module.id needed
      // no module.loaded needed
      exports: {}
    })

    // Execute the module function
    __webpack_modules__[moduleId](module, module.exports, __webpack_require__)

    // Return the exports of the module
    return module.exports
  }

  /* webpack/runtime/define property getters */
  ;(() => {
    // define getter functions for harmony 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/runtime/hasOwnProperty shorthand */
  ;(() => {
    __webpack_require__.o = (obj, prop) =>
      Object.prototype.hasOwnProperty.call(obj, prop)
  })()

  /* webpack/runtime/make namespace object */
  ;(() => {
    // define __esModule on exports
    __webpack_require__.r = exports => {
      if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
        Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' })
      }
      Object.defineProperty(exports, '__esModule', { value: true })
    }
  })()

  // startup
  // Load entry module
  __webpack_require__(0)
  // This entry module used 'exports' so it can't be inlined
})()

__webpack_require__.o确定属性是否已存在

__webpack_require__.d将definition上的属性挂载到exports

关键代码:

 __webpack_require__.d(__webpack_exports__, {
  child: () => /* binding */ child,
  obj: () => /* binding */ obj,
  change: () => /* binding */ change
})

用箭头函数动态的返回相应的值。

注意:这里虽然关键代码在模块代码执行前声明,但是没有违背let的“暂时性死区“原则。因为这里的函数会在exports使用属性时触发getter时使用。

参考

利用 webpack 理解 CommonJS 和 ES Modules 的差异

caisanli commented 3 years ago

为什么编译后的代码是用的箭头函数?

YBFACC commented 3 years ago

为什么编译后的代码是用的箭头函数?

webpack的处理的原因吧,不过这里箭头函数也确实舒服。

YBFACC commented 3 years ago

为什么编译后的代码是用的箭头函数?

箭头函数直接确定了this指向,声明了也不调用。等到你调用该模块的时候,直接返回模块的引用

caisanli commented 3 years ago

我发现webpack5默认编译时按ES6处理的 所以编译后会有箭头函数 可以设置target属性['web', 'es5']

YBFACC commented 3 years ago

我发现webpack5默认编译时按ES6处理的 所以编译后会有箭头函数 可以设置target属性['web', 'es5']

关键是这里的值引用模式。箭头函数也确实优雅