module.exports so với xuất mặc định trong Node.js và ES6


317

Sự khác biệt giữa Node module.exportsvà ES6 là export defaultgì? Tôi đang cố gắng tìm ra lý do tại sao tôi gặp lỗi "__ không phải là hàm tạo" khi tôi cố gắng export defaulttrong Node.js 6.2.2.

Những gì hoạt động

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

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

// This works
module.exports = SlimShady

Những gì không làm việc

'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

Câu trả lời:


401

Vấn đề là với

  • mô-đun ES6 được mô phỏng như thế nào trong CommonJS
  • cách bạn nhập mô-đun

ES6 đến CommonJS

Tại thời điểm viết bài này, không có môi trường nào hỗ trợ các mô-đun ES6 nguyên bản. Khi sử dụng chúng trong Node.js, bạn cần sử dụng một cái gì đó như Babel để chuyển đổi các mô-đun thành CommonJS. Nhưng chính xác thì điều đó xảy ra như thế nào?

Nhiều người coi module.exports = ...là tương đương export default ...exports.foo ...tương đương export const foo = .... Điều đó không hoàn toàn đúng, hoặc ít nhất không phải là cách Babel làm điều đó.

defaultXuất khẩu ES6 thực sự cũng được đặt tên xuất khẩu, ngoại trừ đó defaultlà tên "dành riêng" và có hỗ trợ cú pháp đặc biệt cho nó. Hãy xem cách Babel biên dịch xuất khẩu có tên và mặc định:

// 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; 

Ở đây chúng ta có thể thấy rằng xuất mặc định trở thành một thuộc tính trên exportsđối tượng, giống như foo.

Nhập mô-đun

Chúng ta có thể nhập mô-đun theo hai cách: Hoặc sử dụng CommonJS hoặc sử dụng importcú pháp ES6 .

Vấn đề của bạn: Tôi tin rằng bạn đang làm một cái gì đó như:

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

hy vọng rằng nó barđược gán giá trị của xuất mặc định. Nhưng như chúng ta có thể thấy trong ví dụ trên, xuất mặc định được gán cho thuộc defaulttính!

Vì vậy, để truy cập vào xuất mặc định, chúng tôi thực sự phải làm

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

Nếu chúng ta sử dụng cú pháp mô-đun ES6, cụ thể là

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

Babel sẽ biến đổi nó thành

'use strict';

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

var _input2 = _interopRequireDefault(_input);

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

console.log(_input2.default);

Bạn có thể thấy rằng mọi quyền truy cập barđược chuyển đổi thành quyền truy cập .default.


Chúng ta không có một bản sao cho điều này?
Bergi

3
@Bergi: Tôi đã không tìm kiếm tbh (xấu hổ về tôi :(). Chắc chắn có câu hỏi về cùng một vấn đề, nhưng đã hỏi theo một cách khác. Hãy cho tôi biết nếu bạn tìm thấy điều gì đó phù hợp!
Felix Kling

1
OK, phải mất một thời gian để tìm thấy những thứ này, nhưng bây giờ bạn có thể sử dụng các quyền hạn mới có được của mình và chọn một trong những Cách sử dụng chính xác ES6 xuất khẩu mặc định với CommonJS phiên yêu cầu? Không thể yêu cầu () giá trị xuất mặc định trong Babel 6.x làm mục tiêu lừa đảo :-)
Bergi

1
Thật mỉa mai khi tôi có thể làm điều này ngay bây giờ: D
Felix Kling

1
@djKianoosh: Xem cho chính mình . Sau khi việc giao cho module.exports, exportsmodule.exportscó giá trị khác nhau, vì vậy việc chuyển nhượng để exports.defaultskhông có tác dụng (vì module.exportslà những gì được xuất khẩu). Nói cách khác, nó giống hệt như bạn chỉ làm module.exports = { ... }.
Felix Kling

1

Bạn cần cấu hình babel chính xác trong dự án của mình để sử dụng xuất mặc định và xuất const foo

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

sau đó thêm cấu hình bên dưới vào .babelrc

"plugins": [ 
       "@babel/plugin-proposal-export-default-from"
      ]

1

Felix Kling đã làm một so sánh tuyệt vời trên hai cái đó, cho bất cứ ai tự hỏi làm thế nào để làm một mặc định xuất khẩu cùng với xuất khẩu có tên với module.exports trong 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()

-61

tl; dr ngay bây giờ để làm việc này, tập tin đang yêu cầu hoặc nhập SlimShadyphải được biên dịch bằng Babel với 'use strict'.

Tôi đang sử dụng babel-cli6.18.0 trong dự án mà ban đầu tôi gặp phải lỗi này.

Không có 'use strict'là Tin xấu

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

'sử dụng nghiêm ngặt', làm ơn

'use strict'
import SlimShady from './slim-shady'
var marshall = new SlimShady()  // all good in the hood

13
Điều này không có ý nghĩa. Mỗi nguồn sử dụng importkhai báo là một mô-đun và những nguồn đã nghiêm ngặt. Sự khác biệt thực tế là về yêu cầu so với nhập khẩu.
Bergi

1
Điều gì có ý nghĩa là sử dụng importthay vì requireexport defaultthay vì exports.default.
Corey Alix


104
Đây phải là câu trả lời bị đánh giá thấp nhất mà tôi từng thấy trên stackoverflow
Jimi

4
@Jimi Đó là bởi vì đó là câu trả lời bị đánh giá thấp thứ tư trên toàn bộ trang web.
pppery
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.