Q20 · Web3 场景优化

你会如何处理"交易发起了,但链上还没确认"的前端体验和性能问题?

乐观更新交易状态UX

⚡ 速记答案(30 秒)

  • 使用乐观更新(optimistic UI):本地先把订单状态标记成"提交中"或"待确认",UI 立即反馈
  • 监听链上事件 / 轮询交易状态:成功 → 更新状态为"已成交/成功",失败 → 回滚本地状态 + 提示
  • 性能上:不要在 UI 主流程里阻塞等待交易确认
  • 链上确认逻辑放到后台任务 / 统一管理的 tx manager 中处理

📖 详细讲解

乐观更新模式


用户操作 → 立即更新 UI → 后台发送交易 → 监听结果 → 同步最终状态

交易状态机


idle → pending → submitted → confirming → confirmed/failed

实现要点


1. UI 响应

• 立即显示"处理中"状态

• 禁用重复提交

• 显示进度指示


2. 后台处理

• 交易队列管理

• 超时处理

• 重试策略


3. 状态同步

• 成功:更新本地状态

• 失败:回滚 + 提示用户

• 超时:提供查询入口


注意事项


• 不要阻塞主线程

• 处理网络中断

• 考虑交易加速/取消

💻 代码示例

交易管理器
type TxStatus = 'pending' | 'submitted' | 'confirmed' | 'failed';

interface Transaction {
  id: string;
  hash?: string;
  status: TxStatus;
  data: any;
}

class TransactionManager {
  private transactions = new Map<string, Transaction>();
  private listeners = new Set<(tx: Transaction) => void>();

  // 发起交易 - 乐观更新
  async submit(id: string, sendTx: () => Promise<string>) {
    // 1. 立即标记为 pending
    this.updateTx(id, { status: 'pending', data: {} });
    
    try {
      // 2. 发送交易
      const hash = await sendTx();
      this.updateTx(id, { status: 'submitted', hash });
      
      // 3. 后台等待确认
      this.waitForConfirmation(id, hash);
    } catch (error) {
      // 发送失败 - 回滚
      this.updateTx(id, { status: 'failed' });
    }
  }

  private async waitForConfirmation(id: string, hash: string) {
    try {
      const receipt = await provider.waitForTransaction(hash);
      this.updateTx(id, { 
        status: receipt.status === 1 ? 'confirmed' : 'failed' 
      });
    } catch {
      this.updateTx(id, { status: 'failed' });
    }
  }

  private updateTx(id: string, updates: Partial<Transaction>) {
    const tx = { ...this.transactions.get(id), ...updates } as Transaction;
    this.transactions.set(id, tx);
    this.listeners.forEach(fn => fn(tx));
  }
}
💡
面试技巧:回答性能问题时,先说指标和标准,再讲优化手段,最后结合实际项目经验。