Skip to content

Commit

Permalink
perf: shadow dom css cache
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Sep 4, 2024
1 parent c05d95d commit c1e3a02
Showing 1 changed file with 81 additions and 23 deletions.
104 changes: 81 additions & 23 deletions src/renderer/src/components/common/ShadowDOM.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,25 @@ import root from "react-shadow"

const ShadowDOMContext = createContext(false)

const MemoedDangerousHTMLStyle: FC<{ children: string }> = memo(
({ children }) => (
<style
dangerouslySetInnerHTML={useMemo(
() => ({
__html: children,
}),
[children],
)}
/>
),
)
const MemoedDangerousHTMLStyle: FC<
{
children: string
} & React.DetailedHTMLProps<
React.StyleHTMLAttributes<HTMLStyleElement>,
HTMLStyleElement
> &
Record<string, unknown>
> = memo(({ children, ...rest }) => (
<style
{...rest}
dangerouslySetInnerHTML={useMemo(
() => ({
__html: children,
}),
[children],
)}
/>
))

const weakMapElementKey = new WeakMap<
HTMLStyleElement | HTMLLinkElement,
Expand All @@ -51,6 +58,24 @@ const cloneStylesElement = (_mutationRecord?: MutationRecord) => {
children: $style.innerHTML,
}),
)

const styles = getLinkedStaticStyleSheets()

for (const style of styles) {
let key = weakMapElementKey.get(style.ref)
if (!key) {
key = nanoid(8)
weakMapElementKey.set(style.ref, key)
}

reactNodes.push(
createElement(MemoedDangerousHTMLStyle, {
key,
children: style.cssText,
["data-href"]: style.ref.href,
}),
)
}
}

return reactNodes
Expand Down Expand Up @@ -82,20 +107,21 @@ export const ShadowDOM: FC<PropsWithChildren<React.HTMLProps<HTMLElement>>> & {
const dark = useIsDark()

const uiFont = useUISettingKey("uiFontFamily")
const staticStyleSheet = useState(getLinkedStaticStyleSheet)[0]

return (
<root.div {...rest}>
<ShadowDOMContext.Provider value={true}>
<div
style={useMemo(() => ({
fontFamily: uiFont,
}), [uiFont])}
style={useMemo(
() => ({
fontFamily: uiFont,
}),
[uiFont],
)}
id="shadow-html"
data-theme={dark ? "dark" : "light"}
className="font-theme"
>
<MemoedDangerousHTMLStyle>{staticStyleSheet}</MemoedDangerousHTMLStyle>
{stylesElements}
{props.children}
</div>
Expand All @@ -106,15 +132,47 @@ export const ShadowDOM: FC<PropsWithChildren<React.HTMLProps<HTMLElement>>> & {

ShadowDOM.useIsShadowDOM = () => useContext(ShadowDOMContext)

function getLinkedStaticStyleSheet() {
let cssText = ""
const cacheCssTextMap = {} as Record<string, string>

function getLinkedStaticStyleSheets() {
const $links = document.head
.querySelectorAll("link[rel=stylesheet]")
.values() as unknown as HTMLLinkElement[]

const styleSheetMap = new WeakMap<
Element | ProcessingInstruction,
CSSStyleSheet
>()

const cssArray = [] as { cssText: string, ref: HTMLLinkElement }[]

for (const sheet of document.styleSheets) {
if (!sheet.href) continue
const rules = sheet.cssRules || sheet.rules
for (const rule of rules) {
cssText += `${rule.cssText}\n`
if (!sheet.ownerNode) continue
styleSheetMap.set(sheet.ownerNode, sheet)
}

for (const $link of $links) {
const sheet = styleSheetMap.get($link)
if (!sheet) continue
if (!sheet.href) continue
const hasCache = cacheCssTextMap[sheet.href]
if (!hasCache) {
if (!sheet.href) continue
const rules = sheet.cssRules || sheet.rules
let cssText = ""
for (const rule of rules) {
cssText += rule.cssText
}

cacheCssTextMap[sheet.href] = cssText
}

cssArray.push({
cssText: cacheCssTextMap[sheet.href],
ref: $link,
})
}
return cssText

return cssArray
}

0 comments on commit c1e3a02

Please sign in to comment.