Web Component入门
本文作者为奇舞团前端开发工程师
引言
前端开发者,现在在进行项目的开发时,一般很少使用原生的js代码,往往都会依靠Vue,React等框架进行开发,而不同的框架都有自己不同的开发规则,但是目前所使用的主流框架,都是遵循组件化开发的模式,即把不同功能的代码,拆分成不同的组件,以此来达到高内聚,低耦合,减少代码量等目的。目前主流的框架,均为是有公司或者公司开源。自己制定了一套完整的开发规范。谷歌在2011年的时候就已经提出了组件化开发的概念,即Web Component ,这个方案现在是被纳入了w3c规范之中。
如何构建
构建一个Web Component,我们需要按照以下三个步骤
1.定义模板
<template>
<div>web component</div>
<button>按钮</button>
</template>
我们在<template>
标签内部,编辑我们组件的结构样式,这里可以类比vue的模板写法。
2.组件逻辑编写
class MyWebComponent extends HTMLElement {
constructor() {
super();
// 深度克隆一份template
const content = template.content.cloneNode(true);
// 将克隆的template添加到dom树上
this.attachShadow({ mode: "closed" }).appendChild(content);
}
}
这里我们需要编写一个class,该class并且要继承于HTMLElement,然后我们在该类的构造函数里面,将我们书写的组件添加到dom树上。这里我使用attachShadow方法,再把content节点添加到dom,attachShadow的作用就是创建shadow dom,这也是web component中很重要的一个概念——影子dom,它和我们一般的dom有所不同,我们可以通过这个方法创造一个相对封闭且独立的dom,这个方法他会接收一个对象,对象的mode键值如果为closed,那么这个dom就为与外界隔离,该dom以外的脚本也无法对其进行操控,下面的图片,就向我们展示了什么是shadow dom。
3.组件注册
window.customElements.define("My-webComponent",MyWebComponent );
我们需要调用customElements.define方法,该方法接收两个参数,第一个参数是我们给组件自定义的标签名(这里我们需要注意一下,用-连接),第二个参数就是组件对应的class。
案例演示,如何编写一个单文件组件
我们有了前面的基础知识,下面,我们就采用我们前面所介绍的知识,来编写一个独立的组件。这个组件的功能也非常简单,我们通过父组件传递给子组件初始数据,进行展示,也可以由子组件,点击添加,为列表添加数据。
既然是采用组件化的写法,我们肯定要想办法把组件抽离成一个单文件的形式,方便我们进行复用,但是由于HTML imports这个方案已经被废弃,我们无法在一个html文件里面直接引入另外一个html页面。所以,如果我们想要实现原生组件复用,就需要把代码写在一个js文件里面,引入该js文件,就等于引入了组件。
//index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
//引入编写好的组件,在这里引入文件,注意要添加defer关键字
<script src="./MyList/index.js" defer></script>
<body>
<div>
//使用组件
<my-list id="node">
<!--原生支持插槽 -->
<slot>web component</slot>
</my-list>
</div>
<script>
//因为是原生,所以我们需要获取dom节点行后续操作
const node = document.getElementById("node");
//我们将变量转换一下格式,就能传递给子组件
node.dataset.arr = JSON.stringify(["吃饭", "睡觉"]);
</script>
</body>
</html>
//index.js
const template = document.createElement("template");
//在js文件中,我们想要书写html和css就必须要借助innerHTML,在其内部书写我们的样式和结构
template.innerHTML = `
<style>
#contain {
display: flex;
flex-direction: column
}
input {
width: 200px
}
</style>
<div id="contain">
<span><slot></slot></span>
<div>
<input type="text" id=input>
<button id="mybutton" data-text1="111111">添加</button>
</div>
</div>
`;
class MyList extends HTMLElement {
constructor() {
//因为我们的组件继承于HTMLElement,所以需要调用super关键字
super();
// 获取标签
const content = template.content.cloneNode(true);
const mybutton = content.getElementById("mybutton");
const input = content.getElementById("input");
const contain = content.getElementById("contain");
// 获取props
const arr = JSON.parse(this.dataset.arr);
//进行事件的监听
mybutton.addEventListener("click", () => {
arr.push(input.value)
const li = document.createElement("li");
li.innerText = input.value;
contain.appendChild(li);
});
// 将数据渲染到页面
arr.forEach((item) => {
const li = document.createElement("li");
li.innerText = item;
contain.appendChild(li);
});
//初始化一个影子dom
this.attachShadow({ mode: "closed" }).appendChild(content);
}
}
// 注册组件
window.customElements.define("my-list", MyList);
框架
通过前面,我们就可以感受到,编写一个Web Component组件,似乎并不是十分的方便,所以我要向大家推荐一个框架 stencil.js,通过它,我们就能使用jsx的语法,更加高效快速的来编写出一个Web Component组件。从而避免使用原始的js。
优缺点
通过前面的介绍,我想大家对Web Components应该有了最基本的了解,下面就给大家简单总结一下使用它的优缺点。
优点
浏览器原生支持,不用加入任何依赖 多种场景适用,天生组件隔离
缺点
跟主流的框架相比,书写较为复杂,需要开发者自己进行原生dom操作 若要写成单文件组件,需要采用模板字符串的写法,没有语法高亮,代码提示等
- END -
关于奇舞团
奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。