借助自定义元素,您可以构建自己的 HTML 标记。本核对清单涵盖一些最佳做法,可帮助您制作优质元素。
通过自定义元素,您可以扩展 HTML 并定义自己的代码。它们是
功能极其强大,但它们的级别也很低,也就是说,
始终清楚如何以最佳方式实现您自己的元素。
为帮助您尽可能创造最佳的体验,我们在下面汇总了
核对清单。它分解了我们认为成为在线旅游平台所需要具备的所有条件
行为良好的自定义元素。
核对清单
阴影 DOM
创建影子根来封装样式。
为什么?
在元素的影子根中封装样式可确保它可以正常运行
无论在何处使用它如果开发者需要
希望将您的元素放在另一个元素的影子根中。这个
甚至适用于复选框或单选按钮等简单元素。它的
影子根中的唯一内容就是样式
。
示例
<howto-checkbox>
元素。
在构造函数中创建影子根。
为什么?
当您对元素拥有专有知识 时,才属于构造函数。
设置您不想要其他实现细节的理想时机
混乱的元素在稍后的回调中执行此操作,例如
connectedCallback
,这意味着您需要防止
元素分离并重新附加到文档的情况。
示例
<howto-checkbox>
元素。
将元素创建的所有子项放入其影子根中。
为什么?
由您的元素创建的子元素是其实现的一部分,应
私密。如果不保护影子根,在 JavaScript 外部
干扰这些孩子。
示例
<howto-tabs>
元素。
使用 <slot>将 light DOM 子项投影到 shadow DOM 中
为什么?
允许组件用户将您的组件内容指定为 HTML 子项,使组件的可组合性更高。如果浏览器不支持自定义元素,嵌套内容将保持可用、可见和访问。
示例
<howto-tabs>
元素。
设置 :host
显示样式(例如 block
、
inline-block
、flex
),除非您偏好使用默认值
inline
。
为什么?
默认情况下,自定义元素为 display: inline
,因此设置其
width
或 height
将不会产生任何影响。经常这样
并可能会给开发者带来
设置网页布局。除非您更喜欢 inline
显示屏,否则
应始终设置默认的 display
值。
示例
<howto-checkbox>
元素。
添加遵循隐藏属性的 :host
显示样式。
为什么?
采用默认 display
样式的自定义元素,例如
:host { display: block }
,将替换特异性较低的规则
内置
<ph type="x-smartling-placeholder"></ph>
hidden
属性 。
如果您希望将 hidden
设置为
属性,以 display: none
呈现该元素。此外
默认为 display
样式,添加对 hidden
的支持
与:host([hidden]) { display: none }
共享。
示例
<howto-checkbox>
元素。
特性和属性
请勿覆盖作者设置的全局属性。
为什么?
全局属性是指出现在所有 HTML 元素上的属性。部分
示例包括 tabindex
和 role
。自定义元素
可能希望将其初始 tabindex
设置为 0,使其成为键盘按键
可聚焦但您应该先检查开发者是否使用
您的元素将此设为其他值。例如,如果他们设置了
tabindex
转换为 -1,即表示不希望出现
具有互动性
示例
<howto-checkbox>
元素。有关详情,请参阅
不覆盖网页作者。
始终接受原始数据(字符串、数字、布尔值)作为任一属性
或属性。
为什么?
自定义元素(如其内置元素)应可配置。
配置可通过声明、属性或命令方式传入
通过 JavaScript 属性进行设置理想情况下,每个属性也应关联到
相应的属性。
示例
<howto-checkbox>
元素。
旨在使原始数据属性与属性保持同步,
属性,反之亦然。
为什么?
您永远不知道用户会如何与元素互动。他们可能
在 JavaScript 中设置属性,然后预期会读取该值
使用 getAttribute()
等 API。如果每个属性都有
两者都反映
使用您的元素。也就是说,调用
setAttribute('foo', value)
还应设置相应的
foo
属性,反之亦然。当然,
此规则。您不应反映高频属性,例如
currentTime
。请自行判断。如果
用户似乎将与某个属性或属性互动,并且
这样反思并不麻烦
示例
<howto-checkbox>
元素。有关详情,请参阅
避免再次进入问题 。
力求仅接受丰富的数据(对象、数组)作为属性。
为什么?
一般来说,没有内置 HTML 元素示例
可接受丰富数据(普通 JavaScript 对象和数组),
属性。而是通过方法调用或
属性。将丰富的数据作为
属性:将大型对象序列化为字符串的成本很高;
在此字符串化过程中,所有对象引用都将丢失。对于
例如,如果您将一个引用了另一个对象的对象字符串化,
也可能是 DOM 节点,这些引用都会丢失。
不向属性反映丰富的数据属性。
为什么?
将丰富的数据属性反映到属性不必要地代价高昂,
需要对相同的 JavaScript 对象进行序列化和反序列化。除非
您有一个应用场景只能通过这项功能解决,
请尽量避免
建议检查是否在元素之前设置的属性
已升级。
为什么?
使用该元素的开发者可能会尝试为该元素设置属性
在其定义加载完毕之前如果将
开发者使用的框架来处理组件加载
并将其属性绑定到模型。
示例
<howto-checkbox>
元素。详细说明
使属性延迟 。
请勿自行应用课程。
为什么?
需要表达状态的元素应使用属性来表达其状态。通过
class
属性通常被视为
开发者使用您的元素,而您自己写入元素可能会无意中
深入探索开发者课程
事件
分派事件来响应内部组件活动。
为什么?
您的组件可能包含一些属性,它们会随着
只有您的组件知道,例如,计时器或动画
或资源完成加载。这有助于分派事件
以便通知主机组件状态
与众不同。
不分派事件来响应主机设置属性(向下
数据流)。
为什么?
为响应主机设置属性而分派事件是多余的
(主机知道当前状态,因为它只是进行了设置)。调度事件
对主机设置属性做出响应,可能会导致数据出现无限循环
绑定系统
示例
<howto-checkbox>
元素。
释疑类视频
不覆盖网页作者
使用该元素的开发者可能会想要覆盖某些
初始状态例如,使用以下代码更改其 ARIA role
或可聚焦性
tabindex
。检查是否设置了这些以及任何其他全局属性,
然后再应用您自己的值
connectedCallback () {
if ( ! this . hasAttribute ( 'role '))
this . setAttribute ( 'role ', 'checkbox ');
if ( ! this . hasAttribute ( 'tabindex '))
this . setAttribute ( 'tabindex ', 0 );
使属性延迟
开发者可能会尝试在元素的
定义已加载。如果开发者使用
该框架负责处理加载组件、将组件插入页面以及
将其属性绑定到模型。
在以下示例中,Angular 以声明方式绑定其模型的
isChecked
属性设置为复选框的 checked
属性。如果
Howto-checkbox 被延迟加载,Angular 可能会尝试设置
在元素升级前检查选中的属性。
<howto-checkbox [checked]="defaults.isChecked"></howto-checkbox>
自定义元素应通过检查是否有任何属性具有
都已在其实例上进行了设置<howto-checkbox>
使用名为 _upgradeProperty()
的方法演示此模式。
connectedCallback () {
...
this . _upgradeProperty ( 'checked ');
}
_upgradeProperty ( prop ) {
if ( this . hasOwnProperty ( prop )) {
let value = this [ prop ];
delete this [ prop ];
this [ prop ] = value ;
}
}
_upgradeProperty()
会捕获未升级实例的值并删除
属性,这样它就不会覆盖自定义元素自身的属性 setter。
这样,当该元素的定义最终实际加载时,它可以立即
以反映正确的状态
避免重复问题
很容易使用 attributeChangedCallback()
将状态反映到
例如:
// When the [checked] attribute changes, set the checked property to match.
attributeChangedCallback ( name , oldValue , newValue ) {
if ( name === 'checked ')
this . checked = newValue ;
}
但是,如果属性 setter 也会反映为
属性。
set checked ( value ) {
const isChecked = Boolean ( value );
if ( isChecked )
// OOPS! This will cause an infinite loop because it triggers the
// attributeChangedCallback() which then sets this property again.
this . setAttribute ( 'checked ', '');
else
this . removeAttribute ( 'checked ');
}
另一种方法是让属性 setter 反映到该属性,并且
让 getter 根据该属性确定其值。
set checked ( value ) {
const isChecked = Boolean ( value );
if ( isChecked )
this . setAttribute ( 'checked ', '');
else
this . removeAttribute ( 'checked ');
}
get checked () {
return this . hasAttribute ( 'checked ');
}
在此示例中,添加或移除属性也会设置该属性。
最后,attributeChangedCallback()
可用于处理附带效应
例如应用 ARIA 状态
attributeChangedCallback ( name , oldValue , newValue ) {
const hasValue = newValue !== null ;
switch ( name ) {
case 'checked ':
// Note the attributeChangedCallback is only handling the *side effects*
// of setting the attribute.
this . setAttribute ( 'aria - checked ', hasValue );
break ;
...
}
}