编辑:此问题似乎仅出现在 .hbs 渲染中,因为当扩展 Web 组件插入顶级索引时此代码确实有效EmberJS 项目的 .html
编辑 2:但是,当我将其放入最小的 .hbs 示例中时,它确实起作用了。所以这显然是与 EmberJS 渲染的交互,也许还有与 Handlebars 的交互。
编辑 3:当我使用 Glimmer 作为独立渲染库并通过 constructor()
构建这些 Web 组件时,问题仍然存在。 Glimmer 组件的功能(我知道这很老套,但它适用于独立的自定义 Web 组件)。
我试图了解如何在 EmberJS 中使用 Web 组件。我知道我可以将 Web 组件重写为 EmberJS 组件,但这不是重点。我想知道如何将Web组件直接集成到EmberJS中。
有一个教程介绍了如何制作自定义 Web 组件并通过制作初始化程序将其放入 EmberJS 中。
https://tenbit.co/blog/a-simple-way-to-integrate-web-components-with-your-ember-app/
所以您可能会认为,为扩展的原生 HTML 组件做同样的事情是小菜一碟,对吗?事实证明并非如此。
这是一个不起作用的最小示例。在这个特定的例子中<p>hello world</p>
显示但 <p>hello Mars</p>
才不是。但是,在独立的 HTML 文件中(请参阅本文底部)具有相同的类和 define()
调用,然后它就可以工作了。
我还制作了一个 EmberJS Twiddle,您可以在其中看到它:https://ember-twiddle.com/5b85957f52aba288bfe9e94ce42b811e
// app/initializers/custom-elements.js
class HelloWorld extends HTMLElement {
constructor() {
super();
console.log("constructor() HelloWorld");
let shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.innerHTML = `<p>hello world</p>`;
}
}
//similar to HelloWorld -- it's simply an extension
class TestingAParagraph extends HTMLParagraphElement {
constructor() {
super();
console.log("constructor() TestingAParagraph");
let shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.innerHTML = `<p>hello Mars</p>`;
}
}
export function initialize(application) {
window.customElements.define("hello-world", HelloWorld);
window.customElements.define("testing-a-paragraph", TestingAParagraph, {
extends: "p"
});
}
export default {
initialize
};
这是 Handlebars 文件:
{{!-- my-template.hbs --}}
<p>Bladiebla</p>
<hello-world></hello-world>
<p>A test is coming</p>
<p is="testing-a-paragraph"></p>
注意:这个最小的示例确实可以作为独立的 HTML 文件使用。
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
// app/initializers/custom-elements.js
class HelloWorld extends HTMLElement {
constructor() {
super();
console.log("constructor() HelloWorld");
let shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.innerHTML = `<p>hello world</p>`;
}
}
//similar to HelloWorld -- it's simply an extension
class TestingAParagraph extends HTMLParagraphElement {
constructor() {
super();
console.log("constructor() TestingAParagraph");
let shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.innerHTML = `<p>hello Mars</p>`;
}
}
window.customElements.define("hello-world", HelloWorld);
window.customElements.define("testing-a-paragraph", TestingAParagraph, {
extends: "p"
});
</script>
</head>
<body>
<p>Bladiebla</p>
<hello-world></hello-world>
<p>A test is coming</p>
<p is="testing-a-paragraph"></p>
</body>
</html>
最佳答案
在答案中,我的可扩展组件是 h1
,而不是 p
。因为它使事情更容易调试。
我发现该流程的工作原理如 jelhan 在评论中所述。
有一个
createElement
(我 100% 肯定,因为我在调试器中看到我的h1
自定义标记在__openElement 上损坏了
)。然后它使用
setAttribute
一一解析所有属性(100% 肯定,因为调试器)。最终,“is”属性由setAttribute
设置。我知道当使用setAttribute
时,Web 组件将不会被实例化。它以
insertBefore
调用结束,在flushElement
或其他地方(我在调试器中看到了这一点,没有费心去查明它) .
Stackoverflow 上有一个关于如何 a web component should be added in vanillaJS 的问题.
有了这些知识,我创建了一个 JS fiddle ,您可以在其中看到 Glimmer 现在的工作流程(工作流程方面),以及 Glimmer 可能如何做到这一点。
我还为 .hbs 文件中静态定义的元素创建了一个 hacky 解决方法(警告:我不创建库,我只是在其中闲逛)
您需要将 __setAttribute
替换为如下内容:
__setAttribute(name, value, namespace) {
if(name === "is"){ //hack, hack, hackerdeehack
//I need the actual document, this.dom doesn't cut it. I suppose it's kind of like a shadow DOM, similar to what ReactJS does.
let webComponent = window.document.createElement(this.constructing.tagName, { is: value });
//copy attributes that are already parsed
[...this.constructing.attributes].forEach( attr => { this.dom.setAttribute(webComponent, attr.nodeName, attr.nodeValue, namespace) });
this.constructing = webComponent;
}
this.dom.setAttribute(this.constructing, name, value, namespace);
}
最终,由 EmberJS/Glimmer 维护者提出某种前进的方向。之所以进行这个特殊的 hack,是因为我不想弄乱 VM,也不想弄乱 Glimmer 的解析行为。但我确实想看看我的想法是否正确,而且确实如此(两天前,我不知道 Glimmer 的存在,所以我需要彻底一点)。
为好奇的人提供一些额外信息
我看到的术语:
- 操作码:VM 的东西,我有点忘记它在 VM 上下文中的含义,但我在编译器构建和计算机体系结构类(class)中看到过它。我总是将其翻译为:CPU 理解它们应该执行什么指令的数字表。
- 系统调用:DOM 函数调用是系统调用,因为它们是渲染引擎的作用。
几个方便的断点(+ console.log()
s),以验证我概述的工作流程:
//element-builder.js
__openElement(tag) {
console.log('NewElementBuilder - __openElement', tag, [this.element], this.element.attributes, 'modules');
debugger;
return this.dom.createElement(tag, this.element);
}
__setAttribute(name, value, namespace) {
debugger;
this.dom.setAttribute(this.constructing, name, value, namespace); //this is how the "is" attribute is set
}
//note: it could also be that __flushElement is called... I didn't check this thoroughly
__appendText(text) {
let { dom, element, nextSibling } = this;
debugger;
console.log('__appendText', dom, element, nextSibling, text);
let node = dom.createTextNode(text);
dom.insertBefore(element, node, nextSibling);
return node;
}
// just to be sure
__flushElement(parent, constructing) {
debugger;
this.dom.insertBefore(parent, constructing, this.nextSibling);
}
关于javascript - EmberJS 和 Web 组件 : extend an existing HTML tag within an initializer (is it possible? ),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58529308/