Lit Element实例自定义CSS Grid规则的最优实现方案咨询
基于LitElement的实例专属CSS Grid配置最优实现方案
核心思路为避免把完整grid-template规则堆在元素的style属性上,将网格配置作为结构化参数传入组件,由组件内部动态生成实例专属样式,既解决属性过长问题,也规避转义、校验难的缺陷。
方案1:使用Adopted StyleSheets生成实例专属样式(现代浏览器最优解)
现代浏览器支持的adoptedStyleSheets接口性能最优,无样式冲突风险,适合较新版本的LitElement使用:
import {LitElement, html} from 'lit-element'; class Timeline extends LitElement { static properties = { gridConfig: { type: Object } } // 公共静态样式提前定义,所有实例复用 static sharedStyles = new CSSStyleSheet(); static { Timeline.sharedStyles.replaceSync(` .timeline-year { draggable: true; /* 其余公共样式 */ } `) } updated(changedProps) { if (changedProps.has('gridConfig')) { // 内部拼接生成grid-template规则 const gridTemplate = this.#buildGridTemplate(this.gridConfig) // 生成实例专属样式表 const instanceSheet = new CSSStyleSheet() instanceSheet.replaceSync(` :host { display: grid; grid-template: ${gridTemplate}; } `) // 合并公共样式与实例专属样式 this.shadowRoot.adoptedStyleSheets = [ this.constructor.sharedStyles, instanceSheet ] } } // 私有方法:根据结构化配置拼接grid-template字符串 #buildGridTemplate(config) { const { rows, columns } = config const rowPart = rows.map(r => `[${r.lineName}] "${r.areaTemplate}" ${r.height}`).join(' ') const colPart = columns.map(c => `[${c.lineName}] ${c.width}`).join(' ') return `${rowPart} / ${colPart}` } render() { return html` <div class="timeline-year"> <!-- 组件内部内容 --> </div> ` } } customElements.define('timeline-year', Timeline);
组件使用时只需传入结构化配置,无需手写超长style:
<timeline-year .gridConfig=${{ rows: [ { lineName: 'years', areaTemplate: '"dummy-group-cell _y_2021---"'.repeat(12), height: 'var(--default-height, 40px)' }, { lineName: 'months', areaTemplate: '"dummy-group-cell _m_2021-09"'.repeat(5) + '"dummy-group-cell _m_2021-10"'.repeat(7), height: 'var(--default-height, 32px)' }, // 其余数据行、 footer行配置 ], columns: [ { lineName: 'group', width: 'auto' }, ...Array.from({length:12}, (_,i) => ({ lineName: `_${1635285600000 + i*86400000}`, width: 'var(--width)' })) ] }}></timeline-year>
方案2:兼容旧版本LitElement/旧浏览器的动态style标签方案
如果使用0.6.x这类旧版本LitElement,不支持adoptedStyleSheets,可直接在render函数中动态生成实例专属的style标签,配合ShadyCSS做兼容:
import {LitElement, html} from '@polymer/lit-element'; class Timeline extends LitElement { static properties = { gridConfig: { type: Object } }; firstUpdated(changedProperties) { super.firstUpdated(changedProperties); window.ShadyCSS.styleSubtree(this); } #buildGridTemplate(config) { // 同方案1的拼接逻辑 } render() { const gridTemplate = this.#buildGridTemplate(this.gridConfig) return html` <style> :host { display: grid; grid-template: ${gridTemplate}; } /* 其余公共样式 */ .timeline-year { draggable: true; } </style> <div class="timeline-year"> <!-- 组件内部内容 --> </div> `; } } customElements.define('timeline-year', Timeline);
每个实例的style标签是独立作用域,不会互相影响,兼容ShadyCSS的 polyfill 规则。
可选优化:拆分配置为基础属性
如果需要进一步简化组件使用成本,可将网格配置拆分为多个基础属性,使用者不需要了解Grid语法即可配置:
<timeline-year start-year="2021" start-month="9" month-count="12" data-row-count="5" row-heights="40px,32px,60px,60px,60px,60px,60px,1fr" ></timeline-year>
组件内部接收这些基础属性后自动拼接生成对应的grid-template规则即可。
内容的提问来源于stack exchange,提问作者Daniel van Mil
相关产品推荐
相关产品推荐

