module.exports vs. standar ekspor di Node.js dan ES6

317

Apa perbedaan antara Node module.exportsdan ES6 export default? Saya mencoba mencari tahu mengapa saya mendapatkan kesalahan "__ bukan konstruktor" ketika saya mencoba export defaultdi Node.js 6.2.2.

Pekerjaan apa

'use strict'
class SlimShady {
  constructor(options) {
    this._options = options
  }

  sayName() {
    return 'My name is Slim Shady.'
  }
}

// This works
module.exports = SlimShady

Apa yang tidak berhasil?

'use strict'
class SlimShady {
  constructor(options) {
    this._options = options
  }

  sayName() {
    return 'My name is Slim Shady.'
  }
}

// This will cause the "SlimShady is not a constructor" error
// if in another file I try `let marshall = new SlimShady()`
export default SlimShady
Marty Chang
sumber

Jawaban:

402

Masalahnya adalah dengan

  • bagaimana modul ES6 ditiru di CommonJS
  • bagaimana Anda mengimpor modul

ES6 ke CommonJS

Pada saat penulisan ini, tidak ada lingkungan yang mendukung modul ES6 secara asli. Saat menggunakannya di Node.js, Anda perlu menggunakan sesuatu seperti Babel untuk mengonversi modul ke CommonJS. Tetapi bagaimana tepatnya hal itu terjadi?

Banyak orang menganggap module.exports = ...sama dengan export default ...dan exports.foo ...setara dengan export const foo = .... Itu tidak sepenuhnya benar, atau setidaknya tidak bagaimana Babel melakukannya.

defaultEkspor ES6 sebenarnya juga bernama ekspor, kecuali itu defaultadalah nama "dicadangkan" dan ada dukungan sintaksis khusus untuknya. Mari kita lihat bagaimana Babel mengkompilasi nama dan ekspor default:

// input
export const foo = 42;
export default 21;

// output
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
var foo = exports.foo = 42;
exports.default = 21; 

Di sini kita dapat melihat bahwa ekspor default menjadi properti pada exportsobjek, sama seperti foo.

Impor modul

Kita dapat mengimpor modul dengan dua cara: Baik menggunakan CommonJS atau menggunakan importsintaks ES6 .

Masalah Anda: Saya yakin Anda melakukan sesuatu seperti:

var bar = require('./input');
new bar();

mengharapkan yang bardiberi nilai ekspor standar. Tetapi seperti yang dapat kita lihat dalam contoh di atas, ekspor default ditugaskan ke defaultproperti!

Jadi untuk mengakses ekspor default, kita harus melakukannya

var bar = require('./input').default;

Jika kita menggunakan sintaks modul ES6, yaitu

import bar from './input';
console.log(bar);

Babel akan mengubahnya menjadi

'use strict';

var _input = require('./input');

var _input2 = _interopRequireDefault(_input);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

console.log(_input2.default);

Anda dapat melihat bahwa setiap akses ke bardikonversi menjadi akses .default.

Felix Kling
sumber
Bukankah kita punya duplikat untuk ini?
Bergi
3
@Bergi: Saya tidak mencari tbh (malu pada saya :(). Tentu saja ada pertanyaan tentang masalah yang sama, tetapi bertanya dengan cara yang berbeda. Beri tahu saya jika Anda menemukan sesuatu yang cocok!
Felix Kling
1
OK, butuh beberapa waktu untuk menemukan ini, tetapi Anda sekarang dapat menggunakan kekuatan yang baru Anda peroleh dan memilih salah satu dari Bagaimana cara menggunakan ES6 "standar ekspor" dengan CommonJS "memerlukan"? dan Tidak dapat meminta () nilai ekspor default dalam Babel 6.x sebagai target dupe :-)
Bergi
1
Betapa ironisnya saya bisa melakukan ini sekarang: D
Felix Kling
1
@djKianoosh: Lihat sendiri . Setelah penugasan ke module.exports, exportsdan module.exportsmemiliki nilai yang berbeda, maka penugasan ke exports.defaultstidak berpengaruh (karena module.exportsapa yang diekspor). Dengan kata lain, itu persis sama dengan jika Anda hanya melakukannya module.exports = { ... }.
Felix Kling
1

Anda perlu mengkonfigurasi babel dengan benar di proyek Anda untuk menggunakan standar ekspor dan ekspor const foo

npm install --save-dev @babel/plugin-proposal-export-default-from

lalu tambahkan konfigurasi di bawah ini di .babelrc

"plugins": [ 
       "@babel/plugin-proposal-export-default-from"
      ]
Hassan Azhar Khan
sumber
1

Felix Kling melakukan perbandingan yang bagus pada keduanya, bagi siapa pun yang bertanya-tanya bagaimana melakukan default ekspor bersama ekspor bernama module.exports in nodejs

module.exports = new DAO()
module.exports.initDAO = initDAO // append other functions a named export

// now you have
let DAO = require('_/helpers/DAO');
// DAO by default is exported class or function
DAO.initDAO()
moein rahimi
sumber
-61

tl; dr sekarang agar ini berfungsi, file yang memerlukan atau mengimpor SlimShadyharus dikompilasi menggunakan Babel dengan 'use strict'.

Saya menggunakan babel-cli6.18.0 dalam proyek di mana saya awalnya mengalami kesalahan ini.

Tanpanya 'use strict'Beruang Berita Buruk

var SlimShady = require('./slim-shady');
var marshall = new SlimShady();  // uh, oh...

tolong gunakan 'ketat'

'use strict'
import SlimShady from './slim-shady'
var marshall = new SlimShady()  // all good in the hood
Marty Chang
sumber
13
Ini tidak masuk akal. Setiap sumber yang menggunakan importdeklarasi adalah modul, dan yang sudah ketat. Perbedaan sebenarnya adalah tentang mengharuskan vs mengimpor.
Bergi
1
Apa yang masuk akal adalah menggunakan importbukan requiredan export defaultbukannya exports.default.
Corey Alix
104
Ini harus menjadi jawaban paling downvoted yang pernah saya lihat di stackoverflow
Jimi
4
@ Jimi Itu karena itu jawaban keempat yang paling banyak diturunkan pendapatnya di seluruh situs.
pppery