从 marked.js 旧版本升级到最新版:API 变更与迁移指南

作者:百宝袋时间:2026-11-03 17:38:17浏览:642

针对 marked.js 从旧版本升级至最新版的完整工程迁移指南。详细梳理包导出、实例隔离、marked.use 扩展流水线、Renderer Token 对象解构及外置安全净化等核心破坏性变更,提供步骤拆解、代码示例与验证清单。

技术升级指南 破坏性变更迁移 目标版本:marked.js 最新稳定版 适用场景:Node.js 服务端 / Web 前端构建
从 marked.js 早期版本(v0.x ~ v4.x)迁移至最新现代化版本,核心任务是完成从“全局单体函数与位置参数”向“实例隔离、插件管线(marked.use())与 Token 字典对象解构”的范式转变。由于最新版彻底废弃了 marked() 直接调用、移除了内置的不安全净化配置(sanitize)、重构了 Renderer 的入参协议,并将标题锚点等辅助能力解耦为独立扩展包,直接替换版本号会导致项目直接中断运行。本文梳理核心破坏性变更矩阵,给出五步标准化平滑迁移流水线与完备的代码改造范例。
从 marked.js 旧版本升级到最新版:API 变更与迁移指南
图 0:marked.js 升级技术全景(从单体调用到现代扩展流水线)

一、版本演进与核心破坏性变更矩阵

在旧版 marked.js(v0.x 至 v4.x 早期)中,库的设计偏向轻量但紧耦合的全局单例模式。所有扩展往往通过改写全局原型链或调用 marked.setOptions() 注入,这种方式在现代前端模块化构建、同构渲染(SSR)或多实例多规则共存场景下,极易引发不可逆的状态污染与安全漏洞。

最新版 marked.js 彻底完成了底层架构的重写,确立了以抽象语法树(AST)Tokens 为中心、以插件管道为承载的工程规范。在着手升级前,必须准确识别以下四项根本性变更:

功能维度 旧版用法 (v0.x - v4.x) 最新版用法 (v12+ / v15+) 迁移影响与行动项
执行入口 marked(markdown, options) 直接作为函数调用 marked.parse(markdown, options) 或 new Marked().parse() 直接报错 直接调用会抛出 TypeError: marked is not a function,需全局替换调用入口。
配置管理 marked.setOptions({...}) 全局生效 marked.use({...}) 链式注册或通过 new Marked() 传入 行为变更 setOptions 已被废弃,应改用基于扩展包机制的 use()。
Renderer 签名 位置参数:
heading(text, level, raw, slugger)
code(code, infostring, escaped)
单一 Token 字典解构:
heading({ depth, text, tokens, raw })
code({ text, lang, escaped })
重大破坏 最易踩坑点。入参从多个普通标量变为包含 Token 信息的单一对象,原 level 属性变更为 depth。
XSS 安全防线 内置 sanitize: true 及 sanitizer 回调 核心库彻底移除净化逻辑,官方强制要求外接 DOMPurify 安全风险 旧配置项被静默忽略或报错,若不接入 DOMPurify 会导致严重的 XSS 注入隐患。
标题与锚点 内置 headerIds: true、mangle: true 移出核心包,独立为 marked-gfm-heading-id 与 marked-mangle 功能缺失 升级后标题默认不再自动挂载 id,需按需引入官方扩展。
语法高亮 在 options 中配置 highlight(code, lang) 回调 移出核心包,推荐配合 marked-highlight 插件或在 walkTokens 中处理 配置失效 options.highlight 不再被原生支持,需通过插件流水线拦截代码块。
从 marked.js 旧版本升级到最新版:API 变更与迁移指南
图 1:marked.js 核心架构演进与 API 变更全景图

二、从旧版到最新版的五步标准迁移流水线

迁移工作应当按照清晰的工程流水线顺序逐步推进。切忌在未经代码重构的情况下盲目升级版本依赖,否则分散的运行时异常将增加排查难度。按照以下五个步骤操作,可确保系统稳步过渡。

从 marked.js 旧版本升级到最新版:API 变更与迁移指南
图 2:从旧版到最新版的五步标准化平滑迁移流水线

步骤一:依赖版本升级与废弃配置清理

首先更新项目中的 marked 包到最新版本。若项目中原先依赖了代码高亮或标题 ID,同时安装官方独立拆分的插件包:

# 1. 升级核心解析库
npm install marked@latest

# 2. 安装外置能力支持插件及安全净化库
npm install marked-gfm-heading-id marked-highlight dompurify
npm install --save-dev @types/dompurify

检查项目中现有的 marked 配置对象,检索并彻底删除以下在最新版中已失效或被禁用的配置属性:

  • sanitize 与 sanitizer:已完全移除,保留此项可能触发配置警告或误导开发者以为安全防护依然生效。
  • mangle:邮箱地址转义混淆已移出核心。
  • headerIds 与 headerPrefix:标题 ID 自动生成已移出核心。
  • baseUrl:相对链接路径重写已移出核心(如需使用请引入 marked-base-url)。
  • highlight:代码高亮回调已移出核心。

步骤二:重构模块调用与实例隔离

旧版本代码常常使用 CommonJS 或默认导入直接把 marked 作为可执行函数调用:

// ❌ 旧版写法 (已失效)
const marked = require('marked');
const htmlOutput = marked('# Hello World', { gfm: true });

在最新版中,静态 marked.parse() 是执行解析的标准 API。对于多组件公用或服务端环境,推荐使用 new Marked() 创建独立实例,避免全局配置互相干扰:

//  最新版推荐写法:模块化与实例隔离
import {{ Marked }} from 'marked';

// 创建专属的 Markdown 解析器实例
const customMarked = new Marked();

// 通过实例解析文本
const markdownSource = '# 模块化改造验证

正文内容解析。';
const rawHtml = customMarked.parse(markdownSource);

步骤三:重构自定义 Renderer(重点:Token 对象解构)

⚠️ 最核心的破坏性变更警示 在旧版本中,Renderer 的钩子方法接收离散的位置参数(例如 heading(text, level, raw, slugger))。在最新版中,Renderer 的所有方法统一只接收一个 Token 对象,旧代码若直接解构第二个参数,level 将必定为 undefined!此外,原 level 属性在 Token 对象中已更名为 depth。

以下是常见渲染方法的重构对照:

// ❌ 旧版 Renderer 编写方式 (已废弃)
const oldRenderer = new marked.Renderer();
oldRenderer.heading = function (text, level, raw) {{
  return `<h${{level}} class="doc-heading">${{text}}</h${{level}}>`;
}};
oldRenderer.code = function (code, infostring, escaped) {{
  return `<pre class="lang-${{infostring}}"><code>${{code}}</code></pre>`;
}};
oldRenderer.link = function (href, title, text) {{
  return `<a href="${{href}}" target="_blank" rel="noopener">${{text}}</a>`;
}};

在最新版中,改用 marked.use({ renderer: {{ ... }} }) 注入,并对入参字典进行解构:

//  最新版 Renderer 编写方式 (Token 对象模式)
import {{ Marked }} from 'marked';

const customMarked = new Marked();

customMarked.use({{
  renderer: {{
    // 注意:入参为单一 Token 对象,层级字段为 depth 而不是 level
    heading({{ tokens, depth, text }}) {{
      // 使用 this.parser.parseInline(tokens) 可保留标题内部的行内标记(如粗体、链接)
      const parsedText = this.parser.parseInline(tokens);
      return `<h${{depth}} class="doc-heading depth-${{depth}}">${{parsedText}}</h${{depth}}>
`;
    }},

    // 代码块接收 text 与 lang
    code({{ text, lang, escaped }}) {{
      const validLang = (lang || '').trim();
      return `<pre class="code-block" data-lang="${{validLang}}"><code class="language-${{validLang}}">${{text}}</code></pre>
`;
    }},

    // 链接接收 href, title, tokens / text
    link({{ href, title, tokens }}) {{
      const label = this.parser.parseInline(tokens);
      const titleAttr = title ? ` title="${{title}}"` : '';
      return `<a href="${{href}}"${{titleAttr}} target="_blank" rel="noopener noreferrer">${{label}}</a>`;
    }}
  }}
}});

步骤四:装配官方扩展补齐外围能力

旧版本中 headerIds 自动为标题生成锚点,highlight 自动处理代码着色。最新版通过 marked.use() 插件机制将这些功能解耦接入。

import {{ Marked }} from 'marked';
import {{ gfmHeadingId }} from 'marked-gfm-heading-id';
import {{ markedHighlight }} from 'marked-highlight';
import hljs from 'highlight.js';

const markedInstance = new Marked();

// 1. 挂载标题锚点 ID 生成插件
markedInstance.use(gfmHeadingId({{
  prefix: 'section-'
}}));

// 2. 挂载代码高亮扩展插件
markedInstance.use(markedHighlight({{
  emptyLangClass: 'hljs',
  langPrefix: 'hljs language-',
  highlight(code, lang) {{
    const language = hljs.getLanguage(lang) ? lang : 'plaintext';
    return hljs.highlight(code, {{ language }}).value;
  }}
}}));

步骤五:集成 DOMPurify 安全过滤与全量回归

旧版的 sanitize: true 仅对少量已知标签进行了简易字符串转义,极易因复杂的嵌套标签、畸形属性或协议绕过引发 XSS 漏洞,这也是官方决定彻底废弃内置净化的主因。

升级后,必须在数据消费前接入工业级 DOM 树净化库 DOMPurify。在 Node.js 服务端渲染环境下,需结合 jsdom 初始化 DOMPurify:

// 服务端 (Node.js 环境) 安全渲染标准模式
import {{ Marked }} from 'marked';
import DOMPurify from 'dompurify';
import {{ JSDOM }} from 'jsdom';

// 初始化服务端安全 DOM 上下文
const window = new JSDOM('').window;
const purify = DOMPurify(window);

const marked = new Marked();

export function renderMarkdownSafely(markdownSource) {{
  // 1. marked 只负责将 Markdown 转换为原始 HTML
  const rawHtml = marked.parse(markdownSource);

  // 2. DOMPurify 负责阻断一切非法标签与脚本
  const safeHtml = purify.sanitize(rawHtml, {{
    USE_PROFILES: {{ html: true }},
    ADD_ATTR: ['target', 'rel'] // 按需保留合法属性
  }});

  return safeHtml;
}}

三、现代 marked 扩展管道与安全架构解析

最新版 marked.js 的内部运行机制建立在严格的数据处理流水线之上。了解流水线各阶段的触发顺序,有助于开发者在正确的节点扩展自定义业务逻辑,而不是盲目在全局做字符串替换。

从 marked.js 旧版本升级到最新版:API 变更与迁移指南
图 3:现代 marked.js 扩展管道与安全渲染防御体系

现代扩展管道包含以下核心生命周期:

  1. Hooks (preprocess):在源码解析前执行。可用于统一处理文本换行、多语言特殊字符转义、或是预先替换特定宏标记。
  2. Tokenizer / Lexer:词法分析器将连续的 Markdown 文本根据 CommonMark 与 GFM 规范切割为离散的 Token 节点树(如 paragraph、heading、code 等)。
  3. walkTokens:遍历所有 Token 节点的公共拦截点。如果需要异步提取文中所有的代码块进行批量预高亮,或者统一为外部图片 URL 添加鉴权参数,应当在此阶段完成。
  4. Renderer:将 Token 转换为 HTML 字符串。此阶段仅负责单一类型节点的结构拼装。
  5. Hooks (postprocess):在整篇文档拼装完成后执行最后的 HTML 调整,例如外层包裹容器。
  6. DOMPurify 独立安全隔离层:在数据交付到浏览器 DOM 或存储到数据库前执行最终防御,彻底剥离恶意注入。

四、典型迁移场景实战与完整代码示例

为了直观展示升级改造后的代码组织形态,以下给出一个集成了现代 ESM 实例隔离、标题锚点生成、代码高亮以及 DOMPurify 净化防线的工业级完整组件代码:

import {{ Marked }} from 'marked';
import {{ gfmHeadingId }} from 'marked-gfm-heading-id';
import {{ markedHighlight }} from 'marked-highlight';
import hljs from 'highlight.js';
import DOMPurify from 'dompurify';

/**
 * 创建标准化 Markdown 渲染服务实例
 */
export function createMarkdownEngine() {{
  const engine = new Marked();

  // 1. 注册标题锚点扩展
  engine.use(gfmHeadingId({{
    prefix: 'content-'
  }}));

  // 2. 注册代码高亮扩展
  engine.use(markedHighlight({{
    langPrefix: 'hljs language-',
    highlight(code, lang) {{
      const validLanguage = hljs.getLanguage(lang) ? lang : 'plaintext';
      return hljs.highlight(code, {{ language: validLanguage }}).value;
    }}
  }}));

  // 3. 注册自定义 Renderer(解构 Token 对象)
  engine.use({{
    renderer: {{
      // 自定义外部链接默认增加 noopener 与新标签页打开
      link({{ href, title, tokens }}) {{
        const text = this.parser.parseInline(tokens);
        const isExternal = href.startsWith('http://') || href.startsWith('https://');
        const targetStr = isExternal ? ' target="_blank" rel="noopener noreferrer"' : '';
        const titleStr = title ? ` title="${{title}}"` : '';
        return `<a href="${{href}}"${{titleStr}}${{targetStr}}>${{text}}</a>`;
      }},

      // 自定义图片渲染增加 loading="lazy"
      image({{ href, title, text }}) {{
        const titleStr = title ? ` title="${{title}}"` : '';
        return `<figure class="doc-image"><img src="${{href}}" alt="${{text}}"${{titleStr}} loading="lazy" /><figcaption>${{text}}</figcaption></figure>`;
      }}
    }}
  }});

  return {{
    render(markdownText) {{
      if (typeof markdownText !== 'string') {{
        return '';
      }}

      // 阶段 1:解析 Markdown
      const rawHtml = engine.parse(markdownText);

      // 阶段 2:安全净化
      const cleanHtml = DOMPurify.sanitize(rawHtml, {{
        USE_PROFILES: {{ html: true }},
        ADD_ATTR: ['target', 'rel', 'loading']
      }});

      return cleanHtml;
    }}
  }};
}}

// 验证调用
const parser = createMarkdownEngine();
const result = parser.render('### 示例标题

请访问 [示例网站](https://example.com) 测试。');
console.log(result);

五、常见故障排查、风险防范与迁移验收

升级完成后,应当针对常见的破坏性表现进行定向验证。以下总结了升级过程中最高频出现的故障现象及其成因对策,并提供验收基准。

从 marked.js 旧版本升级到最新版:API 变更与迁移指南
图 4:marked.js 迁移验收清单与核心收益总结

常见运行时故障排查

  • 现象一:抛出 TypeError: marked is not a function
    原因:代码中仍保留了 marked(text) 的旧式调用写法。
    修复:将 marked(...) 统一修改为 marked.parse(...),或改用 new Marked().parse(...)。
  • 现象二:自定义标题样式失效,且标题等级全变成 <undefined>
    原因:Renderer 中依然使用 heading(text, level)。在最新版中,第一个参数是包含全部属性的 Token 字典对象,第二个参数为空,导致 level 值为 undefined。
    修复:将入参改为对象解构 heading({ depth, text, tokens }),并将原代码中的 level 替换为 depth。
  • 现象三:控制台持续输出 headerIds option is deprecated 警告,且页内锚点失效
    原因:配置中仍保留了 headerIds: true,但该配置在现代核心库中已被放弃,不再负责为 <h1>~<h6> 生成 id 属性。
    修复:删除该配置项,改用 npm install marked-gfm-heading-id 并在 marked.use(gfmHeadingId()) 中注册。
  • 现象四:渲染出的 HTML 携带 <script>alert(1)</script> 且无告警
    原因:升级删除了 sanitize: true,但未在解析完成后套接 DOMPurify。
    修复:严格遵循“Markdown 解析后必须调用 DOMPurify.sanitize(html)”的安全规范。

系统上线验收清单

  • 实例作用域隔离:在多个模块中分别引入并初始化解析器,确认各自扩展互不污染。
  • GFM 规范回归测试:验证表格(Tables)、删除线(Strikethrough)、复选任务列表(Task Lists)均按标准解析。
  • Renderer 自定义节点输出校验:检查自定义的 heading、code、link、image 等标签属性是否与旧版本输出一致。
  • 代码高亮挂载测试:使用带语言标识的代码块(如 ```typescript)进行渲染,检查 CSS 类名与 DOM 节点是否正常上色。
  • XSS 安全测试:输入带有恶意 <img src="x" onerror="alert(1)"> 与 javascript: 伪协议的测试用例,验证输出中危险代码已被 100% 剥离。

完成以上步骤后,marked.js 即成功升级至最新稳定架构。项目不仅彻底摆脱了遗留版本的内存污染隐患与安全缺陷,还获得了基于 AST 语法树扩展与 TypeScript 强类型支持带来的长期工程可维护性。

作者最新文章
相关文章
更多