So I wrote this vue hook to inject global css programmatically
const injectedStyles = new Set<string>()
function useGlobalStyles(cssStyles: string) {
let styleEl: HTMLStyleElement
onBeforeMount(() => {
if (injectedStyles.has(cssStyles)) {
return
} else {
injectedStyles.add(cssStyles)
}
styleEl = document.createElement('style')
styleEl.textContent = cssStyles
document.head.appendChild(styleEl)
})
}
It works, but it will keep style tag in head even if component is unmounted. Fixing this is the difficult part for me. I think it would required keeping track of all component instances that depend on these particular cssStyles
, but then wouldn't it prevent garbage collector from disposing of them?
CodePudding user response:
One solution is a counter that tracks the number of consumers of a particular style:
- When a component using this hook mounts:
- add the style to
<head>
- increment the counter
- add the style to
- When that component unmounts:
- decrement the counter
- if the resulting count is zero, remove the style from
<head>
Note this solution doesn't require tracking component instances, so there's no possibility of a memory leak here.
import { onBeforeMount, onUnmounted } from 'vue'
// map of injected CSS styles and their number of consumers
const injectedStyles = {} as Record<string, number>
function useGlobalStyles(cssStyles: string) {
let styleEl: HTMLStyleElement
onBeforeMount(() => {
if (injectedStyles[cssStyles] === undefined) {
injectedStyles[cssStyles] = 1
} else {
injectedStyles[cssStyles]
// style already injected, no need to do anything
return
}
styleEl = document.createElement('style')
styleEl.textContent = cssStyles
document.head.appendChild(styleEl)
})
onUnmounted(() => {
injectedStyles[cssStyles]--
if (injectedStyles[cssStyles] <= 0) {
delete injectedStyles[cssStyles]
styleEl?.remove()
styleEl = undefined
}
})
}