web-dev-qa-db-fra.com

Erreurs "Uncaught TypeError: XXX n'est pas un constructeur" inattendues avec Babel et ES6

J'essaie Webpack, et j'essaie les instructions de ce tutoriel , donne ou prend quelques éléments personnalisés.

C'est du code simple, vraiment, mais cette erreur me laisse perplexe et je pense que c'est une bêtise qui m'a manqué.

J'ai défini deux classes ES6, chacune correspondant à un modèle Handlebars, et le point d'entrée de mon application est supposé remplacer le code HTML réservé au fichier d'index par son contenu:

Point d'accès:

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');
}

Index:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    <h1>My title</h1>
    <a>Click me</a>

    <script src="build/bloj.js"></script>
</body>
</html>

Bouton:

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));
    }
}

Entête:

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})
        );
    }
}

Malheureusement, cela ne fonctionne pas et j'obtiens ces deux erreurs lors de l'affichage de la page:

Uncaught TypeError: Header is not a constructor
Uncaught TypeError: Button is not a constructor

Que pourrais-je manquer?

Voici la configuration de mon 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
    }
};
27
Silver Quettier

Que pourrais-je manquer?

Babel affecte les exportations par défaut à la propriété default. Donc, si vous utilisez require pour importer des modules ES6, vous devez accéder à la propriété default:

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

Je me rends compte que vous avez déjà une réponse. Cependant, j'ai eu un problème similaire auquel j'ai trouvé une réponse. Commencer ma propre question et y répondre me semble bizarre… Donc, je vais laisser ça ici.

J'ai eu la même erreur que vous avez. Cependant, j'ai réussi à le résoudre en changeant mon

export default {Class}

à 

export default Class

Je ne sais pas pourquoi j'ai enveloppé la classe dans un objet mais je me souviens de l'avoir vue quelque part, alors je viens de commencer à l'utiliser.

Ainsi, au lieu que la classe par défaut renvoie une classe, elle a renvoyé un objet tel que ce {Class: Class}. Ceci est tout à fait valide, mais cassera webpack + babel.

EDIT: J'ai appris depuis pourquoi cela casse probablement babel + webpack. Le export default est destiné à n'avoir qu'une seule exportation. Un objet javascript peut contenir de nombreuses propriétés. Ce qui signifie qu'il peut avoir plus d'une exportation. (Voir: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export ).

Pour plusieurs exportations, utilisez: export {definition1, definition2}

Cas d'utilisation: je l'ai utilisé dans une situation où j'ai créé une bibliothèque qui exportait différents types d'éditeur (alors que le code sous-jacent était identique, l'apparence de l'éditeur change en fonction de l'exportation que vous utilisez).

8
Byebye

Vous pouvez simplement mettre export var __useDefault = true; juste après l'exportation de votre classe.

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

Ce n'est pas le problème dans cette question particulière, mais pour certaines raisons, babel ne hisse pas les classes dans le même fichier.

Donc, si vous déclarez votre classe Token en haut du fichier et écrivez plus tard new Token(), elle sera exécutée.

Si vous déclarez votre classe après l'appel du constructeur, vous aurez le xxx n'est pas un constructeur error

1
Nicolas Zozol

Bien que ce ne soit pas la cause de votre problème particulier, j'ai rencontré un problème très similaire lorsque je tentais d'extraire babel d'une application de nœud existante qui utilisait la syntaxe import et export de ES6. A l'avenir.

Babel résoudra toutes les dépendances circulaires d’un module à l’autre. Vous pourrez ainsi utiliser les variables import et export de ES6 avec un abandon inconsidéré. Toutefois, si vous devez vous débarrasser de Babel et utiliser un nœud natif, vous devrez remplacer les variables import et exports par require. Cela peut réintroduire un problème de référence circulaire latent dont babel s'occupait en arrière-plan. Si vous vous trouvez dans cette situation, recherchez dans votre code une zone ressemblant à ceci:

Déposer un:

const B = require('B');

class A {
  constructor() {
    this.b = new B();
  }
}
module.exports = A;

Fichier B:

const A = require('A'); // this line causes the error

class B {
  constructor() {
    this.a = new A();
  }
}
module.exports = B;

Il existe différentes manières de résoudre ce problème en fonction de la structure de votre code. Le moyen le plus simple est probablement de donner à B une référence à A au lieu de créer une nouvelle instance de la classe A. Vous pouvez également résoudre dynamiquement la référence lors du chargement de A. Il existe une multitude d'autres solutions, mais c'est un bon endroit pour commencer.

0
xtro