添加 css <head>时如何避免内存泄漏



所以我写了这个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的所有组件实例,但这难道不会阻止垃圾收集器处理它们吗?

一个解决方案是一个计数器,用于跟踪特定风格的消费者数量:

  1. 当使用此挂钩的组件安装时:
    • 将样式添加到<head>
    • 递增计数器
  2. 当该组件卸载时:
    • 递减计数器
    • 如果结果计数为零,则从<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
}
})
}

最新更新