Back to Prompt Optimizer

开发经验总结

docs/archives/124-navigation-optimization/experience.md

2.10.213.0 KB
Original Source

开发经验总结

🎯 核心经验

1. 布局锚点策略 - 创新性解决方案

核心思想: 在动态布局中通过固定关键元素确保整体稳定性

应用场景:

  • 条件渲染的按钮组合
  • 模态切换的界面元素
  • 响应式布局中的关键组件

实施要点:

vue
<!-- ✅ 正确模式:锚点策略 -->
<!-- 条件元素放在锚点前 -->
<Button v-if="condition" />
<!-- 锚点元素始终渲染 -->
<Button class="layout-anchor" :class="{ active: condition }" />
<!-- 锚点后的元素位置稳定 -->
<Button />

<!-- ❌ 错误模式:条件渲染导致位移 -->
<Button />
<Button v-if="condition" />  <!-- 会影响后续元素位置 -->
<Button />

设计原则:

  • 锚点选择: 选择视觉权重适中、功能重要的元素
  • 状态表达: 通过CSS类而不是条件渲染表达状态
  • 位置策略: 条件元素放在锚点前,保护锚点后的布局

可复用性: 这个模式可应用于所有涉及条件显示的UI布局设计。

2. 功能分层设计理念

核心理念: 通过视觉权重区分功能重要性,优化用户认知负担

分层标准:

typescript
// 功能分层配置
const UI_LAYERS = {
  // 核心功能:用户主要操作路径
  core: {
    type: 'default',
    size: 'medium',
    ghost: false,
    weight: 'high'
  },
  
  // 辅助功能:设置和次要操作
  auxiliary: {
    type: 'quaternary', 
    size: 'small',
    ghost: true,
    weight: 'low'
  }
}

视觉权重控制:

  • 高权重: 饱和色彩、较大尺寸、实心按钮
  • 低权重: 淡化颜色、较小尺寸、透明背景

用户体验效果:

  • 降低界面认知复杂度
  • 引导用户关注主要功能
  • 保持次要功能的可访问性

3. 组件统一化最佳实践

统一原则: "一个功能,一个组件"

实施策略:

vue
<!-- ❌ 避免:混用不同组件 -->
<NButton>操作A</NButton>
<ActionButtonUI>操作B</ActionButtonUI>
<CustomButton>操作C</CustomButton>

<!-- ✅ 推荐:统一组件,配置区分 -->
<ActionButtonUI type="default">操作A</ActionButtonUI>
<ActionButtonUI type="secondary">操作B</ActionButtonUI>
<ActionButtonUI type="quaternary">操作C</ActionButtonUI>

配置标准化:

typescript
// 建立配置预设
const BUTTON_PRESETS = {
  navigation: {
    type: 'default',
    size: 'medium',
    ghost: false,
    round: true
  },
  auxiliary: {
    type: 'quaternary',
    size: 'small', 
    ghost: true
  }
}

长期收益:

  • 维护成本降低:只需维护一套组件逻辑
  • 样式一致性:避免微妙的视觉差异
  • 重构便利性:统一修改影响全局

4. 渐进式架构升级策略

核心思路: 在不破坏现有功能的基础上逐步改进架构

实施路径:

  1. 功能保持: 确保新架构100%兼容现有功能
  2. 平滑过渡: 保留旧组件导出,标记为deprecated
  3. 逐步替换: 在新功能中使用新组件,旧功能逐步迁移
  4. 最终清理: 确认无依赖后删除废弃组件

风险控制:

typescript
// 渐进式导出策略
export { default as LanguageSwitchDropdown } from './components/LanguageSwitchDropdown.vue'
export { 
  default as LanguageSwitch,
  /** @deprecated Use LanguageSwitchDropdown instead */
} from './components/LanguageSwitch.vue'

经验教训: 急于删除旧组件往往导致意外的依赖问题,渐进式升级更安全可靠。

🛠️ 技术实现经验

1. Naive UI组件深度集成

集成策略: 充分利用组件库能力,避免重复造轮子

最佳实践:

vue
<!-- ✅ 正确:利用NDropdown原生能力 -->
<NDropdown 
  :options="languageOptions"
  @select="handleLanguageSelect"
>
  <NButton quaternary>
    <template #icon>
      <span class="text-lg">🌐</span>
    </template>
  </NButton>
</NDropdown>

<!-- ❌ 避免:重新实现下拉逻辑 -->
<div class="custom-dropdown">
  <!-- 手动实现下拉菜单逻辑 -->
</div>

组件选型原则:

  • 功能匹配度: 组件功能是否满足需求
  • 扩展性: 是否支持未来功能扩展
  • 样式统一: 与整体设计语言的一致性
  • API稳定性: 组件接口是否稳定可靠

经验积累: Naive UI组件质量很高,大部分场景下直接使用比自定义实现更优。

2. Vue 3 Composition API应用经验

状态管理模式:

typescript
// ✅ 推荐:reactive + computed的清晰模式
const state = reactive({
  currentLanguage: 'zh-CN',
  availableLanguages: []
})

const languageOptions = computed(() => 
  state.availableLanguages.map(lang => ({
    key: lang.code,
    label: lang.name
  }))
)

// ❌ 避免:过度使用ref导致解包混乱
const currentLanguage = ref('zh-CN')
const availableLanguages = ref([])
const languageOptions = ref([])  // 手动维护衍生状态

生命周期使用:

typescript
// 服务注入和初始化的标准模式
const preferences = inject('preferenceService')

onMounted(async () => {
  // 组件挂载后初始化
  if (preferences) {
    const saved = await preferences.getLanguage()
    if (saved) {
      state.currentLanguage = saved
    }
  }
})

错误处理模式:

typescript
const handleLanguageSelect = async (key: string) => {
  try {
    // 业务逻辑
    setLocale(key)
    await preferences?.setLanguage(key)
  } catch (error) {
    // 用户友好的错误处理
    console.error('Language switch failed:', error)
    // 可选:显示错误提示
    message.error('语言切换失败,请重试')
  }
}

3. 响应式设计实现技巧

响应式策略: 组件内置响应式 > 媒体查询 > JavaScript动态计算

组件内置响应式:

vue
<!-- ✅ 最优:利用组件内置特性 -->
<ActionButtonUI 
  icon="⚙️"
  text="设置"
  <!-- 组件内部自动处理:max-md:hidden -->
/>

<!-- ✅ 可选:TailwindCSS媒体查询 -->
<span class="hidden md:inline">设置</span>

<!-- ❌ 避免:JavaScript动态控制 -->
<span v-if="!isMobile">设置</span>

断点设计原则:

css
/* 移动优先的断点策略 */
.navigation-button {
  /* 移动端基础样式 */
  
  @media (min-width: 768px) {
    /* 平板样式 */
  }
  
  @media (min-width: 1024px) { 
    /* 桌面样式 */
  }
}

测试覆盖策略: 确保在关键断点处进行实际设备测试。

4. TypeScript类型设计经验

接口设计原则:

typescript
// ✅ 清晰的接口定义
interface LanguageOption {
  key: string      // 必需:locale代码
  label: string    // 必需:显示名称
  flag?: string    // 可选:图标
}

interface LanguageSwitchProps {
  options?: LanguageOption[]  // 可选:默认使用内置选项
  showFlags?: boolean         // 可选:是否显示图标
}

// ❌ 避免:模糊的类型定义
interface SomeProps {
  data?: any
  config?: object
}

类型复用策略:

typescript
// 建立类型复用体系
export type ButtonType = 'default' | 'primary' | 'secondary' | 'tertiary' | 'quaternary'
export type ButtonSize = 'small' | 'medium' | 'large'

interface BaseButtonProps {
  type?: ButtonType
  size?: ButtonSize
  ghost?: boolean
}

🚫 避坑指南

1. 条件渲染布局陷阱

常见错误: 在布局关键位置使用v-if

vue
<!-- ❌ 危险:会导致布局跳动 -->
<div class="navigation">
  <Button>固定按钮1</Button>
  <Button v-if="condition">条件按钮</Button>  <!-- 位置不稳定 -->
  <Button>固定按钮2</Button>  <!-- 会随条件按钮跳动 -->
</div>

正确做法: 使用样式控制可见性或锚点策略

vue
<!-- ✅ 安全:保持DOM结构稳定 -->
<div class="navigation">
  <Button>固定按钮1</Button>
  <Button :class="{ invisible: !condition }">条件按钮</Button>
  <Button>固定按钮2</Button>  <!-- 位置稳定 -->
</div>

2. 组件导出清理误区

常见错误: 急于删除旧组件导出

typescript
// ❌ 危险:可能存在隐藏依赖
// export { default as OldComponent } from './OldComponent.vue'  // 直接删除

安全做法: 渐进式清理

typescript
// ✅ 安全:保留并标记deprecated
export { 
  default as OldComponent,
  /** @deprecated Use NewComponent instead. Will be removed in next major version. */
} from './OldComponent.vue'

清理检查清单:

  1. 全局搜索组件使用情况
  2. 检查测试文件中的引用
  3. 确认文档中无示例代码引用
  4. 验证构建过程无依赖

3. CSS权重冲突

常见问题: 主题切换时图标颜色被覆盖

css
/* 问题:全局CSS覆盖了组件样式 */
.icon {
  color: currentColor !important;  /* 过强的权重 */
}

解决策略:

vue
<!-- 方案1:内联样式优先级最高 -->
<NIcon :style="{ color: iconColor }">

<!-- 方案2:更具体的CSS选择器 -->
<NIcon class="theme-icon">
css
.theme-icon {
  color: var(--theme-color) !important;
}

4. 响应式测试盲区

常见遗漏: 只在浏览器开发工具中测试响应式

javascript
// ❌ 不充分:仅模拟器测试
browser.setViewportSize({ width: 375, height: 812 })

完整测试: 真实设备验证

javascript
// ✅ 完整:多设备尺寸 + 真实设备测试
const testSizes = [
  { width: 375, height: 812, name: 'iPhone' },
  { width: 768, height: 1024, name: 'iPad' },
  { width: 1920, height: 1080, name: 'Desktop' }
]

// 额外:真实设备测试
// 1. iPhone实际测试
// 2. Android设备测试  
// 3. 不同浏览器测试

5. 国际化文本长度陷阱

问题: 不同语言文本长度差异巨大

vue
<!-- 问题:德语文本可能比中文长3倍 -->
<Button>{{ $t('nav.settings') }}</Button>
<!-- 中文:"设置" (2字符) -->
<!-- 德语:"Einstellungen" (12字符) -->

解决方案:

vue
<!-- CSS处理文本溢出 -->
<Button class="nav-button">
  {{ $t('nav.settings') }}
</Button>
css
.nav-button {
  min-width: 120px;      /* 为长文本预留空间 */
  text-overflow: ellipsis; /* 溢出显示省略号 */
  overflow: hidden;
}

🔄 架构设计经验

1. 跨包组件统一模式

设计目标: 减少代码重复,统一维护入口

实施模式: "单一源码,多端部署"

bash
# Web版本(主实现)
packages/web/src/App.vue

# Extension版本(复用)  
cp packages/web/src/App.vue packages/extension/src/App.vue

# 好处:
# 1. 统一的bug修复
# 2. 一致的功能更新
# 3. 降低维护成本

适用场景判断:

  • ✅ 界面逻辑相同的跨平台应用
  • ✅ 功能需求99%重叠的组件
  • ❌ 平台特定功能较多的场景
  • ❌ 性能要求差异很大的情况

2. 组件层次架构设计

分层原则:

UI组件库 (Naive UI)
    ↓
封装组件层 (ActionButtonUI, LanguageSwitchDropdown)
    ↓  
业务组件层 (App.vue, MainLayout)
    ↓
页面应用层 (Web, Extension, Desktop)

职责划分:

  • UI组件库: 提供基础交互能力
  • 封装组件: 统一样式和行为规范
  • 业务组件: 实现具体功能逻辑
  • 页面应用: 组织整体用户体验

设计收益:

  • 清晰的依赖关系
  • 便于单独测试和维护
  • 支持逐层优化和替换

3. 配置驱动的扩展设计

核心思想: 通过配置而非代码修改支持功能扩展

语言扩展示例:

typescript
// ✅ 配置驱动:添加新语言只需修改配置
const AVAILABLE_LANGUAGES = [
  { key: 'zh-CN', label: '简体中文', flag: '🇨🇳' },
  { key: 'en-US', label: 'English', flag: '🇺🇸' },
  { key: 'ja-JP', label: '日本語', flag: '🇯🇵' }  // 新增
]

// ❌ 硬编码:添加新语言需要修改多处代码
const toggleLanguage = () => {
  if (current === 'zh-CN') return 'en-US'
  if (current === 'en-US') return 'ja-JP'
  if (current === 'ja-JP') return 'zh-CN'
}

扩展点设计原则:

  • 数据驱动: 功能变化通过数据配置体现
  • 接口稳定: 扩展不影响现有API
  • 向后兼容: 新功能不破坏旧版本

4. 错误边界和降级策略

容错设计: 组件在异常情况下的行为

vue
<template>
  <!-- 主要功能 -->
  <LanguageSwitchDropdown v-if="servicesReady" />
  
  <!-- 降级功能 -->
  <NButton v-else disabled>
    {{ $t('common.loading') }}
  </NButton>
</template>

<script>
// 错误处理
const handleLanguageSwitch = async (lang) => {
  try {
    await switchLanguage(lang)
  } catch (error) {
    // 降级:不阻断用户操作,记录错误
    console.error('Language switch failed, using client fallback')
    useClientOnlyLanguageSwitch(lang)
  }
}
</script>

降级策略制定:

  • 功能降级: 核心功能失败时的替代方案
  • 样式降级: CSS失效时的基础可用性
  • 服务降级: 外部服务失败时的本地处理

经验总结: 本项目通过系统化的设计和实施,不仅解决了具体的用户体验问题,更重要的是建立了一套可复用的设计模式和最佳实践。这些经验可以直接应用于后续的UI优化工作,显著提升开发效率和产品质量。

核心价值: 从单点问题解决升华为系统性能力建设,为团队积累了宝贵的技术资产。