Files
argon-theme/docs/mermaid-developer-guide.md
nanhaoluo 29bfd284e0 feat: 实现 Mermaid 代码块魔改支持
- 添加 convertMermaidCodeblocks() 函数,在代码高亮前拦截 mermaid 代码块
- 支持标准 Markdown 代码块 (\\\mermaid) 渲染
- 更新 detectMermaidBlocks() 添加 mermaid-from-codeblock 选择器
- 更新 extractMermaidCode() 支持新容器类型
- 创建测试文件 test-codeblock-magic.html
- 更新用户文档、开发者文档和 FAQ
- 完全绕过代码高亮和 WordPress 格式化
- 支持 PJAX 页面切换
- 特殊字符和换行符正确保留
2026-01-24 21:35:12 +08:00

1231 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Mermaid 功能开发者文档
## 目录
1. [架构概述](#架构概述)
2. [PHP 函数参考](#php-函数参考)
3. [JavaScript API 参考](#javascript-api-参考)
4. [配置系统](#配置系统)
5. [扩展开发](#扩展开发)
6. [测试指南](#测试指南)
---
## 架构概述
### 系统组成
Mermaid 功能由以下几个部分组成:
```
┌─────────────────────────────────────────┐
│ WordPress 后台设置 │
│ (settings.php) │
└──────────────┬──────────────────────────┘
├─> 配置管理 (functions.php)
│ ├─ argon_get_mermaid_option()
│ ├─ argon_update_mermaid_option()
│ └─ argon_validate_mermaid_settings()
├─> 库加载器 (functions.php)
│ ├─ argon_enqueue_mermaid_scripts()
│ ├─ argon_has_mermaid_content()
│ └─ argon_get_mermaid_library_url()
├─> 插件兼容层 (functions.php)
│ ├─ argon_detect_mermaid_plugins()
│ ├─ argon_is_mermaid_library_enqueued()
│ └─ argon_should_load_mermaid_library()
└─> 前端渲染引擎 (argontheme.js)
├─ MermaidRenderer.init()
├─ MermaidRenderer.detectMermaidBlocks()
├─ MermaidRenderer.renderChart()
└─ MermaidRenderer.handleThemeSwitch()
```
### 工作流程
```mermaid
flowchart TD
A[页面加载] --> B{检查是否启用}
B -->|否| Z[结束]
B -->|是| C[检测页面内容]
C --> D{包含 Mermaid?}
D -->|否| Z
D -->|是| E[检测插件]
E --> F{插件已加载?}
F -->|是| G[只提供样式]
F -->|否| H[加载 Mermaid 库]
H --> I[初始化配置]
G --> I
I --> J[检测代码块]
J --> K[渲染图表]
K --> L[监听主题切换]
L --> Z
```
---
## PHP 函数参考
### 配置管理函数
#### argon_get_mermaid_option()
获取 Mermaid 配置选项。
**函数签名:**
```php
function argon_get_mermaid_option($option_name, $default = null)
```
**参数:**
- `$option_name` (string) - 配置选项名称,支持简短名称或完整名称
- `$default` (mixed) - 默认值,当选项不存在时返回
**返回值:**
- (mixed) 配置选项值
**支持的选项名称:**
| 简短名称 | 完整名称 | 说明 |
|---------|---------|------|
| `enabled` | `argon_enable_mermaid` | 是否启用 Mermaid |
| `cdn_source` | `argon_mermaid_cdn_source` | CDN 来源 |
| `custom_cdn_url` | `argon_mermaid_cdn_custom_url` | 自定义 CDN URL |
| `theme` | `argon_mermaid_theme` | 图表主题 |
| `use_local` | `argon_mermaid_use_local` | 使用本地镜像 |
| `debug_mode` | `argon_mermaid_debug_mode` | 调试模式 |
**示例:**
```php
// 使用简短名称
$enabled = argon_get_mermaid_option('enabled', false);
// 使用完整名称
$theme = argon_get_mermaid_option('argon_mermaid_theme', 'auto');
// 获取不存在的选项,返回默认值
$custom = argon_get_mermaid_option('custom_option', 'default_value');
```
---
#### argon_update_mermaid_option()
保存 Mermaid 配置选项。
**函数签名:**
```php
function argon_update_mermaid_option($option_name, $value)
```
**参数:**
- `$option_name` (string) - 配置选项名称
- `$value` (mixed) - 配置选项值
**返回值:**
- (bool) 是否保存成功
**示例:**
```php
// 启用 Mermaid
argon_update_mermaid_option('enabled', true);
// 设置主题
argon_update_mermaid_option('theme', 'dark');
// 设置 CDN 来源
argon_update_mermaid_option('cdn_source', 'jsdelivr');
```
---
#### argon_validate_mermaid_cdn_url()
验证 Mermaid CDN URL 格式。
**函数签名:**
```php
function argon_validate_mermaid_cdn_url($url)
```
**参数:**
- `$url` (string) - CDN URL
**返回值:**
- (bool) 是否为有效的 CDN URL
**验证规则:**
1. URL 不能为空
2. 必须是有效的 URL 格式
3. 必须以 `.js` 结尾
4. 必须使用 `http://``https://` 协议
**示例:**
```php
// 有效的 URL
$valid = argon_validate_mermaid_cdn_url('https://cdn.example.com/mermaid.min.js');
// 返回: true
// 无效的 URL不以 .js 结尾)
$invalid = argon_validate_mermaid_cdn_url('https://cdn.example.com/mermaid');
// 返回: false
```
---
#### argon_get_mermaid_theme()
获取当前主题模式对应的 Mermaid 主题。
**函数签名:**
```php
function argon_get_mermaid_theme()
```
**返回值:**
- (string) Mermaid 主题名称
**说明:**
- 如果配置为固定主题,直接返回配置的主题
- 如果配置为 `auto`,在 PHP 端返回 `default`,实际切换在 JavaScript 中实现
**示例:**
```php
$theme = argon_get_mermaid_theme();
// 返回: 'default', 'dark', 'forest', 'neutral' 之一
```
---
### 库加载函数
#### argon_has_mermaid_content()
检测页面内容是否包含 Mermaid 代码块。
**函数签名:**
```php
function argon_has_mermaid_content($content)
```
**参数:**
- `$content` (string) - 页面内容
**返回值:**
- (bool) 是否包含 Mermaid 代码块
**支持的格式:**
- `<div class="mermaid">`
- `<pre><code class="language-mermaid">`
- `<pre data-lang="mermaid">`
- `<code class="mermaid">`
**示例:**
```php
global $post;
if (argon_has_mermaid_content($post->post_content)) {
// 页面包含 Mermaid 代码块
echo '检测到 Mermaid 图表';
}
```
---
#### argon_get_mermaid_library_url()
获取 Mermaid 库的 URL。
**函数签名:**
```php
function argon_get_mermaid_library_url()
```
**返回值:**
- (string) Mermaid 库 URL
**说明:**
- 根据配置返回对应的 CDN 或本地路径
- 如果自定义 URL 无效,自动降级到 jsDelivr
**示例:**
```php
$url = argon_get_mermaid_library_url();
// 返回: 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js'
```
---
#### argon_enqueue_mermaid_scripts()
加载 Mermaid JavaScript 库。
**函数签名:**
```php
function argon_enqueue_mermaid_scripts()
```
**说明:**
-`wp_enqueue_scripts` 钩子中调用
- 只在启用 Mermaid 且页面包含代码块时加载
- 自动检测插件,避免重复加载
**钩子:**
```php
add_action('wp_enqueue_scripts', 'argon_enqueue_mermaid_scripts');
```
---
### 插件兼容函数
#### argon_detect_mermaid_plugins()
检测已安装的 Mermaid 相关插件。
**函数签名:**
```php
function argon_detect_mermaid_plugins()
```
**返回值:**
- (array) 插件检测结果数组
**返回格式:**
```php
[
'wp-githuber-md' => false,
'markdown-block' => false,
'code-syntax-block' => false,
'mermaid-loaded' => false
]
```
**示例:**
```php
$plugins = argon_detect_mermaid_plugins();
if ($plugins['wp-githuber-md']) {
echo 'WP Githuber MD 插件已安装';
}
```
---
#### argon_is_mermaid_library_enqueued()
检查是否有插件已经加载了 Mermaid 库。
**函数签名:**
```php
function argon_is_mermaid_library_enqueued()
```
**返回值:**
- (bool) 是否已加载
**说明:**
- 检测常见的 Mermaid 脚本句柄
- 包括:`mermaid`, `mermaid-js`, `githuber-mermaid`, `wp-mermaid`, `markdown-mermaid`
**示例:**
```php
if (argon_is_mermaid_library_enqueued()) {
echo 'Mermaid 库已由其他来源加载';
}
```
---
#### argon_should_load_mermaid_library()
判断是否应该加载 Mermaid 库。
**函数签名:**
```php
function argon_should_load_mermaid_library()
```
**返回值:**
- (bool) 是否应该加载
**说明:**
- 检测插件和已加载的库
- 避免重复加载
- 在调试模式下输出日志
**示例:**
```php
if (argon_should_load_mermaid_library()) {
// 主题负责加载 Mermaid 库
wp_enqueue_script('mermaid', $url);
}
```
---
## JavaScript API 参考
### MermaidRenderer 对象
前端渲染引擎,负责检测和渲染 Mermaid 图表。
#### 属性
| 属性 | 类型 | 说明 |
|------|------|------|
| `initialized` | boolean | 是否已初始化 |
| `rendered` | Set | 已渲染的图表 ID 集合 |
| `config` | Object | 配置对象 |
---
#### init()
初始化 Mermaid 配置并渲染所有图表。
**函数签名:**
```javascript
MermaidRenderer.init()
```
**返回值:**
- (boolean) 是否初始化成功
**说明:**
- 检查 Mermaid 库是否已加载
- 初始化 Mermaid 配置
- 渲染所有图表
- 监听主题切换事件
**示例:**
```javascript
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
if (typeof MermaidRenderer !== 'undefined') {
MermaidRenderer.init();
}
});
```
---
#### detectMermaidBlocks()
检测所有 Mermaid 代码块。
**函数签名:**
```javascript
MermaidRenderer.detectMermaidBlocks()
```
**返回值:**
- (Array) Mermaid 代码块元素数组
**检测规则:**
1. `div.mermaid` - 标准格式
2. `pre code.language-mermaid` - Markdown 格式
3. `pre[data-lang="mermaid"]` - 自定义属性格式
4. `code.mermaid` - 简化格式
**示例:**
```javascript
const blocks = MermaidRenderer.detectMermaidBlocks();
console.log(`检测到 ${blocks.length} 个 Mermaid 代码块`);
```
---
#### extractMermaidCode()
提取代码块内容。
**函数签名:**
```javascript
MermaidRenderer.extractMermaidCode(element)
```
**参数:**
- `element` (HTMLElement) - 代码块元素
**返回值:**
- (string) Mermaid 代码
**说明:**
- 支持多种代码块格式
- 自动处理 WP-Markdown 生成的 `document.write()` 格式
- 解码转义字符(`\n`, `\"`, `\'`
**示例:**
```javascript
const element = document.querySelector('.mermaid');
const code = MermaidRenderer.extractMermaidCode(element);
console.log(code);
```
---
#### renderChart()
渲染单个图表。
**函数签名:**
```javascript
MermaidRenderer.renderChart(element, index)
```
**参数:**
- `element` (HTMLElement) - 代码块元素
- `index` (number) - 图表索引
**说明:**
- 生成唯一的图表 ID
- 避免重复渲染
- 渲染成功后替换原始代码块
- 渲染失败时显示错误提示
**示例:**
```javascript
const blocks = MermaidRenderer.detectMermaidBlocks();
blocks.forEach((block, index) => {
MermaidRenderer.renderChart(block, index);
});
```
---
#### handleThemeSwitch()
处理主题切换事件。
**函数签名:**
```javascript
MermaidRenderer.handleThemeSwitch()
```
**说明:**
- 检测页面主题变化
- 重新渲染所有图表
- 使用新的主题配置
**示例:**
```javascript
// 监听主题切换
document.addEventListener('themeChanged', function() {
MermaidRenderer.handleThemeSwitch();
});
```
---
### 配置对象
#### argonMermaidConfig
全局配置对象,由 PHP 传递到前端。
**属性:**
```javascript
{
enabled: true, // 是否启用
theme: 'auto', // 图表主题
debugMode: false, // 调试模式
fallbackUrls: [], // 备用 CDN URL 列表
libraryLoadedByPlugin: false // 库是否由插件加载
}
```
**访问方式:**
```javascript
if (window.argonMermaidConfig) {
console.log('Mermaid 已启用:', argonMermaidConfig.enabled);
console.log('当前主题:', argonMermaidConfig.theme);
}
```
---
## 配置系统
### 配置选项
| 选项名称 | 类型 | 默认值 | 说明 |
|---------|------|--------|------|
| `argon_enable_mermaid` | boolean | false | 是否启用 Mermaid |
| `argon_mermaid_cdn_source` | string | 'jsdelivr' | CDN 来源 |
| `argon_mermaid_cdn_custom_url` | string | '' | 自定义 CDN URL |
| `argon_mermaid_theme` | string | 'auto' | 图表主题 |
| `argon_mermaid_use_local` | boolean | false | 使用本地镜像 |
| `argon_mermaid_debug_mode` | boolean | false | 调试模式 |
### CDN 来源选项
| 值 | 说明 | URL |
|----|------|-----|
| `jsdelivr` | jsDelivr CDN | `https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js` |
| `unpkg` | unpkg CDN | `https://unpkg.com/mermaid@10/dist/mermaid.min.js` |
| `custom` | 自定义 CDN | 用户指定的 URL |
| `local` | 本地文件 | `{theme_url}/assets/vendor/mermaid/mermaid.min.js` |
### 主题选项
| 值 | 说明 |
|----|------|
| `auto` | 自动切换(跟随页面主题) |
| `default` | 默认主题(浅色) |
| `dark` | 深色主题 |
| `forest` | 森林主题(绿色) |
| `neutral` | 中性主题(灰色) |
---
## 扩展开发
### 添加新的图表类型支持
Mermaid 支持的所有图表类型都已自动支持,无需额外配置。
### 自定义样式
`style.css` 中添加自定义样式:
```css
/* 自定义 Mermaid 容器样式 */
.mermaid-container {
background: #f5f5f5;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
/* 自定义错误提示样式 */
.mermaid-error-container {
background: #fff3cd;
border-left: 4px solid #ffc107;
padding: 15px;
border-radius: 4px;
}
/* 夜间模式样式 */
html.darkmode .mermaid-container {
background: #2d2d2d;
}
```
### 添加自定义钩子
`functions.php` 中添加钩子:
```php
/**
* Mermaid 渲染前钩子
*
* @param string $content 页面内容
* @return string 修改后的内容
*/
function my_custom_mermaid_before_render($content) {
// 自定义处理逻辑
return $content;
}
add_filter('argon_mermaid_before_render', 'my_custom_mermaid_before_render');
/**
* Mermaid 渲染后钩子
*
* @param string $content 渲染后的内容
* @return string 修改后的内容
*/
function my_custom_mermaid_after_render($content) {
// 自定义处理逻辑
return $content;
}
add_filter('argon_mermaid_after_render', 'my_custom_mermaid_after_render');
```
---
## 测试指南
### 单元测试
运行 PHP 单元测试:
```bash
cd tests
php run-mermaid-config-tests.php
```
### 测试用例
测试文件位于 `tests/` 目录:
- `test-mermaid-config.php` - 配置管理测试
- `test-mermaid-loader.php` - 库加载测试
- `test-mermaid-fallback.php` - CDN 降级测试
- `test-wp-markdown-format.html` - WP-Markdown 格式测试
### 手动测试
1. **基础功能测试**
- 启用 Mermaid 支持
- 创建包含 Mermaid 代码的文章
- 验证图表是否正确渲染
2. **主题切换测试**
- 切换页面主题(日间/夜间)
- 验证图表主题是否自动切换
3. **CDN 降级测试**
- 模拟 CDN 加载失败
- 验证是否自动尝试备用 CDN
4. **插件兼容测试**
- 安装 Mermaid 相关插件
- 验证是否正确检测并避免冲突
5. **错误处理测试**
- 输入错误的 Mermaid 代码
- 验证错误提示是否友好
---
## 调试技巧
### 启用调试模式
在主题设置中启用调试模式,查看详细日志:
```javascript
[Argon Mermaid] Mermaid 配置初始化成功 {theme: "default"}
[Argon Mermaid] 检测到 3 Mermaid 代码块
[Argon Mermaid] 渲染图表 #mermaid-chart-1234567890-0
[Argon Mermaid] 图表渲染成功
```
### 浏览器控制台
打开浏览器开发者工具F12查看
1. **控制台日志** - 查看 Mermaid 相关日志
2. **网络请求** - 查看 CDN 加载状态
3. **元素检查** - 查看渲染后的 SVG 结构
### PHP 错误日志
`wp-config.php` 中启用调试:
```php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
```
查看日志文件:`wp-content/debug.log`
---
## 性能优化
### 按需加载
主题已实现按需加载,只在包含 Mermaid 代码的页面加载库。
### 异步加载
Mermaid 库使用 `async` 属性异步加载,不阻塞页面渲染。
### CDN 缓存
使用 CDN 加速加载,浏览器会自动缓存库文件。
### 避免重复渲染
使用 `rendered` Set 记录已渲染的图表,避免重复渲染。
---
## 贡献指南
### 代码规范
遵循 Argon 主题的代码规范:
- **PHP**: 遵循 WordPress Coding Standards
- **JavaScript**: 使用 Tab 缩进,严格相等运算符
- **CSS**: 使用 Tab 缩进,属性独占一行
### 提交规范
- **功能添加**: `feat: 添加 XXX 功能`
- **Bug 修复**: `fix: 修复 XXX 问题`
- **文档更新**: `docs: 更新 XXX 文档`
### 测试要求
- 添加新功能时,必须添加对应的测试用例
- 修复 Bug 时,必须添加回归测试
- 所有测试必须通过
---
## 相关资源
- [Mermaid 官方文档](https://mermaid.js.org/)
- [WordPress 开发文档](https://developer.wordpress.org/)
- [Argon 主题 GitHub](https://github.com/solstice23/argon-theme)
---
**最后更新:** 2024-01-22
**文档版本:** 1.0.0
---
## 代码块魔改技术
### 概述
代码块魔改是一种在代码高亮之前拦截并转换 Mermaid 代码块的技术,使得标准 Markdown 代码块 (` ```mermaid `) 能够正确渲染为 Mermaid 图表,而不被代码高亮处理。
### 实现原理
#### 1. 执行顺序
```
页面加载/PJAX切换
highlightJsRender() 调用
convertMermaidCodeblocks() 执行 ← 【拦截阶段】
代码高亮处理其他代码块
MermaidRenderer.detectMermaidBlocks() ← 【检测阶段】
MermaidRenderer.renderChart() ← 【渲染阶段】
```
#### 2. 核心函数
**convertMermaidCodeblocks()**
在代码高亮之前转换 Mermaid 代码块。
**位置:** `argontheme.js` 第 3942 行之前
**功能:**
- 使用多个选择器查找 mermaid 代码块
- 提取纯文本代码(使用 `textContent`
- 创建 `<div class="mermaid-from-codeblock">` 容器
- 替换原始代码块元素
- 添加 `data-processed="true"` 标记防止重复处理
**代码示例:**
```javascript
function convertMermaidCodeblocks(){
// 支持多种代码块格式
const selectors = [
'pre > code.language-mermaid', // 标准 Markdown 格式(最常见)
'pre > code.mermaid', // 简化格式
'code.language-mermaid', // 无 pre 包裹
'pre[data-lang="mermaid"]' // 自定义属性格式
];
let processedCount = 0;
selectors.forEach(selector => {
document.querySelectorAll(selector).forEach(element => {
// 避免重复处理
if (element.dataset.mermaidProcessed) {
return;
}
// 提取代码
let code = element.textContent.trim();
if (!code) {
return;
}
// 创建容器
const container = document.createElement('div');
container.className = 'mermaid-from-codeblock';
container.textContent = code;
container.dataset.processed = 'true';
// 替换元素
const targetElement = element.closest('pre') || element;
if (targetElement.parentNode) {
targetElement.parentNode.replaceChild(container, targetElement);
processedCount++;
}
// 标记已处理
element.dataset.mermaidProcessed = 'true';
});
});
if (processedCount > 0 && typeof console !== 'undefined' && console.log) {
console.log('[Mermaid] 转换了 ' + processedCount + ' 个代码块');
}
}
```
#### 3. 集成点
**集成点 1highlightJsRender() 函数**
在函数开始处调用 `convertMermaidCodeblocks()`
```javascript
function highlightJsRender(){
// 在代码高亮之前,先处理 Mermaid 代码块
convertMermaidCodeblocks();
if (typeof(hljs) == "undefined"){
return;
}
// ... 原有的代码高亮逻辑
}
```
**集成点 2detectMermaidBlocks() 函数**
在 selectors 数组中添加新的容器类型:
```javascript
const selectors = [
'div.mermaid-shortcode', // Shortcode 格式(推荐)
'div.mermaid-from-codeblock', // 代码块魔改格式(新增)
'div.mermaid', // 标准格式
'pre code.language-mermaid', // Markdown 格式(降级)
'pre[data-lang="mermaid"]', // 自定义属性格式
'code.mermaid' // 简化格式
];
```
**集成点 3extractMermaidCode() 函数**
添加对新容器类型的处理:
```javascript
// 处理代码块魔改格式(新增)
else if (element.classList.contains('mermaid-from-codeblock')) {
code = element.textContent;
this.logDebug('从代码块魔改格式提取代码');
}
```
### 技术细节
#### 选择器优先级
选择器按以下优先级匹配:
1. `pre > code.language-mermaid` - 标准 Markdown 格式(最常见)
2. `pre > code.mermaid` - 简化格式
3. `code.language-mermaid` - 无 pre 包裹
4. `pre[data-lang="mermaid"]` - 自定义属性格式
#### 重复处理防护
使用 `data-mermaid-processed` 属性标记已处理的元素:
```javascript
if (element.dataset.mermaidProcessed) {
return; // 跳过已处理的元素
}
// ... 处理逻辑
element.dataset.mermaidProcessed = 'true';
```
这样可以:
- 避免 PJAX 切换时重复处理
- 防止多次调用导致的错误
- 提高性能
#### 代码提取
使用 `textContent` 而非 `innerHTML`
```javascript
let code = element.textContent.trim();
```
**优势:**
- 获取纯文本,避免 HTML 实体
- 防止 XSS 攻击
- 保持原始内容不变
#### 容器创建
创建新的 div 容器:
```javascript
const container = document.createElement('div');
container.className = 'mermaid-from-codeblock';
container.textContent = code;
container.dataset.processed = 'true';
```
**属性说明:**
- `class="mermaid-from-codeblock"` - 标识来源于代码块
- `data-processed="true"` - 标记已处理
- `textContent` - 纯文本内容(不使用 `innerHTML`
#### 元素替换
优先替换整个 `<pre>` 元素:
```javascript
const targetElement = element.closest('pre') || element;
if (targetElement.parentNode) {
targetElement.parentNode.replaceChild(container, targetElement);
}
```
**逻辑:**
- 如果代码块在 `<pre>` 中,替换整个 `<pre>` 元素
- 如果没有 `<pre>` 包裹,替换 `<code>` 元素
- 保留原始位置和上下文
### PJAX 兼容性
代码块转换自动支持 PJAX因为
1. `highlightJsRender()` 已在 PJAX 回调中调用
2. `convertMermaidCodeblocks()``highlightJsRender()` 开始处执行
3. 使用 `data-processed` 属性防止重复处理
**PJAX 回调链:**
```javascript
$(document).on('pjax:complete', function() {
// ... 其他初始化
try { highlightJsRender(); } catch (err) { ... } // 包含代码块转换
// ... 其他初始化
});
```
### 性能优化
#### 1. 使用原生 JavaScript
```javascript
document.querySelectorAll(selector) // 而非 $(selector)
```
**理由:** 原生方法性能更好,减少 jQuery 开销
#### 2. 提前返回
```javascript
if (element.dataset.mermaidProcessed) {
return; // 提前返回,避免不必要的处理
}
```
**理由:** 减少重复处理,提高效率
#### 3. 批量处理
```javascript
selectors.forEach(selector => {
document.querySelectorAll(selector).forEach(element => {
// 处理逻辑
});
});
```
**理由:** 一次性查找所有元素,减少 DOM 查询次数
#### 4. 最小化 DOM 操作
```javascript
const container = document.createElement('div');
container.className = 'mermaid-from-codeblock';
container.textContent = code;
container.dataset.processed = 'true';
targetElement.parentNode.replaceChild(container, targetElement);
```
**理由:** 一次性创建和替换,减少重排和重绘
### 安全性
#### XSS 防护
使用 `textContent` 而非 `innerHTML`
```javascript
container.textContent = code; // 安全
// container.innerHTML = code; // 不安全
```
**理由:** `textContent` 会自动转义 HTML防止 XSS 攻击
#### 代码来源验证
```javascript
let code = element.textContent.trim();
if (!code) {
return; // 拒绝空代码
}
```
**理由:** 避免处理恶意或无效的代码块
### 错误处理
#### 空代码检查
```javascript
let code = element.textContent.trim();
if (!code) {
return; // 跳过空代码块
}
```
**理由:** 避免创建空容器,减少不必要的 DOM 操作
#### Try-Catch 包裹
```javascript
try {
convertMermaidCodeblocks();
} catch (err) {
console.error('Mermaid 代码块转换失败:', err);
}
```
**理由:** 捕获异常,不中断其他代码块的处理
#### 降级支持
如果代码块转换失败,仍可通过降级选择器检测:
```javascript
'pre code.language-mermaid' // 降级选择器
```
**理由:** 确保即使转换失败,仍能渲染
### 扩展方式
#### 添加新的选择器
`selectors` 数组中添加新的选择器:
```javascript
const selectors = [
'pre > code.language-mermaid',
'pre > code.mermaid',
'code.language-mermaid',
'pre[data-lang="mermaid"]',
'your-custom-selector' // 添加自定义选择器
];
```
#### 自定义容器类名
修改容器类名:
```javascript
container.className = 'your-custom-class';
```
然后在 `detectMermaidBlocks()` 中添加对应的选择器:
```javascript
const selectors = [
'div.mermaid-shortcode',
'div.your-custom-class', // 添加自定义类名
'div.mermaid',
// ...
];
```
### 调试技巧
#### 1. 启用调试日志
`convertMermaidCodeblocks()` 中添加日志:
```javascript
console.log('[Mermaid] 找到代码块:', element);
console.log('[Mermaid] 提取的代码:', code);
console.log('[Mermaid] 创建的容器:', container);
```
#### 2. 检查转换结果
在浏览器控制台中检查:
```javascript
// 查看所有转换后的容器
document.querySelectorAll('.mermaid-from-codeblock')
// 查看容器内容
document.querySelectorAll('.mermaid-from-codeblock').forEach(el => {
console.log(el.textContent);
});
```
#### 3. 验证执行顺序
在关键位置添加日志:
```javascript
function highlightJsRender(){
console.log('[1] highlightJsRender 开始');
convertMermaidCodeblocks();
console.log('[2] convertMermaidCodeblocks 完成');
// ... 代码高亮逻辑
console.log('[3] 代码高亮完成');
}
```
### 测试用例
参见 `tests/test-codeblock-magic.html` 文件,包含以下测试:
1. 标准 Markdown 格式
2. 多个代码块(批量处理)
3. 特殊字符保留
4. 空代码块(边界情况)
5. 多行代码块(换行符保留)
6. 简化格式
7. 无 pre 包裹
8. 自定义属性格式
9. 复杂图表
### 常见问题
#### Q: 为什么要在代码高亮之前拦截?
A: 因为代码高亮会:
- 添加行号和控制按钮
- 转换特殊字符(`-->``>`
- 添加额外的 HTML 结构
- 干扰 Mermaid 渲染
#### Q: 为什么使用 `textContent` 而不是 `innerHTML`
A: 因为:
- `textContent` 获取纯文本,避免 HTML 实体
- 防止 XSS 攻击
- 保持原始内容不变
#### Q: 如何确保 PJAX 兼容性?
A: 通过:
-`highlightJsRender()` 中调用(已在 PJAX 回调中)
- 使用 `data-processed` 属性防止重复处理
- 无需额外修改 PJAX 逻辑
#### Q: 如何添加新的代码块格式?
A: 在 `selectors` 数组中添加新的选择器,然后在 `detectMermaidBlocks()``extractMermaidCode()` 中添加对应的处理逻辑。
---