LitElement:在小型 Web 组件中创建许多自定义样式规则与几个动态规则时的最佳实践(或最佳性能)?

LitElement: best practise (or best performance) when creating many custom style rules vs a couple of dynamic rules inside a tiny web component?

所以我正在开发一个非常小的 Web 组件,以作为更大的设计系统的一部分。

我对网络组件的使用有点陌生,但我知道这个特定的网络组件可以在一个布局中使用很多很多次。

此 Web 组件控制在传递给它的任何子组件周围放置多少垂直 space。

组件的剖析非常简单:

import { LitElement, html, css, unsafeCSS, property, customElement } from 'lit-element';

export const spacingAmounts = {
  'x-small': css`4px`,
  'small': css`8px`,
  'medium': css`12px`,
  'large': css`16px`,
  'x-large': css`20px`,
  '2x-large': css`30px`,
  '3x-large': css`40px`,
  '4x-large': css`60px`,
  '5x-large': css`90px`,
  '6x-large': css`120px`,
};

const createSpacingStyleRules = (direction: 'top' | 'bottom') => {
  return Object.keys(spacingAmounts).map(s => {
    const amount = spacingAmounts[s];
    return css`
      :host([${unsafeCSS(direction)}="${unsafeCSS(s)}"]) {
        margin-${unsafeCSS(direction)}: ${unsafeCSS(amount)};
      }
    `;
  });
};

@customElement('gu-vertical-space')
export class GuVerticalSpace extends LitElement {
  @property() top: string;
  @property() bottom: string;

  static get styles() {
    const styles = [
      css`
        :host {
          display: block;
        }
      `,
      // ----------------------------------------------------------
      // @TODO:
      // test if it's better performance wise to either:
      //
      // 1 -  generate a verbose list of static styles for
      //      each instance of gu-vertical-space
      //  or
      // 2 -  generate a tiny amount of styles on the fly, 
      //      based on property inputs...
      // ----------------------------------------------------------
      // ...createSpacingStyleRules('top'),
      // ...createSpacingStyleRules('bottom'),
    ];
    return styles;
  }

  render() {
    const styles = [];
    if (this.top) {
      styles.push(css`
        :host([top="${unsafeCSS(this.top)}"]) {
          margin-top: ${spacingAmounts[this.top]}
        }
      `);
    }
    if (this.bottom) {
      styles.push(css`
        :host([bottom="${unsafeCSS(this.bottom)}"]) {
          margin-bottom: ${spacingAmounts[this.bottom]}
        }
      `);
    }
    return html`
      <style>${styles}</style>
      <slot></slot>
    `;
  }
}


这里有 2 种方法可以将预定义的边距量映射到 Web 组件主机的顶部或底部。

当前有效的方法只是在渲染函数中动态生成一个 <style> 块,其中包含由 "top" 或 "bottom" 输入属性决定的任何边距量。

我正在考虑的另一种方法(尽管在 atm 中被注释掉了)是静态生成大量样式规则列表,从而避免在 render() 函数中生成任何动态样式内容的需要,这可能是一种反模式 - 如果我正确理解 lit-element docs

也许我缺少一种更优雅的方法?我倾向于当前的方法,只是因为它感觉更容易理解 - 但很好奇其他人的想法!

正如 Alan 所建议的,解决这个问题的一个更简单的方法是使用 css-变量。

基本上,只需将输入保证金量映射为 connect() 生命周期事件中的 css 变量(或者渲染,如果您认为道具在初始渲染后会发生变化)- 并调用这是一天!

static get styles() {
  return css`
    :host {
      display: block;
      margin-top: var(--margin-top);
      margin-bottom: var(--margin-bottom);
    }
  `;
}

connectedCallback() {
  super.connectedCallback();
  this.style.setProperty(
    '--margin-top',
    this.top
      ? spacingAmounts[this.top]
      : 'unset'
  );
  this.style.setProperty(
    '--margin-bottom',
    this.bottom
      ? spacingAmounts[this.bottom]
      : 'unset'
  );
}