扩展 HTMLElement:使用 webpack 时构造函数失败
extending HTMLElement: Constructor fails when webpack was used
我将以下 TypeScript 程序转译为 ES5:
文件 1:
class BaseElement extends HTMLElement {
constructor() {
super();
}
}
文件 2:
import {BaseElement} from './BaseElement';
class MyElement extends BaseElement {
constructor() {
super();
}
}
var el = new MyElement();
将所有内容手动放入文件中,代码工作正常并在浏览器中执行,HTMLElement 的构建没有问题。但是,一旦我通过 webpack 打包它,我就会收到以下错误消息:
Uncaught TypeError: Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function.
不使用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();
使用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();
/***/ }
/******/ ]);
基本上,webpack 将任何模块放入一个函数中,并在它们之间维护一个导出变量,但是 HTMLElement 的构造失败了。没有 webpack(上面的代码),它工作正常。
有什么想法吗?
你确定它在没有 webpack 的情况下也能正常工作吗? 运行 它通过 playground 给出了与您描述的相同的错误(在运行时)。
无论如何,您不应该扩展 HTMLElement
。
HTMLElement
实际上是打字稿中的一个接口,所以如果有的话你应该这样实现它。
它作为对象类型存在于浏览器中,但尚未声明为 typescript class,因此 typescript 无法正确扩展它。
有关解决此问题的方法,请参阅此 answer。
这是转译问题。如果您正在转译或使用 ES5,那么您需要为支持本机 Web 组件的浏览器捆绑 native-shim。(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 when new
is 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 propagate
new.target`.
查看问题讨论https://github.com/webcomponents/custom-elements/issues/29
ES5-style classes don't work with native Custom Elements
要解决这个问题,只需将 tsconfig.json 文件中的目标更改为 es6。
我在这个问题的帮助下解决了这个问题 - https://github.com/facebook/create-react-app/issues/3225
基本上我通过 npm 安装了这 2 个插件,并在我的 WebPack 配置中为 Babel 添加了这 2 个插件:
use: [
{
loader: 'babel-loader',
options: {
presets: ['es2015'],
// https://github.com/facebook/create-react-app/issues/3225
plugins: ['transform-custom-element-classes', 'transform-es2015-classes']
},
}
],
用于 Web 组件的 Babel 7 + Webpack 4 配置:
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/
}
]
}
使用 transform-es2015-classes
插件会在使用 Babel 7 时中断构建过程,因为 babel-preset-env
已经包含它。 @babel/plugin-proposal-class-properties
是生命周期回调所必需的。在 Babel 7 中不推荐使用年度预设,例如 es2015
,请改用 @babel/preset-env
。
1) 巴别塔 7.6.0
就个人而言,错误似乎随着这些特定的 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"
}
还有这个 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']
}
}
]
}
};
所以基本上我告诉 webpack 转译我的 /app/scripts 文件夹中的任何 .js 文件,并将它们保存在 /dist/scripts 中,使用 babel-loader,使用 @babel/preset-env
包.
2) 巴别塔 6.*.0
但是,如果您使用的是 @babel/core
6.*.*
,您可能需要查看此 https://medium.com/@allenhwkim/chrome-browser-custom-element-error-e86db5ae3b8c。它非常简单,在尝试更新我所有的 babel 包之前我已经成功地使用了它。
"All" 你需要做的是 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"]
}
}
]
}
我将以下 TypeScript 程序转译为 ES5:
文件 1:
class BaseElement extends HTMLElement {
constructor() {
super();
}
}
文件 2:
import {BaseElement} from './BaseElement';
class MyElement extends BaseElement {
constructor() {
super();
}
}
var el = new MyElement();
将所有内容手动放入文件中,代码工作正常并在浏览器中执行,HTMLElement 的构建没有问题。但是,一旦我通过 webpack 打包它,我就会收到以下错误消息:
Uncaught TypeError: Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function.
不使用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();
使用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();
/***/ }
/******/ ]);
基本上,webpack 将任何模块放入一个函数中,并在它们之间维护一个导出变量,但是 HTMLElement 的构造失败了。没有 webpack(上面的代码),它工作正常。
有什么想法吗?
你确定它在没有 webpack 的情况下也能正常工作吗? 运行 它通过 playground 给出了与您描述的相同的错误(在运行时)。
无论如何,您不应该扩展 HTMLElement
。
HTMLElement
实际上是打字稿中的一个接口,所以如果有的话你应该这样实现它。
它作为对象类型存在于浏览器中,但尚未声明为 typescript class,因此 typescript 无法正确扩展它。
有关解决此问题的方法,请参阅此 answer。
这是转译问题。如果您正在转译或使用 ES5,那么您需要为支持本机 Web 组件的浏览器捆绑 native-shim。(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`.
查看问题讨论https://github.com/webcomponents/custom-elements/issues/29
ES5-style classes don't work with native Custom Elements
要解决这个问题,只需将 tsconfig.json 文件中的目标更改为 es6。
我在这个问题的帮助下解决了这个问题 - https://github.com/facebook/create-react-app/issues/3225
基本上我通过 npm 安装了这 2 个插件,并在我的 WebPack 配置中为 Babel 添加了这 2 个插件:
use: [
{
loader: 'babel-loader',
options: {
presets: ['es2015'],
// https://github.com/facebook/create-react-app/issues/3225
plugins: ['transform-custom-element-classes', 'transform-es2015-classes']
},
}
],
用于 Web 组件的 Babel 7 + Webpack 4 配置:
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/
}
]
}
使用 transform-es2015-classes
插件会在使用 Babel 7 时中断构建过程,因为 babel-preset-env
已经包含它。 @babel/plugin-proposal-class-properties
是生命周期回调所必需的。在 Babel 7 中不推荐使用年度预设,例如 es2015
,请改用 @babel/preset-env
。
1) 巴别塔 7.6.0
就个人而言,错误似乎随着这些特定的 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"
}
还有这个 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']
}
}
]
}
};
所以基本上我告诉 webpack 转译我的 /app/scripts 文件夹中的任何 .js 文件,并将它们保存在 /dist/scripts 中,使用 babel-loader,使用 @babel/preset-env
包.
2) 巴别塔 6.*.0
但是,如果您使用的是 @babel/core
6.*.*
,您可能需要查看此 https://medium.com/@allenhwkim/chrome-browser-custom-element-error-e86db5ae3b8c。它非常简单,在尝试更新我所有的 babel 包之前我已经成功地使用了它。
"All" 你需要做的是 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"]
}
}
]
}