这不是一篇原理分析的文章,它更像是一篇使用说明书。JTBC5 (以下简称 j5) 自定义组件是使用 Web Components 的方式来实现组件化,j5 为了使组件的使用、编写上更加方便、自动化,增加了一些附加的功能。本文分两部分介绍,第一部分说一下什么是 Web Components(web 自定义组件),以及如何使用;第二部分说一下 j5 的自定义组件是怎么实现自动加载的以及如何实现的。
一、自定义 web 组件(Web Components)。
1、什么是 Web Components
Web Components 是一组 web 原生 API 的统称,允许您创建可重用的自定义元素 (custom elements)并且在您的 web 应用中使用它们。参见 https://developer.mozilla.org/zh-CN/docs/Web/Web_Components 。Web Components 大部分技术规范兼容现代浏览器,因此它是一套原生的 web 组件规范。
2、Web Components 的使用
下面看一个常规的使用方法,示例代码如下
1)一个完全自定义的元素
/main.js
//代码来源 https://mdn.github.io/web-components-examples/edit-word/
//做了部分修改
customElements.define('edit-word',
class extends HTMLElement {
constructor() {
// 必须首先调用 super 方法
super();
// 创建一个 shadow root
const shadowRoot = this.attachShadow({mode: 'open'});
// 创建一个 html 模板
let shadowRootHTML = `
<style>:host {display: inline-block; --text-font-size: 12px;} span{display: inline-block;}</style>
<form style="display: none;"><input required="required"></form>
<span><slot></slot></span>
`;
// 将创建的模板附加到 shadow dom
shadowRoot.innerHTML = shadowRootHTML;
// 添加功能
let span = shadowRoot.querySelector('span');
let form = shadowRoot.querySelector('form');
let input = shadowRoot.querySelector('input');
this.addEventListener('click', () => {
span.style.display = 'none';
form.style.display = 'inline-block';
input.focus();
input.setSelectionRange(0, input.value.length)
});
form.addEventListener('submit', e => {
updateDisplay();
e.preventDefault();
});
input.addEventListener('blur', updateDisplay);
function updateDisplay() {
span.style.display = 'inline-block';
form.style.display = 'none';
span.textContent = input.value;
input.style.width = span.clientWidth + 'px';
}
}
}
);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>edit-word demo</title>
<script src="main.js" defer></script>
</head>
<body>
<h1><code>edit-word</code> demo</h1>
<p>我叫 <edit-word>张三</edit-word></p>
</body>
</html>
语法:
customElements.define(name, constructor, options);
a、name: 自定义元素名,例如上面的 “edit-word”,在 html 中可以直接使用如 <edit-word > 张三 </edit-word>
b、constructor:自定义元素构造器。对应上面的 匿名类 “class extends HTMLElement”, 当然也可以是具名类,类也可以放到别一个文件中,然后通过 import 引入,就像下面的 “WordCount” WordCount 这是一个类的引用。
c、options:(可选项)控制元素如何定义。目前有一个选项支持:
extends. 指定继承的已创建的元素。被用于创建自定义元素。如
customElements.define ('word-count', WordCount, { extends: 'p'}); 表示这个自定义元素继承自 p 标签
2)第二个是继承自基本的 HTML 元素的自定义标签
这个和上面结构基本一样,不同的地方一个是多出 options 选项来指定继承的基本标签元素;再一个是自定义元素够造器,也就是用来定义自定义元素功能的类(上面的 constructor 参数),这个类需要继承的不是 HTMLElement 而是和 options 中指定的要扩展的标签的类。如 p 标签 对应的是 HTMLParagraphElement。
使用时,需要先写出基本的元素标签,并通过 is
属性指定 custom element 的名称。例如<p is="word-count">
, 或者 document.createElement("p", { is: "word-count" })
。
例子就不在累述了,可以参见 https://developer.mozilla.org/zh-CN/docs/Web/Web_Components/Using_custom_elements
二、JTBC5 的自定义组件
j5 的自定义组件更加的自动化。只要把编写好的自定义标签的功能(对应上面 customElements.define 中的 constructor 参数)的类文件,按照 j5 的存储路径规范存储后 j5 就会按需加载,也就是说不用按上面 “常规用法” 中先定义、然后通过 script 标签引入,最后才能使用。参 jian:https://help.jtbc.cn/php/5.0/#structure/component.xml
1、自定义标签类的存储路径规范
1)j5 核心组件目录,一般存放 j5 官方组件。为了方便管理不建议自己编写的组件放到这个目录。\Public\common\assets\js\components\jtbc\
此目录下组件是按照文件夹分别存放。如图
如 jtbcAnchor 文件夹下 jtbcAnchor.js 现实了对 a 标签的扩展。注意命名都是以小驼峰式命名。如图
如何在中使用呢?只需在模板文件中直接写标签 <a is=“jtbc-anchor” href="网址"></a>(因为这是继承的基本 html 元素 a, 所以使用时先写基本标签 a , 再通过 is 属性指定自定义元素名), 元素名是把小驼峰式命名改为用 “-” 连接成的标签名。因为,custom element 的名称不能是单个单词,且其中必须要有短横线。
2)非 j5 官方的自定义组件可以放到 \Public\common\assets\js\components\ 自定义文件夹 \,比如 my 文件夹(这个名字可以自己取,名字全小写)。组件同样是以小驼峰命名的文件夹单独存放。组件的类文件名与其文件夹同名。如图
如何引用: <my-edit-field></my-edit-field> 同样是把小驼峰式命名改为用短横线连接,转为标签名。如图
3)模块下的自定义组件。存放到对应模块下,如新闻模块 \Public\news\common\assets\js\components\ 。
模块下自定义组件组织相对自由,因为不像上面两个组件比较多,需要有条理一些。
a、可以把包含组件的直接放到 components 文件夹下。引用方式 <web-news-scroll></web-news-scroll>
b、也可以像上面那样用不同的文件夹来分类存储组件的文件夹。引用方式 <web-news-detail-scroll></web-news-detail-scroll>、<web-news-list-scroll></<web-news-list-scroll>
如图
c、上面的规律就是
web 代表要去模块下面加载
news 代表需要搜索的模块名
下面代表的是组件名:这里的组件名可以不用小驼峰。用小驼峰的组件名,每一层需要一个文件夹。如上面的 detailScroll、ListScroll。如图
2、为什么 j5 的组件不用引入就能自动加载,这要看一段源码,当然以理解它也不会妨碍你很好的用 j5 ,编写自己的自定义组件。
j5 的自动加载的实现文件位于 /Public/common/assets/js/components/components.js 中。如图
看到第 44 行中的 customElements.define (tag, component.default, options); 了吗?是不是和一开始介绍的常规用法自定义组件的语法一样 ---
customElements.define(name, constructor, options);。
有了自动加载,我们就只需实现自定义组件的行为类,然后按照上面介绍的 3 种路径存放方式存放,自定义组件就可以按需加载了。它让我们专注于组件功能逻辑,省去了不必要的重复工作。
感谢阅读,希望这篇文章能给你带来帮助!