如何用纯原生 JS 封装一个组件(三):template && slot

4,356 阅读2分钟

「这是我参与2022首次更文挑战的第18天,活动详情查看:2022首次更文挑战」。

写在前面

  • 前面一篇文章,我们提到了 web compontens 是一套原生 JavaScript 就已经支持组件封装的技术
  • 它由三个主要技术组成:自定义元素,HTML 模板,影子 DOM
  • 然后我们也介绍了 template 的用法,其实它就是定义通用模板的一个容器,我们可以将模板应用在任何我们需要的地方,并且 template 元素在浏览器渲染页面时不会显示在页面上,只会显示 template 包含的内容
  • 上一篇文章最后也提到了经常与 template 搭配使用的 slot 元素
  • slot 就是插槽,与 vue 中的插槽类似,也可以使用 具名插槽

匿名插槽

  • 话不多说,先写个例子,感受一下原生 JavaScript 实现的插槽
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <my-com myTitle="myCom-title">
      <span>这是 s1 具名插槽</span>
      <span>这是 s2 具名插槽</span>
      <span>这是 s3 具名插槽</span>
    </my-com>
  </body>

  <script>
    class MyCom extends HTMLElement {
      constructor() {
        super();
        console.log(this);

        // props
        const title = this.getAttribute("myTitle");

        // template、slot 搭配 shadow dom
        const templateDom = document.createElement("template");
        templateDom.innerHTML = `
          <div>
            <h2 class="title">template demo title:${title}</h2>
            2222-【<slot></slot>】-2222
            <br />
            3333-【<slot></slot>】-3333
            <br />
            1111-【<slot></slot>】-1111
          </div>
        `;

        const divTemplate = templateDom.content;
        
        // open 表示可以通过页面内的 JavaScript 方法来获取 Shadow DOM
        let shadowDom = this.attachShadow({ mode: "open" }); 
        
        shadowDom.append(divTemplate);
      }
    }

    customElements.define("my-com", MyCom);
  </script>
</html>
  • 这里使用自定义元素注册了一个 my-com 元素,my-com 关联的 MyCom 为自定义元素的构造函数
  • 创建一个 template 标签,并且构建我们的组件的结构标签,然后取出 template 的 content
  • 然后通过 shadom dom 技术将 content 添加到自定义元素内部即可
    • 这里有写到 shadom dom,是下一篇文章会介绍的知识点,本文先不做过多介绍
  • 我们看下现象 image.png
  • 可以看到,使用匿名插槽时,自定义元素包裹的内容会被放到第一个 slot 中
  • 接下来我们看下具名插槽的用法

具名插槽

  • 用过 vue 的小伙伴都知道具名插槽的用法,其实这里的用法也是一样的
  • 我们先看个例子
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <my-com myTitle="myCom-title">
      <div>my-com template</div>
      <span slot="s1">这是 s1 具名插槽</span>
      <span slot="s2">这是 s2 具名插槽</span>
      <span slot="s3">这是 s3 具名插槽</span>
    </my-com>

    <hr />

    <div>other</div>
  </body>

  <script>
    class MyCom extends HTMLElement {
      constructor() {
        super();
        console.log(this);

        // props
        const title = this.getAttribute("myTitle");

        // template、slot 搭配 shadow dom
        const templateDom = document.createElement("template");
        templateDom.innerHTML = `
          <style>
            div{
              background: teal;
            }
          </style>
          <div>
            <h2 class="title">template demo title:${title}</h2>
            2222-【<slot name="s2"></slot>】-2222
            <br />
            3333-【<slot name="s3"></slot>】-3333
            <br />
            1111-【<slot name="s1"></slot>】-1111
          </div>
        `;

        const divTemplate = templateDom.content;

        // open 表示可以通过页面内的 JavaScript 方法来获取 Shadow DOM
        // let shadowDom = this.attachShadow({ mode: "closed" }); 
        
        // open 表示可以通过页面内的 JavaScript 方法来获取 Shadow DOM
        let shadowDom = this.attachShadow({ mode: "open" }); 
        shadowDom.append(divTemplate);
      }
    }

    customElements.define("my-com", MyCom);
  </script>
</html>
  • 具名插槽,顾名思义,就是给匿名插槽取个名字
  • 我们在使用时,给外部自定义元素子代标签加上 slot 属性
  • 其值与 template 内部的插槽 slot 的 name 属性值相同时
  • 就会在渲染时将外部的内容渲染到对应的插槽上,以到达具名插槽的作用

小结

  • template && slot 通常会一起组合使用
  • 并且在与 自定义元素 和 影子 dom 配合时,就可以做出一些有意思的组件
  • 在下篇文章中,我们会介绍 影子 dom 也就是 shadom dom 技术的一些知识

最后

  • 今天的分享就到这里,欢迎大家在评论区留言讨论
  • 如果觉得文章写的还不错的话,希望大家不要吝惜点赞,大家的鼓励是我分享的最大动力 🥰