Web Component 中的 Template

1,044 阅读5分钟

「这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战」。

前言

Web Components 是一套不同的技术,允许我们创建可重用的定制元素(它们的功能封装在您的代码之外)并且在我们的web应用中使用它们。

在我们平时的开发当中,都知道要尽可以的重用代码。Web Components 就是用来创建封装功能的定制元素,然后可以在指定的地方重复使用。

它由三项主要技术组成:

1. Custom elements(自定义元素)
2. Shadow DOM(影子DOM)
3. HTML templates(HTML模板)

本篇主要来讲一下 HTML templates(HTML模板) 比如我们平常经常使用的vue框架的原理,也是基于这个实现的。

什么是Template

1.<template> 标签

<template> 标签被称为内容模板元素是一种用于保存客户端内容机制,该内容在加载页面时不会呈现( 默认的 displaynone ),但随后可以在运行时使用JavaScript实例化。

在 Web Component 中的 template 作用是一样的,在它创建的 html ,在加载完后的页面是不会显示的,这样子的话,我们就可以创建一堆的 template 标签 用来保存视图,然后在需要的时候通过js脚本进行渲染,实现一个按需加载的效果

比如正常情况下

<!-- <template> -->
    <img src="https://img1.baidu.com/it/u=940811906,4152926232&fm=26&fmt=auto" alt="加载失败">
<!-- </template> -->

会在浏览器上显示一张图片:

image.png

而使用 <template> 标签做一个包裹则不会显示,因为 <template> 标签默认的 displaynone

image.png

2.<slot> 插槽

slot的通俗理解就是“占坑”,在组件中占好了位置。当使用该组件标签的时候,里面的内容会自动替换

为什么要使用Template

在这之前就要大概说一下 Web Component 的实现,也就是如何将一个自定义元素变成 Web Components 组件

1.自定义一个HTML标签

<user-card></user-card>

要注意的是自定义的元素必须包含连线,用来和原生的HTML标签区分开来

2.为这个自定义元素添加内容

首先定义一个类,注意这个类要继承HTMLElement,这样才能够拥有HTML元素的特性

class UserCard extends HTMLElement {
    constructor() {
        super();
    }
}

然后为这个类添加内容,再将这个类和自定义标签做一个绑定,这样类里面的内容才能反应到自定义标签上。绑定的过程用到了浏览器原生的 customElements.define() 方法。

class UserCard extends HTMLElement {
    constructor() {
        super();

        let box = document.createElement('div');
        box.classList.add('box');

        let image = document.createElement('img');
        image.src = 'https://img1.baidu.com/it/u=940811906,4152926232&fm=26&fmt=auto';
        image.classList.add('image');

        let name = document.createElement('p');
        name.classList.add('name');
        name.innerText = 'zhangsan';

        let age = document.createElement('p');
        age.classList.add('email');
        age.innerText = '18';

        let button = document.createElement('button');
        button.classList.add('button');
        button.innerText = 'Follow';

        this.append(box);
        box.append(image, name, age, button);
    }
}
window.customElements.define('user-card', UserCard);//将类和自定义标签做绑定

类当中最后的 this 代表的是这个自定义元素。这样之后,页面中的dom结构就已经生成了,可以在添上一些css样式,这里css代码就不放上来了

image.png

这样我们就简单的实现了一个自定义的标签,接下来, <template> 的作用就在于:我们可以在它里面事先写好HTML结构,然后在这个类里面去使用它就可以了,这样子就可以省去很多在JavaScript中写dom的不方便

3.使用<template> 来完成上面的样式

我们可以直接在body中定义<template>来存放我们想要等等添加到自定义标签里的内容,并且因为<template>是不会被显示的,所以并不会影响页面视图。比如:

<template id="userCardTemplate">
    <div class="box">
        <img src="https://img1.baidu.com/it/u=940811906,4152926232&fm=26&fmt=auto" class="image">
        <p class="name">zhangsan</p>
        <p class="age">18</p>
        <button class="button">Follow</button>
    </div>
</template>

然后再将它添加到自定义标签当中,这里要注意,因为这个<template>中存储的样式不一定只使用一次,所以在添加的时候,要克隆一份标签添加进去,这里要使用 Node.cloneNode 来进行节点克隆,这样操作最后的结果和上面的是一样的

class UserCard extends HTMLElement {
    constructor() {
        super();

        var templateElem = document.getElementById('userCardTemplate');
        var content = templateElem.content.cloneNode(true);
        this.appendChild(content);
    }
}

到这就能够看的出来,使用了<template>能够减少大量的在JS里进行节点的繁琐操作,万一这个组件非常的巨大,那么用JS去描述是非常的麻烦的

4.在 <template> 中封装样式

<template>不止是可以将大量的HTML写在其中,还可以在里面写css样式,这样的样式可以定义不对外面产生影响,真正的实现组件化:

<template id="userCardTemplate">
        <style>
        .box {
            width: 220px;
            background-color: rgb(180, 180, 180);
            margin: 20px auto;
            padding: 10px 0;
            border-radius: 8px;
        }
        img {
            display: block;
            width: 200px;
            height: 200px;
            margin: auto;
        }
        p {
            margin: 0;
            text-align: center;
            margin-top: 10px;
            font-weight: bold;
        }
        button {
            display: block;
            margin: 10px auto;
            text-align: center;
            width: 100px;
            height: 30px;
            border: none;
            border-radius: 4px;
            background-color: rgb(81, 199, 253);
            color: #fff;
            font-weight: bold;
        }
        </style>
        <div class="box">
            <img src="https://img1.baidu.com/it/u=940811906,4152926232&fm=26&fmt=auto" class="image">
            <p class="name">zhangsan</p>
            <p class="age">18</p>
            <button class="button">Follow</button>
        </div>
    </template>

内部有一个 :host 伪类可以代表元素本身

然后在类里面可以去设定 Shadow DOM 设定这部分里的代码于外部隔离,这里简单说明,只需要在类中设定this.attachShadow() :

var shadow = this.attachShadow( { mode: 'closed'|'open' } );

closeopen 分别是对不对外开放这个组件

4.使用插槽来增加灵活性

比如我们可以在示例中添加一个插槽

<p class="name"><slotname="my-text">zhangsan</slot></p>

如果在标记中包含元素时未定义插槽的内容,或者如果浏览器不支持插槽, 的内容则会是插槽中默认的内容

然后我们可以在自定义标签中定义插槽的内容来填充,比如:

 <user-card>
   <ul slot="my-text">
       <li>Let's have some different text!</li>
    	 <li>In a list!</li>
  </ul>
</user-card>

总结

HTML templates 的合理使用能够减少很多的不必要的html代码的复用,在和插槽做一个相结合能够在标签当中去自定义部分的内容,提升模板的灵活性。

引用外部文章:
<template>:内容模板元素 - HTML(超文本标记语言) | MDN
web component 【Template】 创建自己的简单SPA应用
Web Components 入门实例教程 - 阮一峰的网络日志