Web Components

Web Components
介绍
谷歌公司掌握了Chrome浏览器, 所以一直在推动浏览器的原生组件, 也就是Web Components API
相比于第三方框架, 原生组件简单直接, 不用加载任何外部模块, 代码量小
允许创建可重用的定制元素, 并且在我们的web应用中直接使用它们
核心技术组成
Custom elements 自定义元素
一组JavaScript API, 允许定义custom elements及其行为, 然后在用户界面中按照需要使用它们
Shadow DOM 影子DOM
一组JavaScript API, 用于将封装的影子DOM树附加到元素(与主文档DOM分开呈现), 并控制其关联功能
通过这种方式, 我们可以保持元素的功能私有, 这样它们就可以被脚本化和样式化, 而不用担心与文档的其它部分发生冲突
HTML template html模板
和元素使你可以编写不在呈现页面中显示的标记模板, 然后它们可以作为自定义元素结构的基础被多次重用
生命周期函数
connectedCallback
当自定义元素第一次被连接到文档DOM时被调用
disconnectedCallback
当自定义元素与文档DOM断开连接时被调用
adoptedCallback
当自定义元素被移动到新文档时被调用
attributeChangedCallback
当自定义元素的第一个属性被增加、移除或更改时被调用
实例开发
实例1:日期组件
01.png
目录结构

|-scripts

|–date.time.js

|-index.html
开发日期组件

(() => {

// 创建模板

const template = document.createElement(‘template’)

// 标签元素里面包含了我们组件里面的具体样式和DOM

template.innerHTML = `





















`

// 创建自定义datetime组件类DateTime, 只要继承HTMLElement类就可以编写自定义标签/元素

class DateTime extends HTMLElement {

/**

* constructor 生命周期函数

* @description 元素创建了, 但是没有添加到页面

* @description 一般来说需要在constructor中创建初始化状态, 事件监听和创建影子DOM

*/

constructor () {

// 调用super方法, 需要使用它来调用父级的构造方法

super()

/**

* attachShadow 影子DOM

* @param open 允许外界访问

* @param closed 关闭外界获取的途径

*/

this.attachShadow({ mode: ‘open’ })

/**

* shadowRoot 影子DOM的根节点

* @description template.content 是一个游离的文档碎片

* @description cloneNode(params) params -> boolean [true|false]

* true -> 深度遍历克隆

*/

this.shadowRoot.appendChild(template.content.cloneNode(true))

}

// 初始化时间

initDateTime () {

// 获取当前的时间对象

let date = new Date()

// 创建英文月份枚举数组

let month = [‘January’, ‘February’, ‘March’, ‘April’, ‘May’, ‘June’, ‘July’, ‘August’, ‘September’, ‘October’, ‘November’, ‘December’]

// 封装配置需要使用的年月日

let dateConfig = {

date: date.getDate() >= 10 ? date.getDate() : ‘0’ + date.getDate(),

month: month[date.getMonth()],

year: date.getFullYear()

}

// console.log(dateConfig)

// datetime内容渲染显示

this.shadowRoot.querySelector(‘.datetime-box__wrap–day’).innerText = dateConfig.date

this.shadowRoot.querySelector(‘.datetime-box__wrap–month’).innerText = dateConfig.month

this.shadowRoot.querySelector(‘.datetime-box__wrap–year’).innerText = dateConfig.year

}

/**

* connectedCallback 生命周期函数

* @description 当自定义元素第一次被连接到文档DOM时被调用

* @description 元素被插入DOM的时候执行, 一般来说用来获取数据, 设置默认的显示样式或者内容

*/

connectedCallback () {

this.initDateTime()

}

}

/**

* 全局注册组件

* @description 把组件类挂载到DOM上去, 自定义标签名使用我们的组件

*/

window.customElements.define(‘date-time’, DateTime)

})()
页面引用


实例2:列表(详情)组件
02.png
目录结构

|-images

|-scripts

|–article-info.js

|–article-info-detail.js

|–article-data.js

|–script.js

|-index.html
html结构



 



 
数据结构  let cardData = [  {  id: "1",  title: 'ndjscnsdcnksdc',  theme: 'Animal',  cover: './images/banner1.jpg',  avatar: './images/default.jpg',  author: 'Jerry',  comments: '1000',  likes: '4339',  shares: '400',  p: 'cnjdncsdkldsnmclkdmcl;sdmc;ldsc;lsdml;csd'  },  {  id: "2",  title: 'ndjscnsdcnksdc',  theme: 'cinms',  cover: './images/banner2.jpg',  avatar: './images/5.jpg',  author: 'Tom',  comments: '500',  likes: '432',  shares: '283',  p: 'skdnckcnlsdcdmcl;sdmcl;dmsclmdl;mocpmspodmcposdmcpo'  },  {  id: "3",  title: 'ndjscnsdcnksdc',  theme: 'Animal',  cover: './images/banner1.jpg',  avatar: './images/default.jpg',  author: 'hbbgf',  comments: '1000',  likes: '4339',  shares: '400',  p: 'dccfssssssssssssssssssdsvervesvf'  },  {  id: "4",  title: 'ndjscnsdcnksdc',  theme: 'cinms',  cover: './images/banner2.jpg',  avatar: './images/5.jpg',  author: 'cdsc',  comments: '500',  likes: '432',  shares: '283',  p: 'cdfcmlkfdnvkfdmvlsmvl;smvl;sml;sml;sdml'  }  ] 样式  .container-detail-wrap {  transition: .5s ease;  width: 100%;  height: 100vh;  overflow-x: hidden;  overflow-y: auto;  position: fixed;  left: 100%; // *  opacity: 0; // *  top: 0;  z-index: 3;  background-color: #fff;  } 开发列表组件  // article-info.js  (() => {  const template = document.createElement('template')  template.innerHTML = ` 

  

 

   Comment  



  • 
  • 
  • 








`

class ArticleInfo extends HTMLElement {

constructor () {

super()

this.attachShadow({ mode: ‘open’ })

this.shadowRoot.appendChild(template.content.cloneNode(true))

this.initEvent()

}

/**

* 初始化事件

* @description 使用CustomEvent定义自定义事件

*/

initEvent () {

const cardInfo = this.$(‘.card-info’)

cardInfo.addEventListener(‘click’, (ev) => {

let myEventInfo = new CustomEvent(‘card-click’, {

composed: true,

bubbles: true

})

this.dispatchEvent(myEventInfo)

})

}

$(selector) {

return this.shadowRoot.querySelector(selector)

}

initRender () {

this.KaTeX parse error: Expected group after '_' at position 13: ('.card-info_̲_cover--image')…(‘.card-info__cover–title’).innerText = this.getAttribute(‘theme’)

this.KaTeX parse error: Expected group after '_' at position 13: ('.card-info_̲_avatar').src =…(‘.card-info__author’).innerText = this.getAttribute(‘author’)

this.$(‘.card-info__title’).innerText = this.getAttribute(‘title’)

}

connectedCallback () {

this.initRender()

}

}

window.customElements.define(‘arcitle-info’, ArticleInfo)

})()
开发详情组件

(() => {

const template = document.createElement(‘template’)

template.innerHTML =  <img class="cover" src=""/>  <span class="theme"></span>  <img class="avatar" src=""/>  <span class="author"></span>  <span class="title"></span>  <span class="comments"></span>  <span class="likes"></span>  <span class="shares"></span>  <p class="p"></p>  <button class="back">Back</button> 

class ArticleInfoDetail extends HTMLElement {

constructor () {

super()

this.attachShadow({ mode: ‘open’ })

this.shadowRoot.appendChild(template.content.cloneNode(true))

this.initEvent()

}

initEvent () {

const backBtn = this.$(‘.back’)

backBtn.addEventListener(‘click’, (ev) => {

let myEvent = new CustomEvent(‘card-back-click’, {

composed: true,

bubbles: true

})

this.dispatchEvent(myEvent)

})

}

$(selector) {

return this.shadowRoot.querySelector(selector)

}

initRender () {

this. ( ′ . c o v e r ′ ) . s r c = t h i s . g e t A t t r i b u t e ( ′ c o v e r ′ )  t h i s . ('.cover').src = this.getAttribute('cover')  this. (.cover).src=this.getAttribute(cover)this.(‘.theme’).innerText = this.getAttribute(‘theme’)

this. ( ′ . a v a t a r ′ ) . s r c = t h i s . g e t A t t r i b u t e ( ′ a v a t a r ′ )  t h i s . ('.avatar').src = this.getAttribute('avatar')  this. (.avatar).src=this.getAttribute(avatar)this.(‘.author’).innerText = this.getAttribute(‘author’)

this. ( ′ . t i t l e ′ ) . i n n e r T e x t = t h i s . g e t A t t r i b u t e ( ′ t i t l e ′ )  t h i s . ('.title').innerText = this.getAttribute('title')  this. (.title).innerText=this.getAttribute(title)this.(‘.comments’).innerText = this.getAttribute(‘comments’)

this. ( ′ . l i k e s ′ ) . i n n e r T e x t = t h i s . g e t A t t r i b u t e ( ′ l i k e s ′ )  t h i s . ('.likes').innerText = this.getAttribute('likes')  this. (.likes).innerText=this.getAttribute(likes)this.(‘.shares’).innerText = this.getAttribute(‘shares’)

this.$(‘.p’).innerText = this.getAttribute(‘p’)

}

connectedCallback () {

this.initRender()

}

/**

* attributeChangedCallback生命周期函数

* @param name

* @param oldValue

* @param newValue

* @description 当自定义元素的第一个属性被增加、移除或更改时被调用

*/

attributeChangedCallback (name, oldValue, newValue) {

// console.log(name, oldValue, newValue)

this.initRender()

}

// 监听属性的变化 - 返回的每一个属性名对应的属性值发生变化, 都会触发对应的attributeChangedCallback方法

static get observedAttributes () {

return [‘id’, ‘cover’, ‘theme’, ‘avatar’, ‘author’, ‘title’, ‘comments’, ‘likes’, ‘shares’, ‘p’]

}

}

window.customElements.define(‘arcitle-info-detail’, ArticleInfoDetail)

})()
scripts.js

(() => {

let container = document.querySelector(‘.container’)

let str = ‘’

// 遍历获取所有的文章信息

cardData.forEach(item => {

str +=  <arcitle-info  id="${item.id}"  cover="${item.cover}"  theme="${item.theme}"  avatar="${item.avatar}"  author="${item.author}"  title="${item.title}">  <span slot="comments">${item.comments}</span>  <span slot="likes">${item.likes}</span>  <span slot="shares">${item.shares}</span>  </arcitle-info> 

})

// append到container中

container.innerHTML = str

document.body.addEventListener(‘card-click’, (ev) => {

let id = ev.target.id

let detailWrap = document.querySelector(‘.container-detail-wrap’)

let cardDetail = document.querySelector(‘arcitle-info-detail’)

// get arcitle data from id

let articleSelectData = cardData.filter(v => v.id === id)[0]

Object.keys(articleSelectData).forEach(key => {

cardDetail.setAttribute(key, articleSelectData[key])

})

// 动画显示文章详情页

detailWrap.style.left = 0;

detailWrap.style.opacity = 1;

})

document.body.addEventListener(‘card-back-click’, (ev) => {

let detailWrap = document.querySelector(‘.container-detail-wrap’)

// 动画显示文章详情页

detailWrap.style.left = ‘100%’;

detailWrap.style.opacity = 0;

})

})()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JackieChan_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值