Erreurs inattendues "Uncaught TypeError: XXX is not a constructor" avec Babel et ES6

je suis en train d'essayer de Webpack, et suis en train d'essayer de les instructions de la section ce tutoriel, donner ou prendre quelques personnalisés de choses.

c'est du code simple, vraiment, mais je suis assez perplexe à propos de cette erreur, et je pense que c'est quelque chose de stupide que j'ai manqué.

j'ai défini deux classes ES6, chacune correspondant à un modèle de guidon, et le point d'entrée de mon application est supposé remplacer le paramètre html dans le fichier index par leur contenu:

point d'entrée:

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

Indice:

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

Header:

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'ai ces deux erreurs lors de l'affichage de la page:

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

Qu'est-ce que je pourrais manquer?

voici ma configuration 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
demandé sur Silver Quettier 2016-04-03 20:37:23

5 réponses

Qu'est-ce que je pourrais manquer?

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

const Button = require('./Components/Button.js').default;
45
répondu Felix Kling 2016-04-03 18:15:57

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

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

export default {Class}

export default Class

Je ne sais pas pourquoi j'ai enveloppé le cours dans un objet mais je me souviens l'avoir vu quelque part alors j'ai commencé à l'utiliser.

Donc au lieu de renvoyer une classe par défaut, il renvoie un objet comme ceci {Class: Class}. Ceci est tout à fait VALIDE mais il va casser webpack+babel.

EDIT: j'ai depuis appris pourquoi cela brise probablement babel+webpack. export default est destiné à n'avoir qu'une 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 exportations multiples utilisation:export {definition1, definition2}.

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

8
répondu Byebye 2017-01-16 09:04:31

Vous pouvez simplement mettre export var __useDefault = true; juste après avoir exporté votre classe.

export default class Header {
...
} 
export var __useDefault = true;
6
répondu maufarinelli 2016-11-16 04:36:52

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

Donc, si vous déclarez votre classe Token au début du fichier, et écrire plus tard new Token(), il sera exécuté.

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

1
répondu Nicolas Zozol 2018-04-17 06:10:18

bien que ce ne soit pas la cause de votre problème particulier, j'ai rencontré un problème très similaire en essayant d'arracher babel hors d'une application de noeud existante qui utilisait ES6 import et export syntaxe, donc, ce post est pour aider quelqu'un d'autre de la difficulté avec cela à l'avenir.

Babel résoudra toutes les dépendances circulaires entre un module et un autre, de sorte que vous pouvez utiliser les import et export avec insouciance. Cependant, si vous avez besoin de se débarrasser de babel et d'utiliser natif nœud, vous devez remplacer import et exportsrequire. Cela peut réintroduire un problème de référence circulaire latente que babel a pris soin de l'arrière-plan. Si vous vous trouvez dans cette situation, trouver un dans votre code qui ressemble à ceci:

Un Fichier:

const B = require('B');

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

Le Fichier B:

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

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

il y a plusieurs façons de résoudre ce problème selon la façon dont vous avez structuré votre code. Le plus simple est probablement de passer 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 A. Il existe une myriade d'autres solutions, mais c'est un bon endroit pour commencer.

0
répondu xtro 2018-01-23 20:46:50