I’m writing this analysis to track down a modular conversion bug in Babel. I’m not clear about it yet, so I’m writing this detailed analysis and will update this post when the results are available.
The following sections discuss only the Babel and CMD module styles, although Babel can also use AMD-style conversion modules.
Module export
export const InlineExport = { } const NormalExport = { } const RenameExport = { } const DefaultExport = { } export { NormalExport} export {RenameExport as HasRenamed} export default DefaultExport // "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var InlineExport = exports.InlineExport = {}; var NormalExport = {}; var RenameExport = {}; var DefaultExport = {}; exports.NormalExport = NormalExport; exports.HasRenamed = RenameExport; exports.default = DefaultExport;Copy the code
Module is introduced into
import { NormalExport } from 'normal' import { HasRenamed as RenameAgain } from 'rename' import DefaultExport from 'default' import * as All from 'All' NormalExport() RenameAgain() DefaultExport() All() var _normal = require('normal'); var _rename = require('rename'); var _default = require('default'); var _default2 = _interopRequireDefault(_default); var _all = require('all'); var all = _interopRequireWildcard(_all); (0, _normal.NormalExport)(); (0, _rename.HasRenamed)(); (0, _default2.default)(); all.hello(); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj ! = null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; }}Copy the code
The code is very readable, but there are two problems
- Pass when exporting the module
Object.defineProperty
The definition of theexports.__esModule
What’s the use? - Why do modules use comma expressions when executed after introduction?
To solve these two puzzles, it is important to understand the difference and connection between ES6’s modular solution and CommonJS solution.
Understand the difference between ES6 Module and CommonJS
CommonJS solution features:
- All objects to be exported are mounted in
module.exports
And expose it to the outside world - through
require
Load other modules,require
The return value of is the object exposed by the module - CommonJS is a single-object output, single-object loaded model
Let’s take a look at the modularity features of ES6:
- Any reference to the interior of a module can be output as follows
export { A, B }
export { A as a, B }
export default A
export const A = { }
- Load any reference to the output in the module as follows
import A from './module'
import * as A from './module'
import { A, B } from './module'
import { A as a, B } from './module'
- ES6 Module is a multi-object output, multi-object load model
Understand the connection between ES6 Modules and CommonJS
Most browsers don’t support ES6 modules, so we’re going to use Babel to convert ES6 modules to CommonJS. Then use a packaging tool like Browserify or Webpack to wrap them up. (An example of Browserify wrapped code is provided at the end of this article, which will look messy if you’re not familiar with it.)
Then there is the problem that ES6’s modular mechanics are so different from CommonJS that Babel needs to be tweaked to meet ES6 standards.
So Babel gets into his game
How does Babel convert ES6 modules?
The two sides of the code presented at the beginning of this article are the conversion methods of Babel, and now I explain how Babel does it.
Babel still exports references to modules through the exports object, but adds a special exports.default property to implement the ES6 default output object. And the module is still loaded via require.
The answer to question one
Adding an __esModule to the output object of a module is intended to convert CommonJS modules that do not conform to Babel requirements into compliant modules, as reflected in require. If a module is loaded with an __esModule attribute, Babel knows that it must have converted the module, so it can safely call the exported object, exports.default, from the loaded module. This is the default export object specified in ES6, so this module meets both CommonJS standards and Babel’s requirements for ES6 modularity. However, if __esModule doesn’t exist, it doesn’t matter. When Babel loads a module that can’t detect __esModule, it knows that the module, while conforming to CommonJS standards, is probably a third-party module, and Babel hasn’t converted it. If you call exports.default directly, you will get an error, so now you can add a default attribute to it, so you will not get an error in the future.
The answer to question two
This comma expression is a JavaScript language feature. It means that the entire comma expression is executed from left to right, and then the value of the comma expression is equal to the value of the expression after the last comma. This is the same with other C-like languages like C/C++.
If (0, foo.bar)() is executed, the comma expression is equivalent to executing foo.bar(), but the context of execution is bound to the global object. So it’s really equivalent to executing foo.bar.call(GLOBAL_OBJECT).
The packaged code example mentioned earlier
(function e(t,n,r){function s(o,u){if(! n[o]){if(! t[o]){var a=typeof require=="function"&&require; if(! u&&a)return a(o,! 0); if(i)return i(o,! 0); var f=new Error("Cannot find module '"+o+"'"); throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}}; t[o][0].call(l.exports,function(e){var n=t[o][1][e]; return s(n? n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require; for(var o=0; o<r.length; o++)s(r[o]); return s})({1:[function(require,module,exports){ 'use strict'; var _lib = require('./lib.js'); var _lib2 = _interopRequireDefault(_lib); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } if (window && window.alert) { alert(_lib2.default); } else { console.log(_lib2.default); } },{"./lib.js":2}],2:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _classCallCheck = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck2 = _interopRequireDefault(_classCallCheck); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } exports.default = 'hello, world'; },{"babel-runtime/helpers/classCallCheck":3}],3:[function(require,module,exports){ "use strict"; exports.__esModule = true; exports.default = function (instance, Constructor) { if (! (instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; },{}]},{},[1]);Copy the code
Refer to the link
- Babel and CommonJS modules