Files
note2any/docs/ARCHITECTURE_QUICK_REFERENCE.md
2025-10-16 16:26:39 +08:00

25 KiB
Raw Blame History

Note2Any v1.4.0 架构快速参考指南

重大更新: v1.4.0引入了全新的模块化核心系统,本文档为新架构的快速参考。

相关文档: 详细架构文档 | 升级对比

📋 新文件结构

src/
├── main.ts                    # 插件入口 (集成核心模块)
├── preview-view.ts            # Obsidian 视图容器 (增强版)
├── preview-manager.ts         # 中央调度器 ★
├── platform-chooser.ts       # 平台选择器
├── article-render.ts          # 内容渲染 (重构中)
├── core/                      # 🆕 核心模块系统
│   ├── error-handler.ts       # 统一错误处理
│   ├── progress-indicator.ts  # 进度反馈系统
│   ├── config-manager.ts      # 配置管理中心
│   ├── publisher-interface.ts # 发布平台抽象
│   ├── publisher-manager.ts   # 发布管理器
│   ├── content-processor.ts   # 内容处理流水线
│   ├── gallery-processor.ts   # 图库处理器
│   ├── image-processor.ts     # 图像处理引擎
│   └── html-processor.ts      # HTML生成器
├── wechat/
│   └── wechat-preview.ts      # 微信预览
└── xiaohongshu/
    └── xhs-preview.ts         # 小红书预览

🎯 核心模块职责 (v1.4.0 新增)

核心支撑层

ErrorHandler - 统一错误处理

职责

  • 全局错误捕获和分类
  • 用户友好的错误提示
  • 错误日志记录和分析
  • 错误恢复策略

关键方法

ErrorHandler.handle(error: Error, context: string): void
ErrorHandler.log(level: LogLevel, message: string): void
ErrorHandler.getUserFriendlyMessage(error: Error): string

使用示例

try {
    await risky_operation();
} catch (error) {
    ErrorHandler.handle(error, 'MyModule.doSomething');
    // 用户将看到友好的错误提示
}

ProgressIndicator - 进度反馈系统

职责

  • 长时间操作的进度反馈
  • 统一的用户状态提示
  • 可视化进度显示

关键方法

progress.start(message: string): void
progress.update(message: string, progress?: number): void
progress.finish(message: string): void
progress.error(message: string): void

使用示例

const progress = new ProgressIndicator();
progress.start('处理图片');
progress.update('上传第1张图片', 25);
progress.update('上传第2张图片', 50);
progress.finish('图片处理完成');

ConfigManager - 配置管理中心

职责

  • 中心化配置管理
  • 运行时配置验证
  • 配置变更通知
  • 类型安全的配置访问

关键方法

ConfigManager.initialize(settings: NMPSettings): void
ConfigManager.getInstance(): ConfigManager
config.get<T>(key: string): T
config.set<T>(key: string, value: T): void

使用示例

const config = ConfigManager.getInstance();
const theme = config.get<string>('defaultStyle');
config.set('enableDebug', true);

发布平台层

PublisherInterface & PublisherManager

职责

  • 统一的平台发布接口
  • 平台无关的业务逻辑
  • 可扩展的平台架构

平台接口

interface IPlatformPublisher {
    id: string;
    name: string;
    initialize(config: PlatformConfig): Promise<void>;
    publish(content: PublishContent): Promise<PublishResult>;
    uploadImage(image: ImageData): Promise<string>;
    validateConfig(): ValidationResult;
}

使用示例

const publisherManager = PublisherManager.getInstance();
const result = await publisherManager.publishTo('wechat', content);

内容处理层

ContentProcessor - 内容处理流水线

职责

  • 模块化内容处理
  • 可配置的处理管道
  • 支持自定义扩展

处理器接口

interface IContentProcessor {
    id: string;
    priority: number;
    process(content: string, context: ProcessContext): Promise<string>;
}

使用示例

const processor = new ContentProcessor();
processor.addProcessor(new GalleryProcessor());
processor.addProcessor(new ImageProcessor());
const result = await processor.process(markdown);

ImageProcessor - 图像处理引擎

职责

  • WebP转JPG转换
  • 批量图片处理
  • 微信图片上传
  • HTML转PNG功能

关键方法

async convertWebpToJpg(data: ArrayBuffer): Promise<ArrayBuffer>
async uploadToWechat(data: ArrayBuffer, filename: string, token: string): Promise<string>
async htmlToPng(element: HTMLElement): Promise<Blob>

GalleryProcessor - 图库处理器

职责

  • 图库短代码解析
  • 本地图片目录扫描
  • Wikilink格式转换

关键方法

async processGalleryShortcodes(content: string): Promise<string>

HtmlProcessor - HTML生成器

职责

  • HTML文档生成
  • CSS样式内联
  • 响应式设计优化
  • 移动端适配

关键方法

generateFullHtml(content: string, options: HtmlProcessOptions): string
optimizeForMobile(html: string): string

🔄 新架构调用关系图

PreviewView (Obsidian容器)
    │
    ├─ 集成 ─> ProgressIndicator (进度反馈)
    ├─ 集成 ─> ErrorHandler (错误处理)
    │
    └─ holds ─> PreviewManager (协调者)
                    │
                    ├─ 使用 ─> ConfigManager (配置管理)
                    ├─ 使用 ─> PublisherManager (发布管理)
                    ├─ 使用 ─> ContentProcessor (内容处理)
                    │              │
                    │              ├─ GalleryProcessor
                    │              ├─ ImageProcessor
                    │              └─ HtmlProcessor
                    │
                    ├─ creates ─> PlatformChooser
                    ├─ creates ─> WechatPreview
                    └─ creates ─> XhsPreview

📝 常见任务示例 (v1.4.0)

1. 添加新的内容处理器

步骤一:实现处理器接口

// src/processors/my-processor.ts
import { IContentProcessor, ProcessContext } from '../core/content-processor';

export class MyContentProcessor implements IContentProcessor {
    id = 'my-processor';
    priority = 100; // 优先级,数字越小越先执行
    
    async process(content: string, context: ProcessContext): Promise<string> {
        // 实现自定义处理逻辑
        const processed = content.replace(/\{\{custom\}\}/g, '<span class="custom">Custom Content</span>');
        return processed;
    }
}

步骤二:注册处理器

// src/main.ts 或相关初始化文件
import { MyContentProcessor } from './processors/my-processor';

const contentProcessor = ContentProcessor.getInstance();
contentProcessor.addProcessor(new MyContentProcessor());

2. 添加新平台支持

步骤一:实现平台发布接口

// src/platforms/my-platform-publisher.ts
import { IPlatformPublisher, PlatformConfig, PublishContent, PublishResult } from '../core/publisher-interface';

export class MyPlatformPublisher implements IPlatformPublisher {
    id = 'my-platform';
    name = 'My Platform';
    
    async initialize(config: PlatformConfig): Promise<void> {
        // 平台初始化逻辑
        const progress = new ProgressIndicator();
        progress.start('初始化平台连接');
        
        try {
            // 验证配置、建立连接等
            progress.finish('平台初始化完成');
        } catch (error) {
            progress.error('平台初始化失败');
            ErrorHandler.handle(error, 'MyPlatformPublisher.initialize');
            throw error;
        }
    }
    
    async publish(content: PublishContent): Promise<PublishResult> {
        const progress = new ProgressIndicator();
        progress.start('发布内容');
        
        try {
            // 发布逻辑实现
            progress.finish('发布成功');
            return { success: true, id: 'published-id' };
        } catch (error) {
            progress.error('发布失败');
            ErrorHandler.handle(error, 'MyPlatformPublisher.publish');
            throw error;
        }
    }
    
    async uploadImage(image: ImageData): Promise<string> {
        // 图片上传逻辑
        return 'uploaded-image-url';
    }
    
    validateConfig(): ValidationResult {
        // 配置验证逻辑
        return { valid: true };
    }
}

步骤二:注册平台

// src/main.ts
import { MyPlatformPublisher } from './platforms/my-platform-publisher';

const publisherManager = PublisherManager.getInstance();
publisherManager.registerPublisher(new MyPlatformPublisher());

3. 使用统一错误处理

在模块中使用

export class MyModule {
    async doSomething() {
        const progress = new ProgressIndicator();
        progress.start('执行操作');
        
        try {
            // 可能出错的操作
            await riskyOperation();
            progress.finish('操作完成');
        } catch (error) {
            progress.error('操作失败');
            ErrorHandler.handle(error as Error, 'MyModule.doSomething');
            // 错误已被处理,用户已看到友好提示
            throw error; // 可选:继续抛出供上层处理
        }
    }
}

4. 配置管理最佳实践

读取配置

const config = ConfigManager.getInstance();

// 类型安全的配置访问
const theme = config.get<string>('defaultStyle');
const enableDebug = config.get<boolean>('enableDebug');
const timeout = config.get<number>('requestTimeout');

监听配置变更

config.onUpdate((updatedConfig) => {
    console.log('配置已更新:', updatedConfig);
    // 响应配置变更
});

🐛 调试技巧 (v1.4.0)

1. 查看模块状态

在浏览器控制台中:

// 查看错误处理器状态
console.log('错误统计:', ErrorHandler.getErrorStats());

// 查看配置管理器状态
const config = ConfigManager.getInstance();
console.log('当前配置:', config.getAll());

// 查看发布管理器状态
const publisherManager = PublisherManager.getInstance();
console.log('可用平台:', publisherManager.listAvailablePublishers());

// 查看内容处理器状态
const contentProcessor = ContentProcessor.getInstance();
console.log('已注册处理器:', contentProcessor.getProcessors());

2. 启用调试模式

// 在开发环境中启用详细日志
const config = ConfigManager.getInstance();
config.set('enableDebug', true);
config.set('logLevel', 'debug');

3. 错误追踪

// 注册错误监听器
ErrorHandler.onError((error, context) => {
    console.log(`错误发生在: ${context}`, error);
    // 可以发送到错误监控服务
});

⚠️ 注意事项 (v1.4.0)

1. 模块初始化顺序

// ✅ 正确的初始化顺序
await ErrorHandler.initialize();
await ConfigManager.initialize(settings);
await PublisherManager.initialize();
await ContentProcessor.initialize();

// ❌ 错误ConfigManager 未初始化就使用)
const config = ConfigManager.getInstance(); // 可能抛出错误

2. 错误处理最佳实践

// ✅ 正确(使用统一错误处理)
try {
    await operation();
} catch (error) {
    ErrorHandler.handle(error, 'Module.method');
}

// ❌ 错误(绕过错误处理系统)
try {
    await operation();
} catch (error) {
    console.error(error); // 用户不会收到友好提示
}

3. 进度反馈规范

// ✅ 正确(完整的进度生命周期)
const progress = new ProgressIndicator();
progress.start('开始操作');
try {
    progress.update('步骤1', 25);
    await step1();
    progress.update('步骤2', 50);
    await step2();
    progress.finish('操作完成');
} catch (error) {
    progress.error('操作失败');
    throw error;
}

// ❌ 错误(没有结束进度指示器)
const progress = new ProgressIndicator();
progress.start('开始操作');
await operation(); // 如果出错,进度指示器会一直显示

🚀 性能优化建议

1. 模块懒加载

// 按需加载重型模块
const imageProcessor = await import('./core/image-processor');
const processor = new imageProcessor.ImageProcessor();

2. 缓存优化

// 利用配置管理器的缓存
const config = ConfigManager.getInstance();
const cachedTheme = config.get('currentTheme'); // 自动缓存

3. 批量操作

// 使用批量处理API
const imageProcessor = new ImageProcessor();
const results = await imageProcessor.processImages(imageList, options);

📚 相关文档


架构版本v1.4.0 (模块化核心系统)
最后更新2025年10月16日

🎯 各文件职责

preview-view.ts

角色Obsidian 视图容器
职责

  • 实现 ItemView 接口
  • 管理视图生命周期
  • 监听 Obsidian 事件
  • 委托业务逻辑给 PreviewManager

关键方法

async onOpen()          // 视图打开
async onClose()         // 视图关闭
async setFile(file)     // 设置文件
async refresh()         // 刷新预览

preview-manager.ts ★

角色:中央调度器(核心)
职责

  • 创建和管理所有子组件
  • 协调平台切换
  • 管理文件渲染
  • 统一对外接口

关键方法

async build()                              // 构建界面
private switchPlatform(platform)           // 平台切换(唯一入口)
async setFile(file)                        // 设置文件
async refresh()                            // 刷新预览
private renderForWechat(file)              // 渲染微信
private renderForXiaohongshu(file)         // 渲染小红书
destroy()                                  // 清理资源

创建流程

constructor(container, app, render)
  
async build()
  ├─ createPlatformChooser()
  ├─ createWechatPreview()
  └─ createXiaohongshuPreview()

platform-chooser.ts

角色:平台选择 UI 组件
职责

  • 渲染平台选择下拉框
  • 处理用户选择事件
  • 触发平台切换回调

关键方法

render()                           // 渲染 UI
setOnChange(callback)              // 设置回调
switchPlatform(platform)           // 程序化切换
getCurrentPlatform()               // 获取当前平台

使用示例

const chooser = new PlatformChooser(container);
chooser.setOnChange((platform) => {
    console.log('切换到:', platform);
});
chooser.render();

wechat/wechat-preview.ts

角色:微信公众号预览实现
职责

  • 渲染微信专属工具栏
  • 处理微信相关操作
  • 管理微信公众号配置

关键方法

build()                       // 构建 UI
show()                        // 显示
hide()                        // 隐藏
updateStyleAndHighlight()     // 更新样式
destroy()                     // 清理

// 待实现
uploadImages()                // 上传图片
postArticle()                 // 发布草稿
exportHTML()                  // 导出 HTML

xiaohongshu/xhs-preview.ts

角色:小红书预览实现
职责

  • 渲染小红书专属界面
  • 处理分页和切图
  • 管理小红书样式

关键方法

build()                                  // 构建 UI
show()                                   // 显示
hide()                                   // 隐藏
async renderArticle(html, file)          // 渲染文章
destroy()                                // 清理

🔄 调用关系图

PreviewView (视图容器)
    │
    ├─ holds ─> PreviewManager (协调者)
    │               │
    │               ├─ creates ─> PlatformChooser
    │               │                  │
    │               │                  └─ onChange callback ─┐
    │               │                                         │
    │               ├─ creates ─> WechatPreview              │
    │               │                  │                      │
    │               │                  ├─ onRefreshCallback ─┤
    │               │                  └─ onAppIdChange ─────┤
    │               │                                         │
    │               └─ creates ─> XhsPreview                 │
    │                              │                          │
    │                              ├─ onRefreshCallback ─────┤
    │                              ├─ onPublishCallback ─────┤
    │                              └─ onPlatformChange ──────┤
    │                                                         │
    └──────────────────────────── all callbacks handled ─────┘

📝 常见任务示例

1. 添加新平台(如抖音)

步骤一:创建预览组件

// src/douyin/douyin-preview.ts
export class DouyinPreview {
    container: HTMLElement;
    app: any;
    
    constructor(container: HTMLElement, app: any) {
        this.container = container;
        this.app = app;
    }
    
    build(): void {
        // 构建抖音专属 UI
    }
    
    show(): void {
        this.container.style.display = 'flex';
    }
    
    hide(): void {
        this.container.style.display = 'none';
    }
    
    destroy(): void {
        // 清理资源
    }
}

步骤二:添加到支持列表

// platform-chooser.ts
const SUPPORTED_PLATFORMS: PlatformInfo[] = [
    { value: 'wechat', label: '微信公众号', icon: '📱' },
    { value: 'xiaohongshu', label: '小红书', icon: '📔' },
    { value: 'douyin', label: '抖音', icon: '🎵' }  // ← 新增
];

// 更新类型定义
export type PlatformType = 'wechat' | 'xiaohongshu' | 'douyin';

步骤三:集成到 PreviewManager

// preview-manager.ts
import { DouyinPreview } from './douyin/douyin-preview';

export class PreviewManager {
    private douyinPreview: DouyinPreview | null = null;
    
    private createDouyinPreview(): void {
        const container = this.mainDiv!.createDiv({ cls: 'douyin-preview-container' });
        this.douyinPreview = new DouyinPreview(container, this.app);
        this.douyinPreview.build();
    }
    
    private async switchPlatform(platform: PlatformType): Promise<void> {
        // ... 现有代码
        
        if (platform === 'douyin') {
            this.showDouyin();
            this.hideWechat();
            this.hideXiaohongshu();
            
            if (this.currentFile) {
                await this.renderForDouyin(this.currentFile);
            }
        }
    }
    
    private async renderForDouyin(file: TFile): Promise<void> {
        // 实现抖音渲染逻辑
    }
}

2. 修改平台切换逻辑

位置preview-manager.ts
方法switchPlatform()

private async switchPlatform(platform: PlatformType): Promise<void> {
    console.log(`切换平台: ${this.currentPlatform}${platform}`);
    
    const previousPlatform = this.currentPlatform;
    this.currentPlatform = platform;
    
    // 更新 UI
    this.platformChooser?.switchPlatform(platform);
    
    // 根据平台显示/隐藏
    if (platform === 'wechat') {
        this.showWechat();
        this.hideXiaohongshu();
        // 如果需要,重新渲染
        if (this.currentFile && previousPlatform !== 'wechat') {
            await this.renderForWechat(this.currentFile);
        }
    } else if (platform === 'xiaohongshu') {
        this.showXiaohongshu();
        this.hideWechat();
        if (this.currentFile && previousPlatform !== 'xiaohongshu') {
            await this.renderForXiaohongshu(this.currentFile);
        }
    }
}

3. 添加新的回调函数

场景:在微信预览中添加新的操作按钮

步骤一:在 WechatPreview 中定义回调

// wechat-preview.ts
export class WechatPreview {
    onCustomActionCallback?: () => Promise<void>;
    
    private buildToolbar() {
        // ... 现有代码
        
        const customBtn = lineDiv.createEl('button', { 
            text: '自定义操作', 
            cls: 'toolbar-button' 
        });
        customBtn.onclick = async () => {
            if (this.onCustomActionCallback) {
                await this.onCustomActionCallback();
            }
        };
    }
}

步骤二:在 PreviewManager 中设置回调

// preview-manager.ts
private createWechatPreview(): void {
    // ... 现有代码
    
    this.wechatPreview.onCustomActionCallback = async () => {
        await this.handleCustomAction();
    };
}

private async handleCustomAction(): Promise<void> {
    // 实现自定义操作逻辑
    console.log('执行自定义操作');
}

4. 监听文件变化

位置preview-view.ts
方法registerEventListeners()

private registerEventListeners(): void {
    // 监听文件切换
    this.listeners.push(
        this.app.workspace.on('file-open', async (file: TFile | null) => {
            if (this.manager) {
                await this.manager.setFile(file);
            }
        })
    );

    // 监听文件修改
    this.listeners.push(
        this.app.vault.on('modify', async (file) => {
            if (file instanceof TFile) {
                const currentFile = this.manager?.getCurrentFile();
                if (currentFile && currentFile.path === file.path) {
                    await this.manager?.refresh();
                }
            }
        })
    );
    
    // 添加新的事件监听
    this.listeners.push(
        this.app.workspace.on('your-custom-event', async (data) => {
            // 处理自定义事件
        })
    );
}

🐛 调试技巧

1. 查看平台切换流程

preview-manager.ts 中添加日志:

private async switchPlatform(platform: PlatformType): Promise<void> {
    console.log(`[PreviewManager] 切换平台: ${this.currentPlatform}${platform}`);
    console.log('[PreviewManager] 当前文件:', this.currentFile?.path);
    
    // ... 现有代码
    
    console.log('[PreviewManager] 平台切换完成');
}

2. 检查组件状态

在浏览器控制台中:

// 查看所有预览视图
app.workspace.getLeavesOfType('note-preview')

// 获取 PreviewView 实例
const leaf = app.workspace.getLeavesOfType('note-preview')[0]
const previewView = leaf.view

// 查看 PreviewManager 状态(通过 private 访问需要技巧)
console.log(previewView.manager)

3. 断点调试

在关键方法中设置断点:

  • PreviewManager.switchPlatform()
  • PreviewManager.setFile()
  • PreviewManager.renderForWechat()
  • PreviewManager.renderForXiaohongshu()

⚠️ 注意事项

1. 回调函数必须在构建前设置

// ✅ 正确
this.wechatPreview = new WechatPreview(...);
this.wechatPreview.onRefreshCallback = async () => { ... };
this.wechatPreview.build();

// ❌ 错误(回调可能不会生效)
this.wechatPreview = new WechatPreview(...);
this.wechatPreview.build();
this.wechatPreview.onRefreshCallback = async () => { ... };

2. 平台切换不要直接修改 currentPlatform

// ❌ 错误(绕过了协调逻辑)
previewManager.currentPlatform = 'xiaohongshu';

// ✅ 正确(通过 switchPlatform
await previewManager.switchPlatform('xiaohongshu');

3. 清理资源

在组件销毁时必须清理资源:

destroy(): void {
    // 清理 DOM 引用
    this.container = null as any;
    
    // 清理子组件
    this.wechatPreview?.destroy();
    this.xhsPreview?.destroy();
    
    // 清理回调
    this.onRefreshCallback = undefined;
}

📚 相关文档


最后更新2025年1月
架构版本v2.0(引入 PreviewManager