json 在HTML自定义元素中使用外部数据

utugiqy6  于 4个月前  发布在  其他
关注(0)|答案(2)|浏览(45)

我正在创建一个与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;


当我查看日志时,我发现constructorconnected 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框架。

zynd9foi

zynd9foi1#

<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>

字符串
在解析完DOM中的Web组件后,您可以加载脚本。因此constructorconnectedCallback(在 opening 标记上触发)已经运行。
参见:https://andyogo.github.io/custom-element-reactions-diagram/
你现在有一个全局init(),它告诉你的组件该做什么?
也许颠倒逻辑,让Web组件加载数据(可以存储)
您可以通过添加minimal-reproducible-example StackOverflow Snippet来帮助我们回答您的问题。它将帮助读者一键执行您的代码。并帮助一键创建答案。

dz6r00yl

dz6r00yl2#

这样的东西可能会起作用:

fetch(configFile)
    .then(config => config.json())
    .then(config => {
       console.log("[Main] app initiated in main.js")
       //at this point constructor and connectedCallback have already
       //run for all 4 elements, so you'll need to add the data another way:
       document.querySelectorAll("[data-content-id]").forEach((element,i)=>{
          element.useData(config);
          //or you might be trying to do something like this:
          //const dataContentId = element.getAttribute("data-content-id");
          //element.useData(config[dataContentId]);
          //you may want to delete the data-content-id attribute
          //if you're going to re-run this code later with newly added elements
          //element.removeAttribute("data-content-id");
          //but remind yourself in the dom what these elements were:
          //element.setAttribute("data-content-id-compiled",dataContentId);
       });
      }
    })
    .catch(error => console.error('Error:', error))

字符串
文档中说有3种方法可以创建一个自治的自定义元素,看起来你有1和3的混合:
https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-autonomous-example
对于另一种方式,首先从html文件中删除自定义元素,使其看起来像:

<!doctype html>
<html lang="en">
  <head>...</head>
  <body>
    <main>
      
    </main>
    <script type="module" src="/main.js"></script>
  </body>
</html>


那么你的fetch处理程序可能看起来像这样:

fetch(configFile)
    .then(config => config.json())
    .then(config => {
       console.log("[Main] app initiated in main.js")
       //in case you put this code elsewhere:
       let MyElementConstructor = customElements.get("my-element");
       let AnotherElementConstructor = customElements.get("another-element");
       //then create the elements:
       const myElement1 = new MyElementConstructor(config);
       myElement1.setAttribute("data-content-id","0");
       const anotherElement1 = new AnotherElementConstructor(config);
       anotherElement1.setAttribute("data-content-id","1");
       const myElement2 = new MyElementConstructor(config);
       myElement2.setAttribute("data-content-id","2");
       const anotherElement2 = new AnotherElementConstructor(config);
       anotherElement2.setAttribute("data-content-id","3");
       //Note, at this point, these 4 elements have had their constructors run,
       //but their connectedCallbacks have not run, you must append them to
       //the dom:
       const main = document.querySelector("main");
       main.appendChild(myElement1);
       main.appendChild(anotherElement1);
       main.appendChild(myElement2);
       main.appendChild(anotherElement2);
      }
    })
    .catch(error => console.error('Error:', error))


我在我的项目中这样做的方式会是这样的:

fetch(configFile)
    .then(config => config.json())
    .then(config => {
       console.log("[Main] app initiated in main.js")
       const main = document.querySelector("main");       
       main.insertAdjacentHTML("beforeend", `
          <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>
       `); 
       byDCID("0").useData(config);
       byDCID("1").useData(config);
       byDCID("2").useData(config);
       byDCID("3").useData(config);
       function byDCID(dcid){
         return document.querySelector(`[data-content-id="${dcid}"]`);
       }
      }
    })
    .catch(error => console.error('Error:', error))

相关问题