为什么CommonJS和ES6可以混合使用?

1 node环境下运行

新建一个文件夹testImport,创建webpack.config.js文件,创建src/index.js 内容为空。

执行npm init

安装第三方工具 npm install airspeed

目录结构如下:

index.js写入测试内容:


import isEmptyObject from 'airspeed/isEmptyObject';

// const isEmptyObject = require('airspeed/isEmptyObject');

const d = {name: 'maile'}

console.log(isEmptyObject(d))

module.exports = d;

node src/index.js

会提示错误信息:SyntaxError: Cannot use import statement outside a module。

这是因为用node去执行index.js文件的时候,该文件运行在node环境下,支持的是CommonJs规范,不支持ES6。

const isEmptyObject = require('airspeed/isEmptyObject');

换成上面的方式进行引入,可以正常执行。

2 webpack编译

如果想让index.js文件支持es6模块,需要经过webpack编译后再使用。来配置下webpack:


const path = require('path')
const config = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: `main.min.js`,
    path: path.resolve(__dirname, 'dist'),
  }
}

module.exports = config;

安装:npm install webpack@4 webpack-cli -D

主意:目前还没有使用babel,仅仅使用了webpack来进行打包。

"scripts": {
    "build": "webpack",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

修改src/index.js


import isEmptyObject from 'airspeed/isEmptyObject';

// const isEmptyObject = require('airspeed/isEmptyObject');

const d = {name: 'maile'}

console.log(isEmptyObject(d))

export default d;

注意:引入和导出使用规范保持一致。

执行npm run build

运行 node .\dist\main.min.js

可以看到控制台打印出了false。

再看下"airspeed/isEmptyObject"的导出方式:

node_modules/airspeed/isEmptyObject.js

/**
 * 
 * @desc   判断`obj`是否为空
 * @param {Object} obj 
 * @returns {Boolean}
 */

function isEmptyObject(obj) {
  if(!obj || typeof obj !== 'object' || Array.isArray(obj)) {
    return false;
  }
  return !Object.keys(obj).length;
}
module.exports = isEmptyObject;

这个文件是用commonJs的规范导出的。后使用的过程中是用es6的规范引入的,经过webpack打包后,就支持了混合使用。

3 分析原因

先来看下webpack打包后的文件:

main.min.js

/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__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;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__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;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = "./src/index.js");
/******/ })
/************************************************************************/
/******/ ({

/***/ "./node_modules/airspeed/isEmptyObject.js":
/*!************************************************!*\
  !*** ./node_modules/airspeed/isEmptyObject.js ***!
  \************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

eval("/**\r\n * \r\n * @desc   判断`obj`是否为空\r\n * @param {Object} obj \r\n * @returns {Boolean}\r\n */\r\n\r\nfunction isEmptyObject(obj) {\r\n  if(!obj || typeof obj !== 'object' || Array.isArray(obj)) {\r\n    return false;\r\n  }\r\n  return !Object.keys(obj).length;\r\n}\r\nmodule.exports = isEmptyObject;\n\n//# sourceURL=webpack:///./node_modules/airspeed/isEmptyObject.js?");

/***/ }),

/***/ "./src/index.js":
/*!**********************!*\
  !*** ./src/index.js ***!
  \**********************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var airspeed_isEmptyObject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! airspeed/isEmptyObject */ \"./node_modules/airspeed/isEmptyObject.js\");\n/* harmony import */ var airspeed_isEmptyObject__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(airspeed_isEmptyObject__WEBPACK_IMPORTED_MODULE_0__);\n\r\n\r\n\r\n// const isEmptyObject = require('airspeed/isEmptyObject');\r\n\r\nconst d = {name: 'maile'}\r\n\r\nconsole.log(airspeed_isEmptyObject__WEBPACK_IMPORTED_MODULE_0___default()(d))\r\n\r\n/* harmony default export */ __webpack_exports__[\"default\"] = (d);\n\n//# sourceURL=webpack:///./src/index.js?");

/***/ })

/******/ });

简化一下上面的代码如下:

 (function(modules) { // webpackBootstrap

 	// The require function
 	function __webpack_require__(moduleId) {

 		// Create a new module (and put it into the cache)
 		var module = {
 			i: moduleId,
 			l: false,
 			exports: {}
 		};

 		// Execute the module function
 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

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

 	// Load entry module and return exports
 	return __webpack_require__("./src/index.js");
 })

 ({

 "./node_modules/airspeed/isEmptyObject.js":

 (function(module, exports) {
   // ./node_modules/airspeed/isEmptyObject.js的内容

  }),
 "./src/index.js":

  (function(module, __webpack_exports__, __webpack_require__) {
  
    "use strict";
    //    ./src/index.js
  })
  
});

这样看着就是不是清晰多了,一个自动执行的函数,入参是 modulesmodules是一个对象,key为入口文件和所依赖文件的路径,value是一个函数,函数体内部是处理后的文件内容。

先来看自执行函数内部,定义了一个webpack_require函数,并且

returnwebpack_require("./src/index.js"),入参是入口文件。

这个webpack_require 函数内部定义了一个module对象,带有exports属性。调用的时候会传递module,和module.exports。

modules[moduleId].call(module.exports, module, module.exports, _webpack_require); 这里调用的就是自执行函数的入参,找到对应的路径key,调用对应的函数。

"./node_modules/airspeed/isEmptyObject.js":

(function(module, exports) {

   eval("/**\r\n * \r\n * @desc   判断`obj`是否为空\r\n * @param {Object} obj \r\n * @returns {Boolean}\r\n */\r\n\r\nfunction isEmptyObject(obj) {\r\n  if(!obj || typeof obj !== 'object' || Array.isArray(obj)) {\r\n    return false;\r\n  }\r\n  return !Object.keys(obj).length;\r\n}\r\nmodule.exports = isEmptyObject;\n\n//# sourceURL=webpack:///./node_modules/airspeed/isEmptyObject.js?");

 }),

eval内部的module.exports = isEmptyObject,访问到的就是函数入参就是webpack_require函数中定义的module。

"./src/index.js":

 (function(module, __webpack_exports__, __webpack_require__) {
 
   "use strict";
   eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var airspeed_isEmptyObject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! airspeed/isEmptyObject */ \"./node_modules/airspeed/isEmptyObject.js\");\n/* harmony import */ var airspeed_isEmptyObject__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(airspeed_isEmptyObject__WEBPACK_IMPORTED_MODULE_0__);\n\r\n\r\n\r\n// const isEmptyObject = require('airspeed/isEmptyObject');\r\n\r\nconst d = {name: 'maile'}\r\n\r\nconsole.log(airspeed_isEmptyObject__WEBPACK_IMPORTED_MODULE_0___default()(d))\r\n\r\n/* harmony default export */ __webpack_exports__[\"default\"] = (d);\n\n//# sourceURL=webpack:///./src/index.js?");
   
 })

上面函数入参webpack_exports就是webpack_require函数中定义的module.exports。而webpack_require就是webpack_require这个函数。

看到eval的第一句先执行了一个函数webpack_require.r(_webpack_exports),这个函数如下:

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

大概意思就是标记一个这个文件使用的是es6规范导出的。

src/index.js文件中第一行内容:

import isEmptyObject from 'airspeed/isEmptyObject';

输入为webpack_require(/*! airspeed/isEmptyObject */ \"./node_modules/airspeed/isEmptyObject.js\")。

可以看出无论是以es6规范导出还是以CommonJs规范导出,最终都会被挂在webpack_require函数中定义的module对象上。当然,引入也是如此。

所以如果你的项目使用了webpack进行打包,就放心了混合使用两种方式了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值