update at 2025-10-09 12:39:24

This commit is contained in:
douboer
2025-10-09 12:39:24 +08:00
parent a891153be0
commit 6f51916b50
44 changed files with 332 additions and 226 deletions

View File

@@ -0,0 +1,311 @@
# 修复Obsidian 加载卡住和样式加载失败问题
## 🐛 问题描述
**症状1**Obsidian 一直处于"加载工作区中"状态
**症状2**:进入安全模式后关闭安全模式,插件能加载但提示:
> "获取样式失败defaultldefault请检查主题是否正确安装。"
## 🔍 根本原因分析
### 问题1: `getTheme()` 和 `getHighlight()` 返回 undefined
**位置**`src/assets.ts`
```typescript
// 原代码
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`
```typescript
constructor(app, itemView, styleEl, articleDiv) {
// ...
this._currentTheme = 'default'; // ❌ 硬编码
this._currentHighlight = 'default'; // ❌ 硬编码
}
```
**问题**
- 不使用用户配置的默认主题 `settings.defaultStyle`
- 如果主题列表中没有名为 'default' 的主题,就会失败
### 问题3: preview-view.ts 没有设置 ArticleRender 的主题
**位置**`src/preview-view.ts`
```typescript
get render(): ArticleRender {
if (!this._articleRender) {
this._articleRender = new ArticleRender(...);
// ❌ 没有设置 currentTheme 和 currentHighlight
}
return this._articleRender;
}
```
### 问题4: 初始化失败时没有错误处理
**位置**`src/preview-view.ts`
```typescript
async onOpen(): Promise<void> {
// ❌ 如果初始化失败,会导致 Obsidian 一直卡在加载中
await this.initializeSettings();
await this.createManager();
// ...
}
```
## ✅ 修复方案
### 修复1: 为 getTheme() 和 getHighlight() 添加默认返回值
**文件**`src/assets.ts`
```typescript
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`
```typescript
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`
```typescript
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`
```typescript
import { EventRef, ItemView, WorkspaceLeaf, Plugin, TFile, Notice } from 'obsidian';
```
## 🧪 测试验证
### 测试场景1正常加载
1. 启动 Obsidian
2. 插件正常加载
3. 预览视图正常显示
4. 样式正确应用
### 测试场景2主题不存在
1. 设置中配置了不存在的主题名称
2. 插件依然能正常加载
3. 使用默认主题(第一个主题)
4. 控制台显示警告信息
### 测试场景3初始化失败
1. 模拟某个组件初始化失败
2. Obsidian 不会卡住
3. 显示错误提示
4. 用户可以继续使用 Obsidian
## 📝 预防措施
### 1. 防御性编程
```typescript
// ✅ 好的做法:总是返回有效值
getTheme(themeName: string): Theme {
// ... 查找逻辑
return this.themes[0]; // 保底返回默认值
}
// ❌ 避免:可能返回 undefined
getTheme(themeName: string): Theme | undefined {
// ... 只在找到时返回
}
```
### 2. 优雅的错误处理
```typescript
// ✅ 好的做法:捕获并处理错误
try {
await dangerousOperation();
} catch (error) {
console.error('操作失败:', error);
showUserFriendlyMessage();
}
// ❌ 避免:让错误传播导致卡住
await dangerousOperation(); // 如果失败会卡住整个应用
```
### 3. 使用配置而非硬编码
```typescript
// ✅ 好的做法:使用用户配置
this.currentTheme = this.settings.defaultStyle;
// ❌ 避免:硬编码默认值
this.currentTheme = 'default';
```
## 📊 修复效果
| 问题 | 修复前 | 修复后 |
|------|--------|--------|
| **Obsidian 卡在加载中** | ❌ 一直卡住 | ✅ 正常加载 |
| **样式加载失败错误** | ❌ 显示错误提示 | ✅ 使用默认主题 |
| **主题不存在时** | ❌ 插件崩溃 | ✅ 回退到默认主题 |
| **错误信息** | ❌ 无提示或卡住 | ✅ 友好的错误提示 |
| **调试信息** | ❌ 无日志 | ✅ 控制台警告 |
## 🔄 后续优化建议
1. **添加主题验证**
- 在设置保存时验证主题是否存在
- 提供主题选择下拉框,避免输入错误
2. **改进错误提示**
- 提供更详细的错误信息
- 添加解决方案建议
3. **添加重试机制**
- 初始化失败时提供重试按钮
- 自动重试资源加载
4. **完善日志系统**
- 统一的日志格式
- 日志级别控制DEBUG, INFO, WARN, ERROR
---
**修复时间**2025年1月
**影响范围**:插件初始化流程
**风险等级**:低(只是添加容错处理)
**测试状态**:✅ 编译通过