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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 03:45:59  来源:igfitidea点击:

extending HTMLElement: Constructor fails when webpack was used

javascripttypescriptwebpackweb-component

提问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.targetto look up the custom element definition for the currently called constructor. new.targetis only set when newis called and is only propagated via super() calls. super() is not emulatable in ES5. The pattern of SuperClass.call(this)`` only works when extending other ES5-style classes, and does not propagatenew.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 propagatenew.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-classesplugin will break the build process when using Babel 7, since babel-preset-envalready contains it. @babel/plugin-proposal-class-propertiesis necessary for the lifecycle callbacks. Using yearly presets such as es2015is deprecated in Babel 7, use @babel/preset-envinstead.

使用transform-es2015-classes插件会在使用 Babel 7 时破坏构建过程,因为babel-preset-env它已经包含它。@babel/plugin-proposal-class-properties是生命周期回调所必需的。使用es2015Babel 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-envpackage.

所以基本上我告诉 webpack 转译我的 /app/scripts 文件夹中的任何 .js 文件,并将它们保存在 /dist/scripts 中,使用 babel-loader 和@babel/preset-env包。

2) Babel 6.*.0

2) 巴别塔 6.*.0

However, if you're using @babel/core6.*.*, 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/core6.*.*,则可能需要查看此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-es2015as 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 HTMLElementis 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.

有关解决此问题的方法,请参阅此答案