typescript 扩展 HTMLElement:使用 webpack 时构造函数失败
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39037489/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
extending HTMLElement: Constructor fails when webpack was used
提问by mbue
I got the following TypeScript program transpiled to ES5:
我将以下 TypeScript 程序转换为 ES5:
File 1:
文件 1:
class BaseElement extends HTMLElement {
constructor() {
super();
}
}
File 2:
文件2:
import {BaseElement} from './BaseElement';
class MyElement extends BaseElement {
constructor() {
super();
}
}
var el = new MyElement();
Putting everything manually within a file, the code works fine and executes in the browser, the HTMLElement is constructed without problems. However, as soon as I pack it via webpack, I get the following error message:
将所有内容手动放入文件中,代码运行良好并在浏览器中执行,HTMLElement 的构建没有问题。但是,一旦我通过 webpack 打包它,我就会收到以下错误消息:
Uncaught TypeError: Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function.
Without webpack, the following JS code is constructed:
不使用webpack,构造如下JS代码:
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();
Using webpack, the following code is constructed:
使用webpack,构造如下代码:
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();
/***/ }
/******/ ]);
Basically, webpack puts any module into a function and maintains an export variable between them, however the construction of HTMLElement fails. Without webpack (code above), it works fine.
基本上,webpack 将任何模块放入一个函数中并在它们之间维护一个导出变量,但是 HTMLElement 的构建失败了。没有 webpack(上面的代码),它工作正常。
Any ideas?
有任何想法吗?
采纳答案by Amit
It is transpiling issue. If you are transpiling or using ES5 then you need to bundle native-shim for browsers with native Web components support.(https://github.com/webcomponents/custom-elements/blob/master/src/native-shim.js)
这是转译问题。如果您正在转译或使用 ES5,那么您需要为具有本机 Web 组件支持的浏览器捆绑本机垫片。(https://github.com/webcomponents/custom-elements/blob/master/src/native-shim.js)
ES5-style classes don't work with native Custom Elements because the HTMLElement constructor uses the value of
new.target
to look up the custom element definition for the currently called constructor.new.target
is only set whennew
is called and is only propagated via super() calls. super() is not emulatable in ES5. The pattern ofSuperClass.call(this)`` only works when extending other ES5-style classes, and does not propagate
new.target`.
ES5 样式的类不适用于本机自定义元素,因为 HTMLElement 构造函数使用 的值
new.target
来查找当前调用的构造函数的自定义元素定义。new.target
仅在new
被调用时设置并且仅通过 super() 调用传播。super() 在 ES5 中是不可模拟的。SuperClass.call(this)`` only works when extending other ES5-style classes, and does not propagate
new.target`的模式。
Check the issue discussion https://github.com/webcomponents/custom-elements/issues/29
检查问题讨论https://github.com/webcomponents/custom-elements/issues/29
回答by Darsan S Kumar
ES5-style classes don't work with native Custom Elements
ES5 样式的类不适用于本机自定义元素
To do a work around, just change the target in the tsconfig.json file to es6.
要解决此问题,只需将 tsconfig.json 文件中的目标更改为 es6。
回答by Dzintars
I fixed this issue with help of this issue - https://github.com/facebook/create-react-app/issues/3225
我在这个问题的帮助下解决了这个问题 - https://github.com/facebook/create-react-app/issues/3225
Basically i installed these 2 plugins via npm and in my WebPack config added those 2 plugins for Babel:
基本上我通过 npm 安装了这两个插件,并在我的 WebPack 配置中为 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']
},
}
],
回答by webpreneur
Babel 7 + Webpack 4 config for Web Components:
用于 Web 组件的 Babel 7 + Webpack 4 配置:
package.json:
包.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:
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/
}
]
}
Using transform-es2015-classes
plugin will break the build process when using Babel 7, since babel-preset-env
already contains it. @babel/plugin-proposal-class-properties
is necessary for the lifecycle callbacks. Using yearly presets such as es2015
is deprecated in Babel 7, use @babel/preset-env
instead.
使用transform-es2015-classes
插件会在使用 Babel 7 时破坏构建过程,因为babel-preset-env
它已经包含它。@babel/plugin-proposal-class-properties
是生命周期回调所必需的。使用es2015
Babel 7 中不推荐使用的年度预设,请@babel/preset-env
改用。
回答by ZyDucksLover
1) Babel 7.6.0
1) 通天塔 7.6.0
Personally, the error seems to be gone with these specific devDependencies :
就个人而言,这些特定的 devDependencies 错误似乎消失了:
"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"
}
And this webpack.config.js :
这个 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']
}
}
]
}
};
So basically I'm telling webpack to transpile any .js file in my /app/scripts folder, and save them in /dist/scripts, using babel-loader, with the @babel/preset-env
package.
所以基本上我告诉 webpack 转译我的 /app/scripts 文件夹中的任何 .js 文件,并将它们保存在 /dist/scripts 中,使用 babel-loader 和@babel/preset-env
包。
2) Babel 6.*.0
2) 巴别塔 6.*.0
However, if you're using @babel/core
6.*.*
, you might want to check this https://medium.com/@allenhwkim/chrome-browser-custom-element-error-e86db5ae3b8c. It's quite simple and I already used it successfully before trying to update all my babel packages.
但是,如果您正在使用@babel/core
6.*.*
,则可能需要查看此https://medium.com/@allenhwkim/chrome-browser-custom-element-error-e86db5ae3b8c。它非常简单,在尝试更新我所有的 babel 包之前,我已经成功使用了它。
"All" you need to do is npm install babel-plugin-transform-es2015-classes babel-plugin-transform-custom-element-classes --save-dev
, then add them in your webpack.config.js (don't forget to npm install --save-dev babel-preset-es2015
as well) :
“所有”你需要做的是npm install babel-plugin-transform-es2015-classes babel-plugin-transform-custom-element-classes --save-dev
,然后将它们添加到你的 webpack.config.js (不要忘记npm install --save-dev babel-preset-es2015
):
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
include: [
path.resolve(__dirname, 'app/scripts')
],
options: {
presets: ['es2015'],
plubins: ["transform-custom-element-classes", "transform-es2015-classes"]
}
}
]
}
回答by Jeppe Stougaard
Are you sure it's working without webpack? Running it through the playgroundgives the same error as you described (at runtime).
你确定它可以在没有 webpack 的情况下工作吗?在操场上运行它会产生与您描述的相同的错误(在运行时)。
Anyway, you shouldn't be extending the HTMLElement
.
The HTMLElement
is actually an interface in typescript, so if anything you should be implementing it as such.
It exists as an object type in the browser, but it hasn't been declared as a typescript class, so typescript isn't able to properly extend it.
无论如何,您不应该扩展HTMLElement
.
这HTMLElement
实际上是打字稿中的一个接口,所以如果有的话你应该这样实现它。
它作为浏览器中的对象类型存在,但它没有被声明为 typescript 类,因此 typescript 无法正确扩展它。
For ways around this issue, see this answer.
有关解决此问题的方法,请参阅此答案。