组件化是个老生常谈的话题了,React 和 Vue 都是组件框架。浏览器本身也有组件化方案。即浏览器原生组件Web Components。原生组件简单直接,不需要加载任何外部模块。下面介绍Web Components 相关的几种技术。Web Components 主要由四种技术组成
Custom Elements
自定义的 HTML 标签,也称为自定义元素(custom element)。是Web Components的核心技术。
- 根据规范,自定义元素的名称必须包含连词线,用于区别原生的 HTML 元素。所以,
<user-info>不能写成<userInfo> - 创建一个继承HTMLElement的类,注意类必须调用super();然后使用
customElements.define()进行注册;我们就可以使用自定义元素了。
<body>
<user-info></user-info>
<script>
class UserInfo extends HTMLElement {
constructor() {
super();
var el = document.createElement('p');
el.innerText = 'User Name';
var el2 = document.createElement('p');
el2.innerText = '132****1';
this.append(el,el2);
}
}
window.customElements.define('user-info', UserInfo);
</script>
</body>
生命周期
构造函数中可以指定自定义元素的生命周期,将会在不同阶段调用。具体包括四个:
connectedCallback:当 custom element 首次被插入文档 DOM 时,被调用。disconnectedCallback:当 custom element 从文档 DOM 中删除时,被调用。adoptedCallback:当 custom element 被移动到新的文档时,被调用。attributeChangedCallback: 当 custom element 增加、删除、修改自身属性时,被调用。
<script>
class UserInfo extends HTMLElement {
constructor() {
super();
var el = document.createElement('p');
el.innerText = 'User Name';
var el2 = document.createElement('p');
el2.innerText = '132****1';
this.append(el,el2);
}
connectedCallback() {
}
}
window.customElements.define('user-info', UserInfo);
</script>
template
使用 JavaScript 写 DOM 结构很麻烦,Web Components API 提供了<template>标签,可以在它里面直接使用 HTML,更加直观。
<template id="userInfoTemplate">
<p class="name"></p>
<p class="phone">132****1</p>
</div>
</template>
改写一下自定义元素的类,为自定义元素加载<template>
<user-info
name="User Name"
phone="132****1111"
></user-info>
class UserInfo extends HTMLElement {
constructor() {
super();
var templateElem = document.getElementById('userInfoTemplate');
var content = templateElem.content.cloneNode(true); content.querySelector(".name").innerText = this.getAttribute("name");
content.querySelector(".phone").innerText = this.getAttribute("phone");
this.appendChild(content);
}
}
- 获取到模板之后,克隆了它的所有子元素,克隆是为了复用,因为页面上的模板并不是一次性的,可能其他的组件也要引用。
- this.getAttribute 可以获取到组件的传参。
样式
给自定义元素添加样式,组件的样式应该与代码封装在一起,只对自定义元素生效,不影响外部的全局样式。可以把样式写在<template>里面即可。
<template id="userInfoTemplate">
<style>
p{
border-bottom: 1px solid #efefef;
}
</style>
<p class="name">User Name</p>
<p class="phone">132****1</p>
</template>
shadow Dom
Web Component 允许内部代码隐藏起来,这叫做 Shadow DOM,即这部分 DOM 默认与外部 DOM 隔离,内部任何代码都无法影响外部。
自定义元素的this.attachShadow()方法开启 Shadow DOM,详见下面的代码
class UserCard extends HTMLElement {
constructor() {
super();
var shadow = this.attachShadow( { mode: 'closed' } );
var templateElem = document.getElementById('userInfoTemplate');
var content = templateElem.content.cloneNode(true);
content.querySelector('.name').innerText = this.getAttribute('name');
content.querySelector('.phone').innerText = this.getAttribute('phone');
shadow.appendChild(content);
}
}
window.customElements.define('user-card', UserCard);
上面代码中,this.attachShadow()方法将一个 shadow root 附加到一个元素上。它的参数mode 属性,值为 open 或 closed,表示 Shadow DOM 内的节点是否能被外部获取。
HTML Import
为了解决加载外部网页这个问题,而提出来的。但是兼容性不是很好。
function supportImport() {
return 'import' in document.createElement('link');
}
if (supportImport()) {
console.log('浏览器支持import特性');
} else {
console.log('浏览器不支持import特性');
// 引入polyfill
var e = document.createElement('script');
e.src = '[https://unpkg.com/@webcomponents/webcomponentsjs@2.0.0/webcomponents-bundle.js](https://unpkg.com/@webcomponents/webcomponentsjs@2.0.0/webcomponents-bundle.js)';
document.body.appendChild(e);
}
HTML Import使得Web Component变得可分享了,其他人只要拷贝useInfo.html,就可以在自己的页面中使用了
<head>
<link rel="import" href="userInfo.html">
</head>
<body>
<user-info
name="User Name"
phone="132****1111"
></user-info>
</body>