Неожиданный "Uncaught TypeError: XXX не является конструктором" ошибок с Babel и ES6
Я пытаюсь использовать Webpack, и я пробую инструкции в этом учебнике, дайте или возьмите несколько пользовательских вещей.
Это простой код, действительно, но я довольно озадачен этой ошибкой и чувствую, что это что-то глупое, что я пропустил.
Я определил два класса ES6, каждый из которых соответствует шаблону Handlebars, и моя точка входа в приложение должна заменить местозаполнитель HTML в индексном файле своим содержимым:
Entrypoint:
import './bloj.less'
// If we have a link, render the Button component on it
if (document.querySelectorAll('a').length) {
require.ensure([], () => {
const Button = require('./Components/Button.js');
const button = new Button('9gag.com');
button.render('a');
}, 'button');
}
// If we have a title, render the Header component on it
if (document.querySelectorAll('h1').length) {
require.ensure([], () => {
const Header = require('./Components/Header.js');
new Header().render('h1');
}, 'header');
}
Индекс
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>My title</h1>
<a>Click me</a>
<script src="build/bloj.js"></script>
</body>
</html>
Кнопка:
import $ from 'jquery';
import './Button.less';
export default class Button {
constructor(link) {
this.link = link;
}
onClick(event) {
event.preventDefault();
alert(this.link);
}
render(node) {
const text = $(node).text();
var compiled = require('./Button.hbs');
// Render our button
$(node).html(
compiled({"text": text, "link": this.link})
);
// Attach our listeners
$('.button').click(this.onClick.bind(this));
}
}
Заголовок:
import $ from 'jquery';
import './Header.less';
export default class Header {
render(node) {
const text = $(node).text();
var compiled = require('./Header.hbs');
// Render the header
$(node).html(
compiled({"text": text})
);
}
}
К сожалению, это не сработает, и я получаю обе эти ошибки при отображении страницы:
Uncaught TypeError: Header is not a constructor
Uncaught TypeError: Button is not a constructor
Что я могу потерять?
Вот моя конфигурация webpack:
var path = require('path');
var webpack = require('webpack');
var CleanPlugin = require('clean-webpack-plugin');
var ExtractPlugin = require('extract-text-webpack-plugin');
var production = process.env.NODE_ENV === 'production';
var appName = 'bloj';
var entryPoint = './src/bloj.js';
var outputDir = './build/';
var publicDir = './build/';
// ************************************************************************** //
var plugins = [
//new ExtractPlugin(appName + '.css', {allChunks: true}),
new CleanPlugin(outputDir),
new webpack.optimize.CommonsChunkPlugin({
name: 'main',
children: true,
minChunks: 2
})
];
if (production) {
plugins = plugins.concat([
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.MinChunkSizePlugin({
minChunkSize: 51200 // 50ko
}),
new webpack.optimize.UglifyJsPlugin({
mangle: true,
compress: {
warnings: false // Suppress uglification warnings
}
}),
new webpack.DefinePlugin({
__SERVER__: false,
__DEVELOPMENT__: false,
__DEVTOOLS__: false,
'process.env': {
BABEL_ENV: JSON.stringify(process.env.NODE_ENV)
}
})
]);
}
module.exports = {
entry: entryPoint,
output: {
path: outputDir,
filename: appName + '.js',
chunkFilename: '[name].js',
publicPath: publicDir
},
debug: !production,
devtool: production ? false : 'eval',
module: {
loaders: [
{
test: /\.js/,
loader: "babel",
include: path.resolve(__dirname, 'src'),
query: {
presets: ['es2015']
}
},
{
test: /\.less/,
//loader: ExtractPlugin.extract('style', 'css!less')
loader: "style!css!less"
},
{
test: /\.html/,
loader: 'html'
},
{
test: /\.hbs/,
loader: "handlebars-template-loader"
}
]
},
plugins: plugins,
node: {
fs: "empty" // Avoids Handlebars error messages
}
};
Ответы
Ответ 1
Что я могу потерять?
Бабель присваивает экспорт по умолчанию свойству default
. Поэтому, если вы используете require
для импорта модулей ES6, вам нужно получить доступ к свойству default
:
const Button = require('./Components/Button.js').default;
Ответ 2
Я понимаю, что у вас уже есть ответ. Однако у меня была аналогичная проблема, на которую я нашел ответ. Запуск моего собственного вопроса и ответ на него кажется странным.
Поэтому я просто собираюсь оставить это здесь.
У меня была такая же ошибка, как и у вас. Однако мне удалось решить это, изменив
export default {Class}
to
export default Class
Я не знаю, почему я завернул класс в объект, но я помню, что видел его где-то, поэтому я только начал его использовать.
Поэтому вместо того, чтобы по умолчанию возвращать класс, он возвращал объект, подобный этому {Class: Class}
.
Это совершенно верно, но он сломает webpack + babel.
EDIT: С тех пор я узнал, почему это, вероятно, нарушает babel + webpack. export default
имеет только 1 экспорт. Объект javascript может содержать много свойств. Это означает, что он может иметь более одного экспорта. (См.: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export).
Для множественного экспорта используйте: export {definition1, definition2}
.
Случай использования: я использовал это в ситуации, когда я создал библиотеку, которая экспортировала различные типы редактора (в то время как базовый код был одинаков, внешний вид редактора меняется в зависимости от того, какой экспорт вы используете).
Ответ 3
Вы можете просто положить export var __useDefault = true;
сразу после экспорта вашего класса.
export default class Header {
...
}
export var __useDefault = true;
Ответ 4
Несмотря на то, что это не является причиной вашей конкретной проблемы, я столкнулся с очень похожими проблемами при попытке вырезать Babel из существующего приложения node, использующего синтаксис ES6 import
и export
, поэтому этот пост заключается в том, чтобы помочь кому-либо еще бороться с этим в будущем.
Babel будет разрешать любые круговые зависимости между одним модулем и другим, поэтому вы можете использовать ES6 import
и export
с отказом от отката. Однако, если вам нужно избавиться от babel и использовать native node, вам нужно будет заменить любые import
и exports
на require
. Это может повторить скрытые круглые эталонные вопросы, которые babel заботился в фоновом режиме. Если вы оказались в этой ситуации, найдите область в своем коде, которая выглядит так:
Файл A:
const B = require('B');
class A {
constructor() {
this.b = new B();
}
}
module.exports = A;
Файл B:
const A = require('A'); // this line causes the error
class B {
constructor() {
this.a = new A();
}
}
module.exports = B;
Существует несколько способов решения этой проблемы в зависимости от того, как вы структурировали свой код. Самый простой способ - передать B
ссылку на A
вместо создания нового экземпляра класса A
. Вы можете также динамически разрешить ссылку при загрузке A
. Существует множество других альтернатив, но это хорошее место для начала.