前言
组件化是前端的发展方向,现在所流行的框架vue和react都是组件化的形式,所以我们必须紧跟时代的脚步,本章让我们来了解一下Web component(组件)中的Template(模板)的具体作用和用法。
什么是Template
template是模板的意思,由于<template>中存放HTML代码,所以他是HTML内容模板元素
可以简单理解为: <template>是一段HTML代码的外壳,将HTML代码进行包装,
Template的作用
我们可以把template看作是一个可存储在文档中以便后续使用的内容片段,
在页面加载时template中的内容不会显示,就像一个工具被放在仓库里,等你需要的时候可以使用JavaScript来将他实例化使用,下来面具体分析template的实际操作
Template的用法
我们可能在写项目的时候经常会遇到一会结构比较相似或者相同的地方,比如这是一个用户信息模板,现在有一个需求,当点击下放按钮时,切换用户信息,这时候该怎么实现?
1. 原生写法
最简单的写法,但是会让代码非常庞大,
<div class="user-card">
<img class="image">
<div class="container">
<p class="name"></p>
<p class="email"></p>
<button class="button">关注</button>
</div>
</div>
<button>小红</button>
<button>小明</button>
<script>
//点击小明
clickMing(){
//获取元素
const image = document.createElement('img');
//赋值
image.src = 'https://semantic-ui.com/images/avatar2/large/kristy.png';
const container = document.createElement('div');
const name = document.createElement('p');
name.innerText = 'User Name';
const email = document.createElement('p');
email.innerText = 'yourmail@some-email.com';
const button = document.createElement('button');
button.innerText = 'Follow';
//每一个元素都需要获取然后赋值,是非常麻烦的,代码也不简洁,
}
//再写一个小红的点击事件,那代码量就非常多了,而且这种方式也非常的不方便需要传入参数,不然数据是写死的,
</script>
2. 自定义元素
像这种会经常使用,或者多次使用的模块,我们可以写一个自定义元素,写好之后只需要使用标签就可以生成该样式,自定义元素必须包含连词线,所以必须是这种形式user-card
<user-card></user-card>
那么要如何写一个自定义元素呢?
想要自定义元素就需要使用JavaScript定义一个类,继承HTMLElement,
class UserCard extends HTMLElement {
constructor() {
super();
}
}
由于继承了HTMLElement,所以自定义的这个类就有了父类的特性,使用浏览器原生的customElements.define()方法,告诉浏览器<user-card>元素与这个类关联。
window.customElements.define('user-card', UserCard);
user-card就是自定义的标签,后续使用在html中,而UserCard就是用来描述这个user-card行为的类,也就是js代码。
简单来说,他们现在虽然已经关联,但是UserCard这个类中还是空的,所以无论你怎么操作user-card,都不会生成元素,所以这个标签的展示内容,还是由UserCard来决定的
class UserCard extends HTMLElement {
constructor() {
super();
//添加元素
var image = document.createElement('img');
//赋值
image.src = 'https://semantic-ui.com/images/avatar2/large/kristy.png';
//添加class
image.classList.add('image');
var container = document.createElement('div');
container.classList.add('container');
var name = document.createElement('p');
name.classList.add('name');
name.innerText = 'User Name';
var email = document.createElement('p');
email.classList.add('email');
email.innerText = 'yourmail@some-email.com';
var button = document.createElement('button');
button.classList.add('button');
button.innerText = 'Follow';
container.append(name, email, button);
this.append(image, container);
}
}
虽然使用了自定义元素,但数据还是写死,无法修改,而且代码就和上一种方法差不多,只是使用js来生成HTNL元素, 这个时候就可以展现template的作用了
3.template的使用
先将代码封装好,即使不使用这段代码,页面也不会显示,你随时可以使用,不适用也不会影响你
//模板
<template id="userCardTemplate">
<img class="image">
<div class="container">
<p class="name"></p>
<p class="email"></p>
<button class="button">关注</button>
</div>
</template>
//js
<script>
class UserCard extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({
mode: 'closed'
});
const templateElem = document.getElementById('userCardTemplate');
const content = templateElem.content.cloneNode(true);
content.querySelector('img').setAttribute('src', this.getAttribute('image'));
content.querySelector('.container>.name').innerText = this.getAttribute('name');
content.querySelector('.container>.email').innerText = this.getAttribute('email');
shadow.appendChild(content);
}
}
window.customElements.define('user-card', UserCard);
</script>
这边来解析一下script中的代码:
- 创建类前面已经讲过了,
constructor是构造函数,这是类的基础this.attachShadow()方法给指定的元素挂载一个Shadow DOM,这里的this就是UserCard,后续会与user-card绑定 可以查阅文档const templateElem = document.getElementById('userCardTemplate');获取template节点const content = templateElem.content.cloneNode(true);克隆template的子节点- 后面会在标签中传入对应是数据,这些是设置数据对应绑定
content.querySelector('img').setAttribute('src', this.getAttribute('image'));content.querySelector('.container>.name').innerText = this.getAttribute('name');content.querySelector('.container>.email').innerText = this.getAttribute('email');shadow.appendChild(content);将content内容添加到shadow中window.customElements.define('user-card', UserCard);将元素和类关联
在html中引用自定义标签,并且传入相应数据即可
<user-card image="https://semantic-ui.com/images/avatar2/large/kristy.png" name="小红" email="yourmail@some-email.com"></user-card>
4.实现切换
附上完整代码:
<body>
<div class="user-card">
<user-card image="https://semantic-ui.com/images/avatar2/large/kristy.png" name="小红" email="yourmail@some-email.com"></user-card>
</div>
<button class="hong">小红</button>
<button class="ming">小明</button>
<template id="userCardTemplate">
<img class="image">
<div class="container">
<p class="name"></p>
<p class="email"></p>
<button class="button">关注</button>
</div>
</template>
<script>
class UserCard extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({
mode: 'closed'
});
const templateElem = document.getElementById('userCardTemplate');
const content = templateElem.content.cloneNode(true);
content.querySelector('img').setAttribute('src', this.getAttribute('image'));
content.querySelector('.container>.name').innerText = this.getAttribute('name');
content.querySelector('.container>.email').innerText = this.getAttribute('email');
shadow.appendChild(content);
}
}
window.customElements.define('user-card', UserCard);
let user = [{
name: "小明",
img: "https://semantic-ui.com/images/avatar2/large/kristy.png",
email: "xiaoming@some-email.com"
}, {
name: "小红",
img: "https://semantic-ui.com/images/avatar2/large/kristy.png",
email: "xiaohong@some-email.com"
}]
document.querySelectorAll('button').forEach(item => {
item.addEventListener('click', function(e) {
let data
user.forEach(item => {
if (item.name === e.target.innerHTML) {
data = item
}
})
const usercard = document.querySelector('.user-card')
usercard.innerHTML = `<user-card image="` + data.img + `" name="` + data.name + `" email="` + data.email + `" ></user-card>`
})
})
</script>
</body>
这里我使用的是自定义一个数组,将数据存入,然后给user-card的外面套一层盒子,点击事件的时候,直接切换盒子里的user-card标签和数据即可,遍历数组,点击小明,判断和数组中哪一个数据的name相等,就把数据赋值给data,使用data渲染给user-card,实现卡片的切换,
总结
代码其实很简单,里面的逻辑非常的简单,我就简单的解释一下应该就没问题了, 主要是体验一下template的使用,了解template的原理和作用。并且简单实战一下,能更清晰的了解他的用法。学习完就可以自行创建自己喜欢的模板进行测试啦!!!
最后附上css代码,自行引入一下就可以了
<style>
button {
margin-top: 20px;
padding: 10px 25px;
font-size: 12px;
border-radius: 5px;
}
:host {
display: flex;
align-items: center;
width: 450px;
height: 180px;
background-color: #d4d4d4;
border: 1px solid #d5d5d5;
box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
border-radius: 3px;
overflow: hidden;
padding: 10px;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
.image {
flex: 0 0 auto;
width: 160px;
height: 160px;
vertical-align: middle;
border-radius: 50%;
}
.container {
box-sizing: border-box;
padding: 20px;
height: 160px;
padding-top: 30px;
}
.container>.name {
font-size: 20px;
font-weight: 600;
line-height: 1;
margin: 0;
margin-bottom: 5px;
}
.container>.email {
font-size: 12px;
opacity: 0.75;
line-height: 1;
margin: 0;
margin-bottom: 15px;
}
.container>.button {
padding: 10px 25px;
font-size: 12px;
border-radius: 5px;
text-transform: uppercase;
}
</style>