前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >试试原生 Web Component: 比你想象的容易

试试原生 Web Component: 比你想象的容易

作者头像
前端修罗场
发布2022-07-29 08:01:14
7290
发布2022-07-29 08:01:14
举报
文章被收录于专栏:Web 技术

组件是前端的发展方向,现在流行的 React 和 Vue 都是组件框架。谷歌公司由于掌握了 Chrome 浏览器,一直在推动浏览器的原生组件,即 Web Components API。相比第三方框架,原生组件简单直接,符合直觉,不用加载任何外部模块,代码量小。目前,它还在不断发展,但已经可用于生产环境。下面我们从<templete>标签说起。

从 < template > 标签开始

<template>是一个HTML元素,它允许我们创建一个模板——web组件的HTML结构。模板不必是一大块代码。它可以很简单:

代码语言:javascript
复制
<template>
  <p>web component</p>
</template>

<template>元素很重要,因为它把所有东西都固定在一起。它就像建筑的地基;它是其他一切赖以建立的基础。

< slot > 标签

<slot>只是另一个HTML元素,就像<template>一样。但是在这种情况下,<slot>定制了<template>在页面上呈现的内容。

代码语言:javascript
复制
<template>
  <p>The <slot>web components</slot> are coming!</p>
</template>

在这里,我们在模板标记中插入了“web components”这个词。如果我们对这个插槽不做任何事情,它默认为标签之间的内容。在这个例子中就是“web components”。

使用<slot>很像使用占位符。我们可以直接使用占位符,或者定义其他东西来代替。我们使用name属性来实现这一点。

代码语言:javascript
复制
<template>
  <p>The <slot name="whats-coming">web components</slot> are coming!</p>
</template>

name属性告诉web组件哪些内容应该放在模板的哪个位置。现在,我们有一个插槽叫“whats-coming”。

使用组件

从技术上讲,我们已经完成了组件的“编写”工作,可以把它放到任何我们想要使用它的地方。

代码语言:javascript
复制
<web-component>
  <span slot="whats-coming">web app is coming</span>
</web-component>

<template>
  <p>The <slot name="whats-coming">web components</slot> are coming!</p>
</template>

看到我们做了什么了吗?我们把<web-component>组件放在页面上,就像其他的<div>或其他组件一样。但我们还在这里添加了一个<span>,它引用了<slot>name属性。当组件渲染时,在<span>之间的是我们想要换成“web components”的东西。

注册组件

正如我所说的,你确实需要一些JavaScript来完成这些工作,但它并不是我一直认为的那种超级复杂、有上千行代码、有深度的代码。希望我也能说服你们。

你需要一个注册自定义元素的构造函数否则,我们的组件就像亡灵一样:它就在那里,但不是完全活着的。

下面是我们将使用的构造函数:

代码语言:javascript
复制
// 用适当的名称<web-component>定义自定义元素 

<web-component>
customElements.define("web-component",

  // 确保拥有HTML元素内建的所有默认属性和方法
  class extends HTMLElement {

    // 在创建新的自定义元素时调用
    constructor() {

      // 调用父构造函数,即' HTMLElement '的构造函数,这样所有的一切都设置得与我们创建内置HTML元素时完全一样
      super();

      // 获取<模板>并将其存储在' warning '中
      let warning = document.getElementById("warningtemplate");

      // 将模板的内容存储在' mywarning '中
      let mywarning = warning.content;

      const shadowRoot = this.attachShadow({mode: "open"}).appendChild(mywarning.cloneNode(true));
    }
  });

我在那里留下了详细的注释,一行一行地解释事情。除了最后一行:

代码语言:javascript
复制
const shadowRoot = this.attachShadow({mode: "open"}).appendChild(mywarning.cloneNode(true));

我们在这里做了很多事。首先,我们使用自定义元素(this)并创建一个秘密操作——shadow DOMmode: open仅仅意味着:根之外的JavaScript可以访问和操作shadow DOM中的元素,有点像设置对组件的后门访问。

从那里,shadow DOM已经创建,我们将向它添加一个节点。该节点将是模板的深层副本,包括模板的所有元素和文本。模板附加到自定义元素的shadow DOM后,<slot>slot属性将接管内容与它应该去的地方的匹配。

看看这个。现在我们可以弹出同一个组件的两个实例,只需更改一个元素就可以呈现不同的内容。

HTML:

代码语言:javascript
复制
<p>The Apocalypse will never happen!</p>
<apocalyptic-warning>
   <span slot="whats-coming">Undead</span>
</apocalyptic-warning>
<apocalyptic-warning>
   <span slot="whats-coming">Halitosis Laden Zombie Minions</span>
</apocalyptic-warning>
<template id="warningtemplate">
  <style>
    p {
      background-color: pink;
      padding: 0.5em;
      border: 1px solid red;
    }
  </style>
    <p>The <slot name="whats-coming">Zombies</slot> are coming!</p>
</template>

js:

代码语言:javascript
复制
customElements.define('apocalyptic-warning',
    class extends HTMLElement {
      constructor() {
        super();
        let warning = document.getElementById('warningtemplate');
        let mywarning = warning.content;

         const shadowRoot = this.attachShadow({mode: 'open'}).appendChild(mywarning.cloneNode(true));

      }
});

样式组件

你可能已经注意到演示中的样式。如你所料,我们完全有能力用CSS来为组件设定样式。事实上,我们可以在<template>中同时包含<style>元素。

代码语言:javascript
复制
<template id="warningtemplate">
  <style>
    p {
      background-color: pink;
      padding: 0.5em;
      border: 1px solid red;
    }
  </style>

    <p>The <slot name="whats-coming">web components</slot> are coming!</p>
</template>

通过这种方式,样式的作用域直接限定在组件上,并且由于shadow DOM,不会泄露给同一页面上的其他元素。

现在,在我的脑海中,我假设一个定制元素获取模板的一个副本,插入您添加的内容,然后使用shadow DOM将其注入到页面中。虽然这是它在前端的样子,但在DOM中却不是这样工作的。自定义元素中的内容保持在它所在的位置,而shadow DOM 有点像覆盖一样被放置在顶部。

而且,由于内容在技术上是在模板之外的,所以我们在模板的<style>元素中使用的任何后代选择器或类都不会对有插槽的内容产生影响。这并不允许以我希望或期望的方式进行完全封装。但由于自定义元素也是元素,我们可以在任何CSS文件中使用它作为元素选择器,包括页面上使用的主样式表。尽管从技术上讲,插入的材料不在模板中,但它在自定义元素中,CSS中的后代选择器也可以工作。

代码语言:javascript
复制
apocalyptic-warning span {
  color: blue;
}

源码:

HTML

代码语言:javascript
复制
<p>The Apocalypse will never happen!</p>
<apocalyptic-warning>
   <span slot="whats-coming">Undead</span>
</apocalyptic-warning>
<apocalyptic-warning>
   <span slot="whats-coming">Halitosis Laden Zombie Minions</span>
</apocalyptic-warning>
<template id="warningtemplate">
  <style>
    p {
      background-color: pink;
      padding: 0.5em;
      border: 1px solid red;
    }
    p span {
      color: red;
    }
  </style>
    <p>The <slot name="whats-coming">Zombies</slot> are coming!</p>
</template>

CSS

代码语言:javascript
复制
apocalyptic-warning span {
 color: blue;
}

JS

代码语言:javascript
复制
customElements.define('apocalyptic-warning',
    class extends HTMLElement {
      constructor() {
        super();
        let warning = document.getElementById('warningtemplate');
        let mywarning = warning.content;

         const shadowRoot = this.attachShadow({mode: 'open'}).appendChild(mywarning.cloneNode(true));

      }
   });

整合

让我们来看一个例子,比如一个约会服务的简介,就像你在世界末日后可能需要的一个简介。为了同时样式化默认内容和插入内容,我们需要在<template>中添加<style>元素,并在CSS文件中添加样式。

JavaScript代码完全一样,除了我们现在使用的是一个不同的组件名<zombie-profile>

JS

代码语言:javascript
复制
customElements.define("zombie-profile",
  class extends HTMLElement {
    constructor() {
      super();
      let profile = document.getElementById("zprofiletemplate");
      let myprofile = profile.content;
      const shadowRoot = this.attachShadow({mode: "open"}).appendChild(myprofile.cloneNode(true));
    }
  }
);

下面是HTML模板,包括封装的CSS:

代码语言:javascript
复制
<template id="zprofiletemplate">
  <style>
    img {
      width: 100%;
      max-width: 300px;
      height: auto;
      margin: 0 1em 0 0;
    }
    h2 {
      font-size: 3em;
      margin: 0 0 0.25em 0;
      line-height: 0.8;
    }
    h3 {
      margin: 0.5em 0 0 0;
      font-weight: normal;
    }
    .age, .infection-date {
      display: block;
    }
    span {
      line-height: 1.4;
    }
    .label {
      color: #555;
    }
    li, ul {
      display: inline;
      padding: 0;
    }
    li::after {
      content: ', ';
    }
    li:last-child::after {
      content: '';
    }
    li:last-child::before {
      content: ' and ';
    }
  </style>

  <div class="profilepic">
    <slot name="profile-image"><img src="https://assets.codepen.io/1804713/default.png" alt=""></slot>
  </div>

  <div class="info">
    <h2><slot name="zombie-name" part="zname">Zombie Bob</slot></h2>

    <span class="age"><span class="label">Age:</span> <slot name="z-age">37</slot></span>
    <span class="infection-date"><span class="label">Infection Date:</span> <slot name="idate">September 12, 2025</slot></span>

    <div class="interests">
      <span class="label">Interests: </span>
      <slot name="z-interests">
        <ul>
          <li>Long Walks on Beach</li>
          <li>brains</li>
          <li>defeating humanity</li>
        </ul>
      </slot>
    </div>

    <span class="z-statement"><span class="label">Apocalyptic Statement: </span> <slot name="statement">Moooooooan!</slot></span>

  </div>
</template>

下面是<zombie-profile>元素的CSS,以及它在主CSS文件中的后代。注意这里的重复,以确保替换的元素和模板中的元素的样式是相同的。

代码语言:javascript
复制
zombie-profile {
  width: calc(50% - 1em);
  border: 1px solid red;
  padding: 1em;
  margin-bottom: 2em;
  display: grid;
  grid-template-columns: 2fr 4fr;
  column-gap: 20px;
}
zombie-profile img {
  width: 100%;
  max-width: 300px;
  height: auto;
  margin: 0 1em 0 0;
}
zombie-profile li, zombie-profile ul {
  display: inline;
  padding: 0;
}
zombie-profile li::after {
  content: ', ';
}
zombie-profile li:last-child::after {
  content: '';
}
zombie-profile li:last-child::before {
  content: ' and ';
}

虽然还有一些问题和其他细微差别,但我希望您现在比几分钟前更有能力使用web组件。就像我们这里一样,先试一下。也许你可以在你的工作中到处添加一个自定义组件,以获得一种感觉,以及它在哪里有意义。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端修罗场 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 组件是前端的发展方向,现在流行的 React 和 Vue 都是组件框架。谷歌公司由于掌握了 Chrome 浏览器,一直在推动浏览器的原生组件,即 Web Components API。相比第三方框架,原生组件简单直接,符合直觉,不用加载任何外部模块,代码量小。目前,它还在不断发展,但已经可用于生产环境。下面我们从<templete>标签说起。
  • 从 < template > 标签开始
  • < slot > 标签
  • 使用组件
  • 注册组件
  • 样式组件
  • 整合
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档