所以我写了这个vue钩子,以编程方式注入全局css
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)
})
}
它可以工作,但即使组件已卸载,它也会将样式标记保留在头上。解决这个问题对我来说是困难的部分。我认为这需要跟踪依赖于这些特定cssStyles
的所有组件实例,但这难道不会阻止垃圾收集器处理它们吗?
一个解决方案是一个计数器,用于跟踪特定风格的消费者数量:
- 当使用此挂钩的组件安装时:
- 将样式添加到
<head>
- 递增计数器
- 将样式添加到
- 当该组件卸载时:
- 递减计数器
- 如果结果计数为零,则从
<head>
中删除样式
请注意,此解决方案不需要跟踪组件实例,因此此处不存在内存泄漏的可能性。
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
}
})
}
作为内存优化,考虑在内存中存储样式的散列,而不是样式字符串本身:
import { onBeforeMount, onUnmounted } from 'vue'
// https://stackoverflow.com/a/7616484/6277151
const hashCode = (str: string) => {
let hash = 0
if (str.length === 0) return hash
for (let i = 0; i < str.length; i++) {
let chr = str.charCodeAt(i)
hash = ((hash << 5) - hash) + chr
hash |= 0 // Convert to 32bit integer
}
return hash
}
// map of injected CSS style hashes and their number of consumers
const injectedStyles = {} as Record<string, number>
function useGlobalStyles(cssStyles: string) {
let styleEl: HTMLStyleElement
const styleHash = hashCode(cssStyles)
onBeforeMount(() => {
if (injectedStyles[styleHash] === undefined) {
injectedStyles[styleHash] = 1
} else {
injectedStyles[styleHash]++
// style already injected, no need to do anything
return
}
styleEl = document.createElement('style')
styleEl.textContent = cssStyles
document.head.appendChild(styleEl)
})
onUnmounted(() => {
injectedStyles[styleHash]--
if (injectedStyles[styleHash] <= 0) {
delete injectedStyles[styleHash]
styleEl?.remove()
styleEl = undefined
}
})
}