可以通过 Hook Node.prototype.appendChild 来实现拦截并修改 CSS 中的 URL。以下是完整的实现方案:
// 保存原始 appendChild 方法
const originalAppendChild = Node.prototype.appendChild;
// Hook appendChild 方法
Node.prototype.appendChild = function(node) {
// 检查是否是 style 元素或包含样式表的内容
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.tagName === 'STYLE') {
// 如果是 style 元素,处理其文本内容
processStyleContent(node);
} else if (node.sheet && node.sheet.cssRules) {
// 如果是 link 元素或者已经有样式表的元素
processStyleSheet(node.sheet);
}
} else if (node.nodeType === Node.TEXT_NODE &&
this.nodeType === Node.ELEMENT_NODE &&
this.tagName === 'STYLE') {
// 如果是添加到 style 元素的文本节点
processStyleTextContent(node);
}
// 调用原始方法
return originalAppendChild.call(this, node);
};
// 处理 style 元素的文本内容
function processStyleContent(styleElement) {
if (styleElement.textContent) {
styleElement.textContent = replaceCSSUrls(styleElement.textContent);
}
// 监听后续的内容变化
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'characterData' ||
(mutation.type === 'childList' && mutation.addedNodes.length > 0)) {
styleElement.textContent = replaceCSSUrls(styleElement.textContent);
}
});
});
observer.observe(styleElement, {
characterData: true,
childList: true,
subtree: true
});
}
// 处理文本节点的内容(当文本被添加到 style 元素时)
function processStyleTextContent(textNode) {
textNode.textContent = replaceCSSUrls(textNode.textContent);
}
// 处理现有的样式表
function processStyleSheet(sheet) {
try {
const rules = sheet.cssRules || sheet.rules;
for (let i = 0; i < rules.length; i++) {
const rule = rules[i];
if (rule.style && rule.style.cssText) {
rule.style.cssText = replaceCSSUrls(rule.style.cssText);
}
// 递归处理媒体查询等嵌套规则
if (rule.cssRules) {
processStyleSheet(rule);
}
}
} catch (e) {
// 可能会因为跨域限制而无法访问某些规则
console.warn('无法访问样式表规则:', e);
}
}
// CSS URL 替换函数 - 这是核心的 URL修改逻辑
function replaceCSSUrls(cssText) {
return cssText.replace(/url\s*\(\s*['"]?(.*?)['"]?\s*\)/gi, (match, url) => {
// 在这里修改 URL,例如添加前缀、域名替换等
console.log('捕获到 CSS URL:', url);
let newUrl=url;
/* 示例修改:
1. 添加 CDN 前缀*/
newUrl='https://cdn.example.com'+url;
/*2. 替换域名*/
newUrl=url.replace('old-domain.com','new-domain.com');
/*3. 添加时间戳参数(避免缓存)*/
newUrl=url+(url.includes('?')?'&':'?')+'t='+Date.now();
return `url("${newUrl}")`;
});
}
// 使用示例:创建并测试 Hook
const style=document.createElement('style');
style.textContent=`
.bg-image{
background-image: url('/images/bg.jpg');
}
.icon{
background-image: url("data:image/svg+xml,...");
}
@font-face{
font-family:'CustomFont';
src: url('/fonts/font.woff2') format('woff2');
}
`;
document.head.appendChild(style); // 这会触发我们的 Hook
console.log('Hook已安装,开始监控 CSS URL');
这个 Hook 的主要特性:
全面覆盖:拦截所有
appendChild调用,检测是否为样式相关操作多层处理:
- Style元素的文本内容
- Link元素引入的样式表
- Text节点直接添加到 Style元素
- Style元素的文本内容
动态监控:使用 MutationObserver监听 Style元素内容的后续变化
安全处理:对可能因跨域限制无法访问的规则进行错误处理
灵活替换:在
replaceCSSUrls函数中可以实现各种URL重写逻辑
关键点说明:
- 正则表达式
/url\s*\(\s*['"]?(.*?)['"]?\s*\)/gi用于匹配 CSS中的各种URL写法
- 替换策略可以根据需求定制(CDN化、域名切换、缓存破坏等)
- data URL也会被匹配但通常不需要修改,可以在替换函数中添加过滤逻辑
可以根据具体需求调整 replaceCSSUrls函数中的URL转换逻辑。
内容由零声教学AI助手提供,问题来源于学员提问




