Web组件,传入和传出数据

Joh*_*ist 17 html javascript json web-component

我的理解是数据通过其属性传递给自定义html元素,并通过调度CustomEvent发送出去.

显然,JavaScript对象可以在事件的详细信息字段中发送出去,但是如果元素需要传递大量数据,该怎么办呢?有没有办法在JavaScript中为它提供一个对象.

如果元素例如包含需要动态初始化或更改的可变数量的部分(例如,具有可变行数的表),该怎么办?我可以设想设置和修改由在组件内部解析的JSON字符串组成的属性,但它不是一种优雅的继续方式:

<my-element tableRowProperties="[{p1:'v1', p2:'v2'}, {p1:'v1',p2:'v2'}, {p1:'v1',p2:'v2'}]"></my-element>
Run Code Online (Sandbox Code Playgroud)

或者,您是否可以让元素监听来自外部的包含数据有效负载的事件?

Int*_*lia 26

传递数据

如果您确实需要/需要将大量数据传递到组件中,那么您可以通过以下四种方式执行此操作:

1)使用财产.这是最简单的,因为您只需通过赋予元素值传递Object,如下所示:el.data = myObj;

2)使用属性.我个人讨厌这种方式,但是有些框架需要通过属性传递数据.这与您在问题中的显示方式类似.<my-el data="[{a:1},{a:2}....]"></my-el>.请注意遵循与转义属性值相关的规则.如果使用此方法,则需要JSON.parse在属性上使用,否则可能会失败.它也可能在HTML中变得非常难看,以便在属性中显示大量数据.

3 通过子元素传入它.想想<select>带有<option>子元素的元素.您可以将任何元素类型用作子元素,它们甚至不需要是真实元素.在您的connectedCallback函数中,您的代码只会抓取所有子代,并将元素,属性或内容转换为组件所需的数据.

4 使用Fetch.提供元素的URL以获取自己的数据.想一想<img src="imageUrl.png"/>.如果您已经拥有组件的数据,那么这似乎是一个糟糕的选择.但浏览器提供了一种很酷的嵌入数据功能,类似于上面的选项2,但是由浏览器自动处理.

以下是在图像中使用嵌入数据的示例:

img {
  height: 32px;
  width: 32px;
}
Run Code Online (Sandbox Code Playgroud)
<img src="data:image/svg+xml;charset=utf8,%3C?xml version='1.0' encoding='utf-8'?%3E%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 314.7 314.7'%3E%3Cstyle type='text/css'%3E .st0{fill:transparent;stroke:%23231F20;stroke-width:12;} .st1{fill:%23231F20;stroke:%23231F20;stroke-width:10;stroke-linejoin:round;stroke-miterlimit:10;} %3C/style%3E%3Cg%3E%3Ccircle class='st0' cx='157.3' cy='157.3' r='150.4'/%3E%3Cpolygon class='st1' points='108,76.1 248.7,157.3 108,238.6'/%3E%3C/g%3E%3C/svg%3E">
Run Code Online (Sandbox Code Playgroud)

以下是在Web组件中使用嵌入数据的示例:

function readSrc(el, url) {
    var fetchHeaders = new Headers({
      Accept: 'application/json'
    });

    var fetchOptions = {
      cache: 'default',
      headers: fetchHeaders,
      method: 'GET',
      mode: 'cors'
    };

    return fetch(url, fetchOptions).then(
      (resp) => {
        if (resp.ok) {
          return resp.json();
        }
        else {
          return {
            error: true,
            status: resp.status
          }
        }
      }
    ).catch(
      (err) => {
        console.error(err);
      }
    );
  }

  class MyEl extends HTMLElement {
    static get observedAttributes() {
      return ['src'];
    }

    attributeChangedCallback(attrName, oldVal, newVal) {
      if (oldVal !== newVal) {
        this.innerHtml = '';
        readSrc(this, newVal).then(
          data => {
            this.innerHTML = `<pre>
${JSON.stringify(data,0,2)}
            </pre>`;
          }
        );
      }
    }
  }

  // Define our web component
  customElements.define('my-el', MyEl);
Run Code Online (Sandbox Code Playgroud)
<!--
This component would go load its own data from "data.json"
<my-el src="data.json"></my-el>
<hr/>
The next component uses embedded data but still calls fetch as if it were a URL.
-->
<my-el src="data:json,[{&quot;a&quot;:9},{&quot;a&quot;:8},{&quot;a&quot;:7}]"></my-el>
Run Code Online (Sandbox Code Playgroud)

您可以使用XHR执行相同的操作,但如果您的浏览器支持Web组件,则它可能支持fetch.如果您确实需要填充,可以使用几种优质的填充剂.

使用选项4的最大优点是,你可以从一个网址让您的数据可以直接嵌入您的数据.这正是大多数预定义的HTML元素,如<img>工作.

UPDATE

我确实想到了将JSON数据放入对象的第5种方法.这是通过<template>在组件中使用标记.这仍然需要你调用,JSON.parse但它可以清理你的代码因为你不需要像JSON那样多地转义它.

class MyEl extends HTMLElement {
  connectedCallback() {
    var data;
    
    try {
      data = JSON.parse(this.children[0].content.textContent);
    }
    
    catch(ex) {
      console.error(ex);
    }

    this.innerHTML = '';
    var pre = document.createElement('pre');
    pre.textContent = JSON.stringify(data,0,2);
    this.appendChild(pre);
  }
}

// Define our web component
customElements.define('my-el', MyEl);
Run Code Online (Sandbox Code Playgroud)
<my-el>
  <template>[{"a":1},{"b":"&lt;b>Hi!&lt;/b>"},{"c":"&lt;/template>"}]</template>
</my-el>
Run Code Online (Sandbox Code Playgroud)

传递数据

有三种方法可以从组件中获取数据:

1)从属性中读取值.这是理想的,因为属性可以是任何东西,通常采用您想要的数据格式.属性可以返回字符串,对象,数字等.

2)读取属性.这要求组件使属性保持最新,并且可能不是最佳的,因为所有属性都是字符串.因此,您的用户需要知道他们是否需要调用JSON.parse您的价值.

3)事件.这可能是添加到组件中最重要的事情.当组件中的状态发生变化时,事件应该触发.事件应基于用户交互触发,并仅提醒用户已发生某事或某些事物可用.传统上,您会在活动中包含相关数据.这减少了组件用户需要编写的代码量.是的,他们仍然可以读取属性或属性,但如果您的事件包含所有相关数据,那么他们可能不需要做任何额外的事情.


log*_*gan 11

有第 6 种方式与上面@Intervalia 的答案非常相似,但使用<script>标签而不是<template>标签。

这与Markdown Element使用的方法相同。

class MyEl extends HTMLElement {
  connectedCallback() {
    var data;
    
    try {
      data = JSON.parse(this.children[0].innerHTML);
    }
    
    catch(ex) {
      console.error(ex);
    }

    this.innerHTML = '';
    var pre = document.createElement('pre');
    pre.textContent = JSON.stringify(data,0,2);
    this.appendChild(pre);
  }
}

// Define our web component
customElements.define('my-el', MyEl);
Run Code Online (Sandbox Code Playgroud)
<my-el>
  <script type="application/json">[{"a":1},{"b":"<b>Hi!</b>"},{"c":"</template>"}]</script>
</my-el>
Run Code Online (Sandbox Code Playgroud)