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
})
});
这样看着就是不是清晰多了,一个自动执行的函数,入参是 modules
。modules
是一个对象,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进行打包,就放心了混合使用两种方式了。