J'ai obtenu le programme TypeScript suivant transposé en ES5:
Fichier 1:
class BaseElement extends HTMLElement {
constructor() {
super();
}
}
Fichier 2:
import {BaseElement} from './BaseElement';
class MyElement extends BaseElement {
constructor() {
super();
}
}
var el = new MyElement();
En plaçant tout manuellement dans un fichier, le code fonctionne correctement et s'exécute dans le navigateur, le HTMLElement est construit sans problème. Cependant, dès que je l'emballe via webpack, j'obtiens le message d'erreur suivant:
Uncaught TypeError: Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function.
Sans webpack, le code JS suivant est construit:
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var BaseElement = (function (_super) {
__extends(BaseElement, _super);
function BaseElement() {
_super.call(this);
}
return BaseElement;
}(HTMLElement));
var MyElement = (function (_super) {
__extends(MyElement, _super);
function MyElement() {
_super.call(this);
}
MyElement.prototype.createdCallback = function () {
this.innerHTML = "lol";
};
return MyElement;
}(BaseElement));
var el = new MyElement();
À l'aide de webpack, le code suivant est construit:
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
__webpack_require__(1);
__webpack_require__(2);
/***/ },
/* 1 */
/***/ function(module, exports) {
"use strict";
var BaseElement = (function (_super) {
__extends(BaseElement, _super);
function BaseElement() {
_super.call(this);
}
return BaseElement;
}(HTMLElement));
exports.BaseElement = BaseElement;
/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
var BaseElement_1 = __webpack_require__(1);
var MyElement = (function (_super) {
__extends(MyElement, _super);
function MyElement() {
_super.call(this);
}
MyElement.prototype.createdCallback = function () {
this.innerHTML = "lol";
};
return MyElement;
}(BaseElement_1.BaseElement));
exports.MyElement = MyElement;
// TODO: inject
var p = new MyElement();
/***/ }
/******/ ]);
Fondamentalement, webpack place n'importe quel module dans une fonction et conserve une variable d'exportation entre eux, mais la construction de HTMLElement échoue. Sans webpack (code ci-dessus), cela fonctionne très bien.
Des idées?
C'est une question transpilable. Si vous transpilez ou utilisez ES5, vous devez regrouper native-shim pour les navigateurs avec prise en charge des composants Web natifs. ( https://github.com/webcomponents/custom-elements/blob/master/src/native- shim.js )
Les classes de style ES5 ne fonctionnent pas avec les éléments personnalisés natifs car le constructeur HTMLElement utilise la valeur de
new.target
Pour rechercher la définition d'élément personnalisé pour le constructeur actuellement appelé.new.target
N'est défini que lorsquenew
est appelé et n'est propagé que via des appels super (). super () n'est pas émulable dans ES5. Le modèle deSuperClass.call(this)`` only works when extending other ES5-style classes, and does not propagate
new.target`.
Consultez la discussion sur le problème https://github.com/webcomponents/custom-elements/issues/29
Les classes de style ES5 ne fonctionnent pas avec les éléments personnalisés natifs
Pour contourner le problème, remplacez simplement la cible du fichier tsconfig.json par es6.
J'ai résolu ce problème à l'aide de ce problème - https://github.com/facebook/create-react-app/issues/3225
Fondamentalement, j'ai installé ces 2 plugins via npm et dans ma configuration WebPack, j'ai ajouté ces 2 plugins pour Babel:
use: [
{
loader: 'babel-loader',
options: {
presets: ['es2015'],
// https://github.com/facebook/create-react-app/issues/3225
plugins: ['transform-custom-element-classes', 'transform-es2015-classes']
},
}
],
1) Babel 7.6.0
Personnellement, l'erreur semble avoir disparu avec ces devDependencies spécifiques:
"devDependencies": {
"@babel/core": "^7.6.0",
"@babel/preset-env": "^7.6.0",
"babel-loader": "^8.0.6",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.8"
}
Et ce webpack.config.js:
var glob = require('glob');
var path = require('path');
module.exports = {
entry: glob.sync('./app/scripts/**.js').reduce(function(obj, el){
obj[path.parse(el).name] = el;
return obj
},{}),
output: {
path: path.resolve(__dirname, './dist/scripts'),
filename: "[name].js"
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
include: [
path.resolve(__dirname, 'app/scripts')
],
options: {
presets: ['@babel/env']
}
}
]
}
};
Donc, fondamentalement, je dis à webpack de transpiler tout fichier .js dans mon dossier/app/scripts, et de les enregistrer dans/dist/scripts, en utilisant babel-loader, avec le @babel/preset-env
paquet.
2) Babel 6. *. 0
Cependant, si vous utilisez @babel/core
6.*.*
, vous voudrez peut-être vérifier cela https://medium.com/@allenhwkim/chrome-browser-custom-element-error-e86db5ae3b8c . C'est assez simple et je l'ai déjà utilisé avec succès avant d'essayer de mettre à jour tous mes packages babel.
"Tout" ce que vous devez faire est npm install babel-plugin-transform-es2015-classes babel-plugin-transform-custom-element-classes --save-dev
, puis ajoutez-les dans votre webpack.config.js (n'oubliez pas de npm install --save-dev babel-preset-es2015
ainsi que) :
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
include: [
path.resolve(__dirname, 'app/scripts')
],
options: {
presets: ['es2015'],
plubins: ["transform-custom-element-classes", "transform-es2015-classes"]
}
}
]
}
Êtes-vous sûr que cela fonctionne sans webpack? L'exécuter à travers le terrain de je donne la même erreur que vous avez décrite (au moment de l'exécution).
Quoi qu'il en soit, vous ne devez pas étendre le HTMLElement
.
Le HTMLElement
est en fait une interface en TypeScript, donc si quelque chose vous devez l'implémenter comme tel.
Il existe en tant que type d'objet dans le navigateur, mais il n'a pas été déclaré en tant que classe TypeScript, donc TypeScript n'est pas en mesure de l'étendre correctement.
Pour trouver des solutions à ce problème, consultez cette réponse .
Configuration de Babel 7 + Webpack 4 pour les composants Web:
package.json:
"devDependencies": {
"@babel/core": "^7.3.4",
"@babel/plugin-proposal-class-properties": "^7.3.4",
"@babel/preset-env": "^7.3.4",
"babel-loader": "^8.0.5",
"babel-plugin-transform-custom-element-classes": "^0.1.0",
"webpack": "^4.29.6",
"webpack-cli": "^3.2.3",
"webpack-dev-server": "^3.2.1"
}
webpack.config.js:
module: {
rules: [
{
test: /\.js$/,
use:{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: [
"transform-custom-element-classes",
"@babel/plugin-proposal-class-properties",
]
},
},
exclude: /node_modules/
}
]
}
En utilisant transform-es2015-classes
le plugin interrompra le processus de construction lors de l'utilisation de Babel 7, puisque babel-preset-env
le contient déjà. @babel/plugin-proposal-class-properties
est nécessaire pour les rappels du cycle de vie. Utilisation de préréglages annuels tels que es2015
est déconseillé dans Babel 7, utilisez @babel/preset-env
au lieu.