直接用正则替换html路径会出错,因为正则无法区分src/href在注释、js字符串或style标签中的出现,易误改;ast方案(如parse5或cheerio)能精准定位dom属性节点,只修改真实html属性值,跳过文本和注释内容。

为什么直接用正则替换 HTML 路径会出错
因为 src、href、data-src 等属性可能出现在注释、JS 字符串、style 标签里,正则无法区分上下文,一替就炸。比如 <!-- <img src="old.jpg"> --> 或 console.log('src="old.jpg"') 也会被误改。
AST 方案能准确识别 HTML 元素节点和属性位置,只改真实 DOM 属性值,跳过文本内容和注释。但 Node.js 原生不支持 HTML AST,得靠第三方库。
推荐用 parse5(轻量、标准兼容好)或 cheerio(jQuery 风格、API 简单),别用已停止维护的 htmlparser2 + domhandler 组合。
如果你了解HTML,CSS和JavaScript,您已经拥有所需的工具开发Android应用程序。本动手本书展示了如何使用这些开源web标准设计和建造,可适应任何Android设备的应用程序 - 无需使用Java。您将学习如何创建一个在您选择的平台的Android友好的网络应用程序,然后转换与自由PhoneGap框架到一个原生的Android应用程序。了解为什么设备无关的移动应用是未来的潮流,并开始构建应用程序,提供更
用 parse5 修改 src 和 href 属性值
parse5 输出的是标准 DOM-like 树,修改后需用 parse5.serialize() 写回字符串。它不自动处理自闭合标签(如 <img>),但序列化时会按输入格式还原,这点比 cheerio 更可控。
- 安装:
npm install parse5 - 只改
<img src>、<script src></script>、<link href>、<a href></a>,忽略data-src等自定义属性(除非你明确要处理) - 路径替换逻辑必须判断是否为相对路径:用
path.isRelative()或简单检查是否以/、./、../开头,避免把https://cdn.com/xxx也改了 - 示例片段:
const { parse, serialize } = require('parse5'); const document = parse(htmlString); // 递归遍历所有元素节点 function walk(node) { if (node.tagName && node.attrs) { for (const attr of node.attrs) { if (attr.name === 'src' || attr.name === 'href') { if (attr.value.startsWith('./') || attr.value.startsWith('../')) { attr.value = attr.value.replace(/^\.\/|^\.\.\//, 'new-base/'); } } } } if (node.childNodes) node.childNodes.forEach(walk); } walk(document); const newHtml = serialize(document);
用 cheerio 处理多文件批量任务更顺手
如果你要一次改几十个 HTML 文件,且习惯 jQuery 式写法,cheerio 的 load() + find() + attr() 更直观,尤其适合带条件筛选的场景(比如只改某个 <div class="content"> 下的图片)。<ul>
<li>安装:<code>npm install cheerio
<svg xmlns="http://www.w3.org/2000/svg"></svg>,需加 { xmlMode: false } 参数,否则 img 可能匹配不到fs.promises + Promise.all(),别用同步 API,否则几百个文件会卡死path.join() 或 path.posix.join()(跨平台安全)const cheerio = require('cheerio');
const $ = cheerio.load(html, { xmlMode: false });
$('img[src], script[src], link[href]').each((_, el) => {
const $el = $(el);
const attr = $el.is('img, script') ? 'src' : 'href';
const oldPath = $el.attr(attr);
if (oldPath && !oldPath.startsWith('http') && !oldPath.startsWith('/')) {
$el.attr(attr, path.posix.join('assets', oldPath));
}
});
路径重写时最容易漏掉的三个地方
很多脚本跑完发现部分资源还是 404,问题往往不在 AST 解析本身,而在路径语义理解偏差:
-
base标签影响所有相对路径:如果 HTML 有<base href="/static/">,那img/src="a.jpg"实际指向/static/a.jpg,你的脚本必须先提取并应用这个 base 值,不能只看属性字面值 - CSS 文件里的
url()不在 HTML AST 范围内:像<link rel="stylesheet" href="style.css">指向的 CSS,里面的background: url(../img/x.png)需单独用postcss或正则处理,HTML AST 工具看不到它 - 构建工具生成的 hash 路径(如
main.a1b2c3.js)通常不应被重写:你的替换逻辑得排除含哈希特征的文件名,否则会破坏缓存失效机制
真正跑通的关键,不是 AST 解析有多准,而是路径上下文判断有没有覆盖这些隐性规则。










