Skip to content

Commit

Permalink
[gem] Improve light dom apply style performance
Browse files Browse the repository at this point in the history
  • Loading branch information
mantou132 committed Jul 15, 2024
1 parent dc03a41 commit 2caea68
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 45 deletions.
35 changes: 20 additions & 15 deletions packages/gem-examples/src/theme/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GemElement, html, render, customElement, connectStore } from '@mantou/gem';
import { GemElement, html, render, customElement, connectStore, createCSSSheet, css, adoptedStyle } from '@mantou/gem';
import { createTheme, getThemeStore, updateTheme } from '@mantou/gem/helper/theme';
import { mediaQuery } from '@mantou/gem/helper/mediaquery';

Expand All @@ -25,24 +25,29 @@ document.onclick = () => {
});
};

const style = createCSSSheet(css`
div {
color: rgba(${theme.color}, 0.5);
border: 2px solid ${theme.primaryColor};
}
`);

const style1 = createCSSSheet(
css`
div {
border: 2px solid ${printTheme.primaryColor};
}
`,
mediaQuery.PRINT,
);

@customElement('app-root')
@connectStore(themeStore)
@adoptedStyle(style1)
@adoptedStyle(style)
export class App extends GemElement {
render() {
return html`
<style>
div {
color: rgba(${theme.color}, 0.5);
border: 2px solid ${theme.primaryColor};
}
@media ${mediaQuery.PRINT} {
div {
border: 2px solid ${printTheme.primaryColor};
}
}
</style>
<div>color: ${themeStore.primaryColor}</div>
`;
return html`<div>color: ${themeStore.primaryColor}</div>`;
}
}

Expand Down
6 changes: 3 additions & 3 deletions packages/gem/src/helper/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const setThemeFnMap = new WeakMap();
*/
export function createTheme<T extends Record<string, unknown>>(themeObj: T) {
const salt = randomStr();
const style = document.createElement('style');
const style = new CSSStyleSheet();
const store = createStore<T>(themeObj);
const theme: Record<string, string> = {};
const props: Record<string, string> = {};
Expand All @@ -48,10 +48,10 @@ export function createTheme<T extends Record<string, unknown>>(themeObj: T) {
setTheme();
const getStyle = () =>
`:root, :host {${Object.keys(store).reduce((prev, key) => prev + `${props[key]}:${store[key]};`, '')}}`;
const replace = () => (style.textContent = getStyle());
const replace = () => style.replaceSync(getStyle());
connect(store, replace);
replace();
(document.head || document.documentElement).append(style);
document.adoptedStyleSheets.push(style);
return theme as SomeType<T>;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/gem/src/lib/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ function defineEmitter(
}

/**
* 分配一个构造的样式表,如果元素是 lightDOM,则将样式表挂载到 RootNode 上
* 分配一个构造的样式表,前面的优先级越高(因为装饰器是越近越早执行)
*
* For example
* ```ts
Expand Down
54 changes: 39 additions & 15 deletions packages/gem/src/lib/element.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
import { html, render, TemplateResult } from 'lit-html';

import { connect, Store } from './store';
import {
LinkedList,
addMicrotask,
Sheet,
SheetToken,
isArrayChange,
GemError,
removeItems,
addListener,
} from './utils';
import { LinkedList, addMicrotask, Sheet, SheetToken, isArrayChange, GemError, addListener } from './utils';

export { html, svg, render, directive, TemplateResult, SVGTemplateResult } from 'lit-html';

Expand Down Expand Up @@ -47,6 +38,43 @@ const tick = (timeStamp = performance.now()) => {
};
noBlockingTaskList.addEventListener('start', () => addMicrotask(tick));

const rootStyleSheetInfo = new WeakMap<Document | ShadowRoot, Map<CSSStyleSheet, number>>();
const rootUpdateFnMap = new WeakMap<Map<CSSStyleSheet, number>, () => void>();

const updateStyleSheets = (map: Map<CSSStyleSheet, number>, sheets: CSSStyleSheet[], value: number) => {
let needUpdate = false;
sheets.forEach((e) => {
const count = map.get(e) || 0;
needUpdate ||= (value === 1 && count === 0) || (value === -1 && count === 1);
// count 为 0 时不立即删除,以便更新时识别是否外部样式
// 但是只要挂载 document 的样式表就不会被 GC 了, 是否要在闲时 GC?
map.set(e, count + value);
});
if (needUpdate) {
// 避免重复更新,例如 list 元素增删,全 light dom 时 document 更新
addMicrotask(rootUpdateFnMap.get(map)!);
}
};

export function appleCSSStyleSheet(ele: HTMLElement, sheets: CSSStyleSheet[]) {
const root = ele.getRootNode() as ShadowRoot | Document;
if (!rootStyleSheetInfo.has(root)) {
const map = new Map<CSSStyleSheet, number>();
rootStyleSheetInfo.set(root, map);
rootUpdateFnMap.set(map, () => {
// 先找到外部样式表
const newSheets = root.adoptedStyleSheets.filter((e) => !map.has(e));
map.forEach((count, sheet) => count && newSheets.push(sheet));
root.adoptedStyleSheets = newSheets;
});
}

const map = rootStyleSheetInfo.get(root)!;

updateStyleSheets(map, sheets, 1);
return () => updateStyleSheets(map, sheets, -1);
}

type GetDepFun<T> = () => T;
type EffectCallback<T> = (depValues: T, oldDepValues?: T) => any;
type EffectItem<T> = {
Expand Down Expand Up @@ -147,11 +175,7 @@ export abstract class GemElement<State = Record<string, unknown>> extends HTMLEl
() => {
// 阻止其他元素应用样式到当前元素
this.dataset.styleScope = '';
const root = this.getRootNode() as ShadowRoot | Document;
root.adoptedStyleSheets.push(...sheets);
return () => {
root.adoptedStyleSheets = removeItems(root.adoptedStyleSheets, sheets);
};
return appleCSSStyleSheet(this, sheets);
},
() => [],
);
Expand Down
11 changes: 0 additions & 11 deletions packages/gem/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,17 +348,6 @@ export function isArrayChange(newValues: any[], oldValues: any[]) {
return false;
}

export function removeItems(target: any[], items: any[]) {
const set = new Set(items);
return target.filter((e) => {
if (set.has(e)) {
set.delete(e);
return false;
}
return true;
});
}

export function objectMapToString<T = any>(
object: Record<string, T>,
separate: string,
Expand Down

0 comments on commit 2caea68

Please sign in to comment.