知识图谱 ForceAtlas2 + Sigma.js v3 完整修复记录
日期: 2026-04-05
耗时: 约 3 小时 (06:00 - 09:00 UTC)
状态: ✅ 已完成
部署: 39c5a6a production success
访问: https://nanobot-kb.pages.dev/knowledge/visualization/graph-view
📋 问题概述
知识图谱页面加载后无法渲染,连续出现多个 JavaScript 错误,导致图谱无法正常显示。
🔍 问题根因分析
问题 1: ForceAtlas2 导入缺失
现象:
Error: graphology-layout-forceatlas2: invalid number of iterations.根因:
- Vite 构建时 Tree-shaking 优化掉未显式导入的代码
ForceAtlas2模块未被打包进最终产物
修复:
typescript
// 添加静态导入 (第 25 行)
import ForceAtlas2 from 'graphology-layout-forceatlas2/worker'提交: 560f522
问题 2: 动态导入覆盖静态导入
现象:
- 修复问题 1 后,线上仍报
invalid number of iterations错误 - 本地代码正确,但构建后行为异常
根因:
typescript
// 第 25 行 - 正确的 worker 版本导入
import ForceAtlas2 from 'graphology-layout-forceatlas2/worker'
// 第 120 行 - 错误的动态导入 (覆盖了第 25 行!)
const { default: ForceAtlas2 } = await import('graphology-layout-forceatlas2')
// ^^^^^^^^^^^^^^^^^^^^^^^^
// 同步版本,不是 worker!分析:
- 第 120 行的动态导入在运行时执行,覆盖了第 25 行的静态导入
graphology-layout-forceatlas2(同步版) 要求传入iterations参数graphology-layout-forceatlas2/worker(worker 版) 不接受iterations参数
修复:
typescript
// 删除第 120 行的动态导入
// 改为注释说明
// ForceAtlas2 已在顶部静态导入 (worker 版本)
console.log('ForceAtlas2 已就绪 (worker 版本)')提交: 8f17dea
问题 3: Sigma.js v3 节点类型错误
现象:
TypeError: Cannot read properties of null (reading 'blendFunc')
Sigma: could not find a valid renderer for node type 'default'根因:
typescript
// 错误的配置
defaultNodeType: 'default' // 'default' 类型不存在!分析:
- Sigma.js v3 的节点类型必须是内置类型或已注册的类型
- 内置类型:
'circle','dot','square','diamond', etc. 'default'不是有效的节点类型
修复:
typescript
// 改为内置的 'circle' 类型
defaultNodeType: 'circle'提交: 39c5a6a
🛠️ 修复步骤
步骤 1: 添加 ForceAtlas2 静态导入
diff
<script setup lang="ts">
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
+ import ForceAtlas2 from 'graphology-layout-forceatlas2/worker'
const isClient = ref(false)
// ...
</script>步骤 2: 删除错误的动态导入
diff
console.log('正在导入 sigma...')
const { default: Sigma } = await import('sigma')
console.log('sigma 导入成功')
- console.log('正在导入 graphology-layout-forceatlas2...')
- const { default: ForceAtlas2 } = await import('graphology-layout-forceatlas2')
- console.log('ForceAtlas2 导入成功')
+ // ForceAtlas2 已在顶部静态导入 (worker 版本)
+ console.log('ForceAtlas2 已就绪 (worker 版本)')步骤 3: 修正 Sigma.js 节点类型
diff
sigmaInstance = new Sigma(graph, graphContainer.value, {
renderEdgeLabels: false,
- defaultNodeType: 'default',
+ defaultNodeType: 'circle',
defaultEdgeType: 'line',
// ...
})🧪 验证方法
1. 本地构建测试
bash
cd docs
npm run docs:build
# 确认无编译错误2. 浏览器自动化测试 (Puppeteer)
javascript
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch({ headless: 'new' });
const page = await browser.newPage();
await page.setCacheEnabled(false);
const errors = [];
page.on('pageerror', error => errors.push(error.message));
await page.goto('https://nanobot-kb.pages.dev/knowledge/visualization/graph-view');
await new Promise(r => setTimeout(r, 5000));
// 检查关键错误
const hasIterationsError = errors.some(e => e.includes('iterations'));
const hasSigmaError = errors.some(e => e.includes('Sigma: could not find'));
const hasWebGLError = errors.some(e => e.includes('blendFunc'));
console.log(`iterations 错误:${hasIterationsError ? '❌' : '✅'}`);
console.log(`Sigma 错误:${hasSigmaError ? '❌' : '✅'}`);
console.log(`WebGL 错误 (Headless 预期): ${hasWebGLError ? '⚠️' : '✅'}`);
await browser.close();3. 线上部署验证
bash
# 检查部署状态
curl -X GET "https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/pages/projects/{PROJECT}/deployments" \
-H "Authorization: Bearer {API_TOKEN}"
# 检查线上 JS 文件
curl -s "https://nanobot-kb.pages.dev/knowledge/visualization/graph-view" | \
grep -oE "assets/chunks/theme\.[^\"']+\.js" | \
xargs -I {} curl -s "https://nanobot-kb.pages.dev/{}" > /tmp/check.js
# 验证无 iterations 参数
python3 -c "
with open('/tmp/check.js') as f:
content = f.read()
print('包含 iterations:', 'iterations' in content)
"📊 最终状态
| 检查项 | 状态 |
|---|---|
| ForceAtlas2 worker 导入 | ✅ |
| 无动态导入覆盖 | ✅ |
| iterations 参数 | ✅ 已移除 |
| Sigma 节点类型 | ✅ circle |
| 线上部署 | ✅ 39c5a6a success |
| 图谱数据 | ✅ 31 节点,24 边 |
| 浏览器测试 | ✅ 所有关键错误通过 |
💡 经验教训
1. 导入语句冲突
教训: 静态导入和动态导入同时存在时,动态导入会覆盖静态导入。
最佳实践:
- ✅ 优先使用静态导入
- ✅ 避免混用静态和动态导入同一模块
- ✅ 如必须动态导入,删除对应的静态导入
2. 模块版本差异
教训: graphology-layout-forceatlas2 有同步版和 worker 版,API 不同。
最佳实践:
- ✅ 明确区分
module和module/worker - ✅ 查阅官方文档确认 API 差异
- ✅ Worker 模式不传
iterations参数
3. 第三方库版本升级
教训: Sigma.js v3 的节点类型与 v2 不同,'default' 不存在。
最佳实践:
- ✅ 升级前查阅迁移指南
- ✅ 检查
package.json的exports字段 - ✅ 使用内置类型或显式注册自定义类型
4. 浏览器测试的重要性
教训: 安装了 Puppeteer 但没有充分利用,导致问题排查缓慢。
最佳实践:
- ✅ 每次修复后运行完整的浏览器测试
- ✅ 收集所有控制台错误并分类
- ✅ 按优先级修复(代码错误 > 渲染错误)
- ✅ 区分预期内错误(Headless WebGL)和真实错误
🔗 相关文件
- 源代码:
docs/.vitepress/theme/components/KnowledgeGraph.vue - 图谱数据:
docs/public/graph_index.json - 构建脚本:
scripts/build-graph-index.py - 测试脚本:
scripts/test-graph-browser.js
📝 提交历史
39c5a6a fix: 修改默认节点类型为 'circle'(Sigma.js v3 内置支持)
8f17dea fix: 删除错误的动态导入,使用顶部静态导入的 worker 版本
560f522 fix: 添加缺失的 ForceAtlas2 导入语句
c905960 fix: 添加修复标记注释以强制重新构建
c48f406 chore: 强制重新部署以刷新 CDN 缓存
c09e3ec docs: 归档知识图谱修复任务 (2026-04-05)最后更新: 2026-04-05 09:07 UTC
状态: ✅ 已完成