v2 介绍

经过漫长的开发实践,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 可能会弃用生命周期回调函数,全面使用装饰器代替

内部状态和 DOM 引用

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} />`; } }

默认使用 Light DOM

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

希望 Gem 能以卓越的设计成为创建自定义元素的首选方案,如果你有任何建议和想法,请创建 Issue