8.5 KiB
8.5 KiB
修复:Obsidian 加载卡住和样式加载失败问题
🐛 问题描述
症状1:Obsidian 一直处于"加载工作区中"状态
症状2:进入安全模式后关闭安全模式,插件能加载但提示:
"获取样式失败defaultldefault,请检查主题是否正确安装。"
🔍 根本原因分析
问题1: getTheme() 和 getHighlight() 返回 undefined
位置:src/assets.ts
// 原代码
getTheme(themeName: string) {
if (themeName === '') {
return this.themes[0];
}
for (const theme of this.themes) {
if (theme.name.toLowerCase() === themeName.toLowerCase() ||
theme.className.toLowerCase() === themeName.toLowerCase()) {
return theme;
}
}
// ❌ 找不到主题时没有返回值!返回 undefined
}
问题:
- 当
themeName为'default'但主题列表中没有完全匹配的主题时 - 方法返回
undefined - 在
article-render.ts的getCSS()中访问theme!.css时出错 - 导致整个插件初始化失败
问题2: ArticleRender 初始化时使用硬编码的 'default'
位置:src/article-render.ts
constructor(app, itemView, styleEl, articleDiv) {
// ...
this._currentTheme = 'default'; // ❌ 硬编码
this._currentHighlight = 'default'; // ❌ 硬编码
}
问题:
- 不使用用户配置的默认主题
settings.defaultStyle - 如果主题列表中没有名为 'default' 的主题,就会失败
问题3: preview-view.ts 没有设置 ArticleRender 的主题
位置:src/preview-view.ts
get render(): ArticleRender {
if (!this._articleRender) {
this._articleRender = new ArticleRender(...);
// ❌ 没有设置 currentTheme 和 currentHighlight
}
return this._articleRender;
}
问题4: 初始化失败时没有错误处理
位置:src/preview-view.ts
async onOpen(): Promise<void> {
// ❌ 如果初始化失败,会导致 Obsidian 一直卡在加载中
await this.initializeSettings();
await this.createManager();
// ...
}
✅ 修复方案
修复1: 为 getTheme() 和 getHighlight() 添加默认返回值
文件:src/assets.ts
getTheme(themeName: string) {
if (themeName === '') {
return this.themes[0];
}
for (const theme of this.themes) {
if (theme.name.toLowerCase() === themeName.toLowerCase() ||
theme.className.toLowerCase() === themeName.toLowerCase()) {
return theme;
}
}
// ✅ 找不到主题时返回第一个主题(默认主题)
console.warn(`[Assets] 主题 "${themeName}" 未找到,使用默认主题`);
return this.themes[0];
}
getHighlight(highlightName: string) {
if (highlightName === '') {
return this.highlights[0];
}
for (const highlight of this.highlights) {
if (highlight.name.toLowerCase() === highlightName.toLowerCase()) {
return highlight;
}
}
// ✅ 找不到高亮时返回第一个高亮(默认高亮)
console.warn(`[Assets] 高亮 "${highlightName}" 未找到,使用默认高亮`);
return this.highlights[0];
}
效果:
- 即使找不到指定的主题,也会返回一个有效的主题对象
- 避免返回
undefined导致的错误 - 添加警告日志,便于调试
修复2: 在 preview-view.ts 中设置正确的主题
文件:src/preview-view.ts
get render(): ArticleRender {
if (!this._articleRender) {
// 创建临时容器用于 ArticleRender
if (!this.styleEl) {
this.styleEl = document.createElement('style');
}
if (!this.articleDiv) {
this.articleDiv = document.createElement('div');
}
this._articleRender = new ArticleRender(
this.app,
this,
this.styleEl,
this.articleDiv
);
// ✅ 设置默认主题和高亮(使用用户配置)
this._articleRender.currentTheme = this.settings.defaultStyle;
this._articleRender.currentHighlight = this.settings.defaultHighlight;
}
return this._articleRender;
}
效果:
- 使用用户配置的默认主题,而不是硬编码的 'default'
- 确保 ArticleRender 初始化时有正确的主题设置
修复3: 添加错误处理,避免卡在加载中
文件:src/preview-view.ts
async onOpen(): Promise<void> {
console.log('[PreviewView] 视图打开');
try {
// 显示加载动画
this.showLoading();
// 初始化设置和资源
await this.initializeSettings();
// 创建预览管理器
await this.createManager();
// 注册事件监听
this.registerEventListeners();
// 渲染当前文件
await this.renderCurrentFile();
uevent('open');
} catch (error) {
// ✅ 捕获错误,避免卡住
console.error('[PreviewView] 初始化失败:', error);
new Notice('预览视图初始化失败: ' +
(error instanceof Error ? error.message : String(error)));
// ✅ 显示友好的错误信息
const container = this.containerEl.children[1] as HTMLElement;
container.empty();
const errorDiv = container.createDiv({ cls: 'preview-error' });
errorDiv.createEl('h3', { text: '预览视图初始化失败' });
errorDiv.createEl('p', {
text: error instanceof Error ? error.message : String(error)
});
errorDiv.createEl('p', {
text: '请尝试重新加载插件或查看控制台获取更多信息'
});
}
}
效果:
- 即使初始化失败,也不会导致 Obsidian 卡住
- 显示友好的错误信息,方便用户排查问题
- 错误信息会打印到控制台,便于开发者调试
修复4: 添加 Notice 导入
文件:src/preview-view.ts
import { EventRef, ItemView, WorkspaceLeaf, Plugin, TFile, Notice } from 'obsidian';
🧪 测试验证
测试场景1:正常加载
- 启动 Obsidian
- 插件正常加载
- 预览视图正常显示
- 样式正确应用
测试场景2:主题不存在
- 设置中配置了不存在的主题名称
- 插件依然能正常加载
- 使用默认主题(第一个主题)
- 控制台显示警告信息
测试场景3:初始化失败
- 模拟某个组件初始化失败
- Obsidian 不会卡住
- 显示错误提示
- 用户可以继续使用 Obsidian
📝 预防措施
1. 防御性编程
// ✅ 好的做法:总是返回有效值
getTheme(themeName: string): Theme {
// ... 查找逻辑
return this.themes[0]; // 保底返回默认值
}
// ❌ 避免:可能返回 undefined
getTheme(themeName: string): Theme | undefined {
// ... 只在找到时返回
}
2. 优雅的错误处理
// ✅ 好的做法:捕获并处理错误
try {
await dangerousOperation();
} catch (error) {
console.error('操作失败:', error);
showUserFriendlyMessage();
}
// ❌ 避免:让错误传播导致卡住
await dangerousOperation(); // 如果失败会卡住整个应用
3. 使用配置而非硬编码
// ✅ 好的做法:使用用户配置
this.currentTheme = this.settings.defaultStyle;
// ❌ 避免:硬编码默认值
this.currentTheme = 'default';
📊 修复效果
| 问题 | 修复前 | 修复后 |
|---|---|---|
| Obsidian 卡在加载中 | ❌ 一直卡住 | ✅ 正常加载 |
| 样式加载失败错误 | ❌ 显示错误提示 | ✅ 使用默认主题 |
| 主题不存在时 | ❌ 插件崩溃 | ✅ 回退到默认主题 |
| 错误信息 | ❌ 无提示或卡住 | ✅ 友好的错误提示 |
| 调试信息 | ❌ 无日志 | ✅ 控制台警告 |
🔄 后续优化建议
-
添加主题验证
- 在设置保存时验证主题是否存在
- 提供主题选择下拉框,避免输入错误
-
改进错误提示
- 提供更详细的错误信息
- 添加解决方案建议
-
添加重试机制
- 初始化失败时提供重试按钮
- 自动重试资源加载
-
完善日志系统
- 统一的日志格式
- 日志级别控制(DEBUG, INFO, WARN, ERROR)
修复时间:2025年1月
影响范围:插件初始化流程
风险等级:低(只是添加容错处理)
测试状态:✅ 编译通过