今天的前端开发基本离不开React和Vue的支持,很多自定义组件库都衍生自这两个框架:Element(Vue)AntDesign(React)这些的出现组件库让我们可以直接使用封装好的组件,并且在开源社区的帮助下,出现了很多模板项目(vue-element-admin,AntDesignPro),让我们可以快速入手一个item。React和Vue虽然为我们的组件开发提供了便利,但是在组件开发思路上,一是自创的JSX语法,二是特有的单文件模板语法。两者的目标都是为组件提供一种封装方式。毕竟里面有原创的东西,和我们刚开始接触的Web基础的HTML、CSS、JS方法还是有一些区别的。今天要介绍的是通过HTML、CSS、JS实现自定义组件,也是目前浏览器原生提供的解决方案:WebComponents。什么是Web组件?WebComponents本身并不是一个单独的规范,而是一组DOMAPI和HTML规范,用于创建具有自定义名称的可重用HTML标签,可以直接在您的Web应用程序中使用。代码重用一直是我们的目标。在JS中,我们可以将可复用的代码封装成一个函数,但是对于复杂的HTML(包括相关的样式和交互逻辑),我们从来没有更好的复用方式。要么使用后端模板引擎,要么使用现有的框架重新封装DOMAPI,而WebComponents的出现就是为了补充浏览器在这方面的能力。如何使用Web组件?WebComponents中包含的几个规范已经在W3C和HTML标准中进行了标准化,主要由三部分组成:自定义元素(customelements):一组用于创建自定义HTML标签的JavaScriptAPI,以及允许在创建标签时进行一些操作或销毁;ShadowDOM(影子DOM):一组用于将创建的DOMTree插入到现有元素中的JavaScriptAPI,并且DOMTree不能被外部修改,所以不用担心元素被其他Local影响操作;HTML模板(HTMLtemplates):直接通过和 标签,其文本内容为:HelloShenfq。这种形式的自定义元素称为:Autonomouscustomelements,是一种可以直接在HTML中使用的独立元素。扩展现有的HTML标签除了定义全新的HTML标签外,我们还可以扩展现有的HTML标签。例如,如果我们需要封装一个与标签具有类似能力的组件,我们可以使用如下方法:if(this.hasAttribute("skills")&&this.getAttribute("skills").includes(',')){//读取技能属性的值constskills=this.getAttribute("skills").split(',');skills.forEach(skill=>{constitem=document.createElement("li");item.innerText=skill;this.appendChild(item);})}}}//扩展
标签customElements.define("skill-list",SkillList,{extends:"ul"});
要扩展现有标签,您需要使用customElements.define方法的第三个参数,第二个参数的类也需要继承相应的需要扩展类型的标签。使用时只需要在标签中添加is属性,属性值为第一个参数定义的名称。生命周期自定义元素的生命周期比较简单,只提供了四种回调方法:connectedCallback:当自定义元素被插入到页面的DOM文档中时调用。disconnectedCallback:当自定义元素从DOM文档中移除时调用。adoptedCallback:移动自定义元素时调用。attributeChangedCallback:当自定义元素添加、删除或修改其自身的属性时调用。下面演示如何使用它:classHelloUserextendsHTMLElement{constructor(){//必须调用super方法super();//创建一个div标签const$box=document.createElement("p");letuserName="用户名";if(this.hasAttribute("name")){//如果有name属性,读取name属性的值userName=this.getAttribute("name");}//设置div标签的文本内容$box.innerText=`你好${userName}`;//创建一个影子节点,创建的其他元素应该附加到这个节点constshadow=this.attachShadow({mode:"open"});shadow.appendChild($box);}connectedCallback(){console.log('Createelement')//5s后将元素移动到iframesetTimeout(()=>{constiframe=document.getElementsByTagName("iframe")[0]iframe.contentWindow.document.adoptNode(this)},5e3)}disconnectedCallback(){console.log('删除元素')}adoptedCallback(){console.log('移动元素')}}
