前言:Web Component不是新东西,是几年前的技术,vue等前端框架就是借鉴的Web Component,但是受限于浏览器兼容性,一直没有大规模应用在项目里,直到现在(2018年年末),除IE仍不支持之外,其它主流浏览器都支持Web Component。
一个组件就是对局部视图(html、css、js)的一个封装
Web Component不是一个东西,它分为四部分,分别是:
script模板是通过获取script的innerHTML来获取,template则是获取读取template节点的content属性来获取
// 获取模板内容
console.log(document.querySelector('template').content);
Web Component 允许内部代码隐藏起来,这叫做 Shadow DOM,即这部分 DOM 默认与外部 DOM 隔离,内部任何代码都无法影响外部。
Vue通过虚拟Dom 和双向绑定实现渲染, Polymer通过shadowDom和双向绑定实现渲染
大致结构:
window.customElements.define()
方法,告诉浏览器
元素与这个类关联。/*
*阮大神的代码借鉴
*/
<body>
<user-card image="https://semantic-ui.com/images/avatar2/large/kristy.png"
name="User Name"
email="[email protected]">user-card>
<br>
<user-card image="https://semantic-ui.com/images/avatar2/large/kristy.png"
name="User Name"
email="[email protected]">user-card>
<template id="userCardTemplate">
<img class="image">
<div class="container">
<p class="name">p>
<p class="email">p>
<button class="button">Follow Johnbutton>
div>
<style>
: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: 5px;
}
.container {
box-sizing: border-box;
padding: 20px;
height: 160px;
}
.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>
template>
body>
<script>
class UserCard extends HTMLElement {
constructor() {
super();
//创建影子DOM节点,this指向UserCard
//this.attachShadow()方法的参数{ mode: 'closed'},表示 Shadow DOM 是封闭的,不允许外部访问。
var shadow = this.attachShadow({ mode: 'closed' });//此时shadow相当于影化后的UserCard,可以这么理解
//获取组件模板
var templateElem = document.getElementById('userCardTemplate');
//拷贝组件模板中的内容,主要是一些css样式
var content = templateElem.content.cloneNode(true);
//对template模板进行DOM操作,this指向UserCard
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);//此时shadow相当于影化后的UserCard,也就是说appendChild后,就可以用到content来修改UserCard的样式
}
}
//使用浏览器原生的customElements.define()方法,告诉浏览器元素与这个类关联。
window.customElements.define('user-card', UserCard);
script>
<body>
<hello-world message="好好学习">hello-world>
<br>
<hello-world message="天天向上">hello-world>
<template id="hw-template">
<p>Hello Worldp>
<style>
p{
padding:10px;
background-color:#f40;
color:#fff
}
style>
template>
body>
<script>
class HelloWorld extends HTMLElement{
constuctor(){
super();
const templateContent = document.querySelector('#hw-template').content
const shadowRoot = this.attachShadow({mode:'open'})
shadowRoot.appendChild(templateContent.cloneNode(true))
//获取属性
const message = this.getAttribute('message')
shadowRoot.querySelector('p').innerText = message
}
}
customElement.define('hello-world')
script>
<body>
<hello-world message="好好学习">hello-world>
<br>
<hello-world message="天天向上">hello-world>
<template id="hw-template">
<p>Hello Worldp>
<style>
p{
padding:10px;
background-color:#f40;
color:#fff
}
style>
template>
body>
<script>
class HelloWorld extends HTMLElement{
constuctor(){
super();
const templateContent = document.querySelector('#hw-template').content
const shadowRoot = this.attachShadow({mode:'open'})
shadowRoot.appendChild(templateContent.cloneNode(true))
const message = this.getAttribute('message')
shadowRoot.querySelector('p').innerText = message
}
//首次插入DOM文档时调用
connectedCallback(){
//在这里发送数据请求(Ajax)
}
//被从文档DOM中删除时调用
disconnectedCallback(){
}
//被移动到新的文档时调用
adoptedCallback(){
}
//当增加、删除、修改自身的属性时被调用
attributeChangedCallback(){
}
}
customElement.define('hello-world')
script>
<body>
<hello-world>
<p slot="header">我是通过插槽进入的组件头部p>
<p slot="content">我是通过插槽进入的组件内容p>
hello-world>
<template id="hw-template">
<div>
<div class="card">
<div class="card-header">
<slot name="content">slot>
div>
<div class="card-content">
<slot name="content">slot>
div>
div>
div>
<style>
.card{
width:500px;
height:300px;
border:1px soild #000;
padding:5px;
}
.card-header{
padding:10px;
background-color:#ccc
}
style>
template>
body>
<script>
class HelloWorld extends HTMLElement{
constuctor(){
super();
const templateContent = document.querySelector('#hw-template').content
const shadowRoot = this.attachShadow({mode:'open'})
shadowRoot.appendChild(templateContent.cloneNode(true))
}
connectedCallback(){
}
disconnectedCallback(){
}
adoptedCallback(){
}
attributeChangedCallback(){
}
}
customElement.define('hello-world')
script>