It was a brilliant idea of mine

I recently ran into a small problem using export Default in my Vue-CLI project. Export default = export default = export default = export default

// a.js
import person from './b'
const { name, age } = person
console.log(name, age)

// b.js
export default {
    name: 'MiKiMiKi',
    age: 18
}
Copy the code

At this point, the idea came to the following es6 deconstruction usage:

// a.js
import { name, age } from './b'
console.log(name, age)
Copy the code

So a look, is not a little witty, and a little concise, packaged, helpless to see such a scene:

A little panic, error, can not find the corresponding attribute, what is it? Let’s explore.

[Description] : The above example is a simulated demo, consistent with the actual “case”.

To explore the process

The WebPack project is initialized

Create a new WebPack project and restore the scene

The process is as follows:

mkdir demo
cd demo
cnpm init
Copy the code

Generate package.json and install dependencies:

cnpm webpack webpack-cli -D
cnpm install babel-loader @babel/core @babel/preset-env -D
Copy the code
  • Webpack-cli is used to use the webpack command
  • Export Default belongs to es6 syntax. Therefore, Babel is required for compilation to solve compatibility problems.
  • To use Babel-Loader, you also need to install the accompanying core libraries@babel/coreAnd for syntactic conversions@babel/preset-env

package.json :

"DevDependencies" : {" @ Babel/core ":" ^ 7.12.7 ", "@ Babel/preset - env" : "^ 7.12.7", "Babel - loader" : "^ 8.2.1", "webpack" : "^ 5.6.0 webpack -", "cli" : "^ 4.2.0"}Copy the code

New webpack. Config. Js:

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  }
}
Copy the code

Create index.js, utils.js:

// index.js
import person from './utils'
const { name, age } = person

console.log(name, age)

// utils.js
export default {
    name: 'MiKiMiKi',
    age: 18
}
Copy the code

Package in the None mode

To make the package easier to see, set mode to None and execute on the command line

webpack --mode none
Copy the code

By default, the package generates dist/main.js. Open this file and think about it:

Analyze the typing file main.js

1. Array of file headers

First look at the header of the file:

Here we define an array that contains two functions. You can clearly see that these are the two modules packaged, the entry index.js and its reference utils.js

2. Rear entrance

Omit the series definitions in the middle and go to the end of the file first

// startup
// Load entry module
__webpack_require__(0);
Copy the code

The entry module is loaded, and 0 corresponds to the first element of the array defined above, i.e. the index.js module.

Ok, we know that __webpack_require__ is used here to load the module. If you look at the definition of __webpack_require__, it’s actually a function that omits some key code extracts that aren’t relevant to this analysis. It looks something like this:

function __webpack_require__(moduleId) {
    // ...
    var module = {
        exports: {}
    };
    
    __webpack_modules__[moduleId](module, module.exports, __webpack_require__)

    return module.exports;
}
Copy the code

__webpack_modules__ is the module array defined above, moduleId is the module ID, and this function returns module.exports objects, which are exported by the module.

3. The utils module

Delete comments and take a look:

((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

  __webpack_require__.r(__webpack_exports__);

  __webpack_require__.d(__webpack_exports__, {
    "default": () => __WEBPACK_DEFAULT_EXPORT__
  });
  const __WEBPACK_DEFAULT_EXPORT__ = ({
    name: 'MiKiMiKi',
    age: 18
  });
})
Copy the code

You can probably guess that what we’re doing here is exporting an object that has a property called default. Combining the following function definition with the middle __webpack_require__.d method definition we can see that this is equivalent to:

"use strict"
_default = {
    name: 'MiKiMiKi',
    age: 18
}
exports.default = _default
module.exports = exports.default
Copy the code

All of this seems to be in line with expectations. We know that the utils module exports something roughly related to default, but it’s never quite clear what that means.

4. The index module

Here console.log(name, age) converts name and age to:

var name = _utils__WEBPACK_IMPORTED_MODULE_0__.default.name,
    age = _utils__WEBPACK_IMPORTED_MODULE_0__.default.age
Copy the code

It takes the property under the _utils__webpack_imported_module_0__.default object, Obviously, this utils__WEBPACK_IMPORTED_MODULE_0_. Default is an object exported by the utils module, which is referenced by the module with import person from ‘./utils’. That is to say,

Person => _utils__webpack_imported_module_0__.default, so we can deconstruct person using ES6 syntax, which is fine.

Here, what is _utils__WEBPACK_IMPORTED_MODULE_0__, is defined as follows:

var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1)
Copy the code

It is known from the previous analysis that __webpack_require__ is a function for module loading, where the 1 is the id of the utils.js module, and ultimately returns the object exported by the utils.js module.

To summarize, import person from ‘./utils’ actually does two things:

1).__webpack_require__ Import module _utils__WEBPACK_IMPORTED_MODULE_0__ 2). Assign _utils__webpack_imported_module_0__. default to person

At this point, if anything is clear, review the first usage:

import { name, age } from './utils'
Copy the code

This is the same thing as

1).__webpack_require__ Import module _utils__WEBPACK_IMPORTED_MODULE_0__ 2). Destruct _utils__WEBPACK_IMPORTED_MODULE_0__ directly when we need _utils__webpack_imported_module_0__.default.

Naturally, this usage does not get the “name” and “age” properties as _utils__WEBPACK_IMPORTED_MODULE_0__ does not contain either of these properties and exports only the default property!

Further explorationexport default

At this point, you can guess another way to write it

import { default as person } from './utils'
Copy the code

Package, successful, no error! The person here can also be deconstructed directly to get the properties inside.

We know that by exporting {… } When exporting, we use import {… } from ‘./utils’, where import {default as person} from ‘./utils’ shows that export default is a syntactic sugar, which means that the two usages are equivalent

// let obj = {} export default obj = {} export {obj as default}Copy the code

And that turns out to be the case, because the packaging is similar, as you can see in the comparison belowexport default)The only difference is the useexport defaultIt assigns obj to a variable, and that’s it.

Import {default: Person} from ‘./utils’; person {} from ‘./utils’; person {} from ‘.

And if we just use export {}

Let obj = {} export {obj}Copy the code

After packing:

Import {obj} from ‘./utils’ is a valid reference

It is also possible to import using CommonJS syntax. Require is the “portal” of module.exports

Const person = require('./utils').default // or const {default: person} = require('./utils')Copy the code

Conclusion:

  1. Export default is used in ES6. rightexport defaultUse WebPack +BabelThe results are simplified as follows:
"use strict"
_default = {
    name: 'MiKiMiKi',
    age: 18
}
exports.default = _default
module.exports = exports.default
Copy the code

For a module exported using export Default, there are three ways to reference it in another file:

// 1
import person from './utils'
// 2
import { default as person } from './utils'
// 3
const person = require('./utils').default
Copy the code
  1. export default objexport { obj as default }The syntactic sugar
  2. export defaultIt is easy to use, it only exports a “thing”, modules are generally relatively simple, when we do not want or think it is not worth the name of the module to directly export the things we want to export. But in many cases, we might not recommend itexport defaultAnd theexport {}Perhaps more in line with expectations. If you are interested, read the following article series:ES Module 1: Disable export Default Object,ES Module 2: Disable export Default Object

[Brief summary and supplement] :

  • Error prone, the ESM can be imported or exported in multiple ways, which may complicate the situation.
  • The interaction between ESM and CJS becomes relatively complicated when default is involved.
  • This can add to the cost of code migration due to different packaging methods.
  • In dynamic import, the default export exposes itself under the name default.
  • .
  1. es6 importNot the kind of deconstruction we normally do to variables, butnamed importsAlthough grammatically similar do not mix up. More on import can be foundimport –MDN
  2. Note: The examples, analysis, and conclusions above are based on WebPack +Babel7, and the results may not apply when you use other packaging tools like Rollup. Before Babel 6, the wrong use of “magic machine” was supported, but after Babel 6, it is not.
  3. There are many scenarios, complex and changeable, no matter what the situation, hands-on practice, maybe you will have an unexpected discovery, good luck!

Reference:

  • Compile and convert ES6+ code using Babel-Loader
  • babel issues#2047 –github
  • Export Default is considered harmful