我正在创建一个与Vite捆绑的简单网站,但没有像React,Vue,Svelte这样的外部框架,等.该网站有重复的HTML元素使用不同的数据.我想使用这些自定义元素.我使用自定义元素,而不是完整的Web组件,因为我想能够使用一个全局样式表.数据是在一个外部JSON文件.我遇到的时间问题,造成困难及时将数据发送到自定义元素,以便对其进行配置。
我的HTML看起来像:
<!doctype html>
<html lang="en">
<head>...</head>
<body>
<main>
<my-element data-content-id="0"></my-element>
<another-element data-content-id="1"></another-element>
<my-element data-content-id="2"></my-element>
<another-element data-content-id="3"></another-element>
</main>
<script type="module" src="/main.js"></script>
</body>
</html>
字符串
main.js:
import MyElement from "./custom-elements/MyElement.js"
import AnotherElement from "./custom-elements/AnotherElement.js"
const init = () => {
// load external config
const configFile = "/config.json"
fetch(configFile)
.then(config => config.json())
.then(config => {
console.log("[Main] app initiated in main.js")
const myElement = new MyElement(config)
const anotherElement = new AnotherElement(config)
}
})
.catch(error => console.error('Error:', error))
}
init()
型
MyElement.js:
class MyElement extends HTMLElement {
constructor(config) {
super()
this.config = config
console.log("constructor")
if (this.config) {
console.log("calling useData from constructor", this.config)
this.useData(this.config)
}
}
useData(prop) {
console.log("useData")
// ... logic here ...
}
connectedCallback() {
console.log("connected callback")
if (this.config) {
console.log("calling useData from callback", this.config)
this.useData(this.config)
}
this.innerHTML = `
<div>
...
</div>
`;
}
}
window.customElements.define('my-element', MyElement)
export default MyElement;
型
当我查看日志时,我发现constructor
和connected callback
在[Main] app initiated in main.js
之前被记录。然后当MyElement类被示例化时,constructor
再次被调用/记录,这次是用数据,所以useData
运行/logs。但是,此时,我已经运行了两次构造函数,并且我已经无法访问DOM中的自定义元素来使用数据。
所以,总而言之,即使我在加载数据之前没有在main.js
中示例化类,构造函数也被调用了两次:当自定义元素出现在DOM中时,(我猜,无论如何,它是在获取数据之前),并且在使用数据示例化时执行一次一旦加载了数据并示例化了类,我就不能像通过connectedCallback
那样访问自定义元素了。
有没有一种方法可以在自定义元素中使用外部获取的数据并正确计时?
更新
我意识到我可以:
1.将导入的数据放在全局变量中,
1.在填充该变量后导入自定义元素类。
这给予我一个main.js
,看起来像这样:
const init = () => {
// external config
const configFile = "/config.json"
fetch(configFile)
.then(config => config.json())
.then(config => {
console.log("[Main] app initiated in main.js")
window.globalContentData = config
import MyElement from "./custom-elements/MyElement.js"
import AnotherElement from "./custom-elements/AnotherElement.js"
}
})
.catch(error => console.error('Error:', error))
}
init()
型
调整后的MyElement.js:
MyElement.js:
class MyElement extends HTMLElement {
constructor() {
super()
this.config = window.globalContentData
console.log("constructor")
if (this.config) {
console.log("calling useData from constructor", this.config)
this.useData(this.config)
}
}
useData(prop) {
console.log("useData")
// ... logic here ...
}
connectedCallback() {
console.log("connected callback")
if (this.config) {
console.log("calling useData from callback", this.config)
this.useData(this.config)
}
this.innerHTML = `
<div>
...
</div>
`;
}
}
window.customElements.define('my-element', MyElement)
export default MyElement;
型
这确实有效。也就是说,我对污染全球空间感到有点肮脏,我想知道是否有一种更优雅的方式。
FWIW,我喜欢开发人员在这个相对简单的网站上使用自定义元素,而不是使用更大的JS框架。
2条答案
按热度按时间zynd9foi1#
字符串
在解析完DOM中的Web组件后,您可以加载脚本。因此
constructor
和connectedCallback
(在 opening 标记上触发)已经运行。参见:https://andyogo.github.io/custom-element-reactions-diagram/
你现在有一个全局
init()
,它告诉你的组件该做什么?也许颠倒逻辑,让Web组件加载数据(可以存储)
您可以通过添加minimal-reproducible-example StackOverflow Snippet来帮助我们回答您的问题。它将帮助读者一键执行您的代码。并帮助一键创建答案。
dz6r00yl2#
这样的东西可能会起作用:
字符串
文档中说有3种方法可以创建一个自治的自定义元素,看起来你有1和3的混合:
https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-autonomous-example
对于另一种方式,首先从html文件中删除自定义元素,使其看起来像:
型
那么你的fetch处理程序可能看起来像这样:
型
我在我的项目中这样做的方式会是这样的:
型