web-dev-qa-db-ja.com

BabelとES6での予期しない「Uncaught TypeError:XXX is not constructor」エラー

私はWebpackを試してみて、 このチュートリアル の指示を試してみてください。

これは本当に単純なコードですが、このエラーについては非常に困惑しており、これは私が見逃した愚かなことだと感じています。

それぞれHandlebarsテンプレートに対応する2つのES6クラスを定義しました。アプリのエントリポイントは、インデックスファイル内のプレースホルダーHTMLをその内容で置き換えることになっています。

エントリポイント:

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
    }
};
34
Silver Quettier

何が欠けているのでしょうか?

Babelは、デフォルトのエクスポートをdefaultプロパティに割り当てます。したがって、requireを使用してES6モジュールをインポートする場合、defaultプロパティにアクセスする必要があります。

const Button = require('./Components/Button.js').default;
55
Felix Kling

私はあなたがすでに答えを持っていることを理解しています。しかし、答えを見つけたのと同様の問題がありました。私自身の質問を開始し、それに答えるのは奇妙に思えます。だからここに置いておきます。

あなたと同じエラーがありました。しかし、私は自分の

export default {Class}

export default Class

クラスをオブジェクトでラップした理由はわかりませんが、どこかで見たのを覚えているので、使い始めました。

したがって、デフォルトでクラスを返す代わりに、このようなオブジェクトを返しました{Class: Class}。これは完全に有効ですが、webpack + babelが壊れます。

編集:私は以来、これがおそらくbabel + webpackを壊す理由を知るようになりました。 export defaultは、1つのエクスポートのみを対象としています。 javascript-objectには多くのプロパティを含めることができます。つまり、複数のエクスポートが可能です。 (参照: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export )。

複数のエクスポートを使用する場合:export {definition1, definition2}

ユースケース:さまざまなタイプのエディターをエクスポートするライブラリーを作成した状況でこれを使用しました(基礎となるコードは同じでしたが、使用するエクスポートによってエディターの外観が変わります)。

9
Byebye

単にexport var __useDefault = true;クラスをエクスポートした直後。

export default class Header {
...
} 
export var __useDefault = true;
6
maufarinelli

この特定の質問の問題ではありませんが、何らかの理由で、Babelは同じファイル内のクラスをホイストしません。

そのため、クラスのTokenをファイルの先頭で宣言し、後でnew Token()と記述すると、実行されます。

コンストラクターの呼び出し後にクラスを宣言すると、xxxはコンストラクターではありませんエラーが発生します

1
Nicolas Zozol

これは特定の問題の原因ではありませんが、ES6のimportおよびexport構文を使用していた既存のノードアプリからbabelをリッピングしようとすると、非常によく似た問題に遭遇しました。投稿は、将来これに苦労している人を助けることです。

Babelは、あるモジュールと別のモジュール間の循環依存関係を解決するため、ES6のimportexportを無謀な放棄とともに使用できます。ただし、babelを削除してネイティブノードを使用する必要がある場合は、importexportsrequireに置き換える必要があります。これにより、バベルがバックグラウンドで処理していた潜在的な循環参照の問題を再導入できます。この状況に陥った場合は、コード内で次のような領域を探してください。

ファイル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をロードするときに参照を動的に解決することもできます。他の無数の選択肢がありますが、これは始めるのに適した場所です。

0
xtro