经过漫长的开发实践,Gem 终于开始迈入了 v2,Gem 以让用户简单的方式编写自定义元素为宗旨进行了此次迭代。下面将介绍 v2 的一些重大更新。
v2 使用 ES 装饰器代替了以前的 TS 装饰器,并且将 GemElement.constructor 的参数用装饰器代替:
@customElement('my-element')
+@aria({ focusable: true, role: 'button' })
+@shadow()
+@async()
class MyElement extends GemElement {
- constructor() {
- super({ focusable: true, isAsync: true, isLight: false });
- this.internals.role = 'button';
- }
}使用装饰器具有更好的可扩展性,另外也降低了代码复杂度。基于同样的目的,还添加了 @effect @memo 等装饰器让你编写更简洁的自定义元素:
@customElement('my-element')
class MyElement extends GemElement {
@attribute name: string;
#content: string;
@memo((myElement) => [myElement.name])
#calcContent = () => {
this.#content = this.name;
}
@effect((myElement) => [myElement.name])
#fetchData = () => {
// request
}
}WARNING
未来 Gem 可能会弃用生命周期回调函数,全面使用装饰器代替
v1 使用特定的字段 state 来表示元素内部状态,并使用 this.setState 来更新状态,在 v2 中,可以使用任意字段,因为定义状态的同时定义了更新方法:
@customElement('my-element')
class MyElement extends GemElement {
#state = createState({ a: true });
render = () => {
this.#state({ a: false });
console.log(this.#state.a);
}
}类似 createState,用 createRef 来代替 v1 的 @refobject:
@customElement('my-element')
class MyElement extends GemElement {
#input = createRef();
render = () => {
return html`<input ${this.#input} />`;
}
}Gem 使用 Shadow DOM 的一个理由是样式隔离性,他让用户可以直接编写“模块化”的 CSS,但是使用 Shadow DOM 编写 WebApp 也有一些缺点:
- 不能使用 SVG 符号
- URL Hash 无效
document.activeElement 无效
- 不方便集成 React/Vue 组件
- 性能较差
如果不是写需要高度封装的自定义元素(例如 UI 库),使用 Light DOM 是更合适的选择。现在,CSS 规范带来了 @scope,所以 Gem 充分利用 @scope 并默认使用 Light DOM,并且同样具备“模块化”(v1 不支持 Light DOM 样式“模块化”),下面的例子中,div 选择器将只应用在 <my-element> 的内容上:
const styles = css`
:scope {
display: block;
}
div {
color: red;
}
`;
@customElement('my-element')
@adoptedStyle(styles)
class MyElement extends GemElement {}NOTE
就像开头的例子,如果想要使用 Shadow DOM,需要添加 @shadow,并将 :scope 替换成 :host。
v1 只支持全局主题,v2 支持范围主题,并且支持主题覆盖:
// 全局主题将自动添加到 `document`
const theme = createTheme({ textColor: '#eee' });
const scopedTheme = createScopedTheme({ scopeTextColor: '#333' });
const overrideTheme = createOverrideTheme(theme, { textColor: '#eff' })
const styles = css`
:scope {
color: ${theme.textColor};
background: ${scopedTheme.scopeTextColor};
}
`;
@customElement('my-element')
@adoptedStyle(styles)
@adoptedStyle(scopedTheme)
@adoptedStyle(overrideTheme)
class MyElement extends GemElement {}此外,得益于相对颜色语法,主题中以 Color 结尾的颜色直接支持使用“重量”(类似字重)调节亮度:theme.textColor500,这是一个比原 textColor 稍亮的颜色。
希望 Gem 能以卓越的设计成为创建自定义元素的首选方案,如果你有任何建议和想法,请创建 Issue。