nutpi
nutpi
  • 发布:2025-10-21 19:47
  • 更新:2025-10-21 19:47
  • 阅读:237

从零到一:使用 uni-app x 开发鸿蒙 GitCode 目录树生成器

分类:鸿蒙Next

从零到一:使用 uni-app x 开发鸿蒙 GitCode 目录树生成器

GitCodeTree Logo

作者: 徐建国

📖 目录

🎯 项目背景

起源故事

作为一名开发者,我经常需要在文档、博客和技术分享中展示项目的目录结构。传统的方法是手动使用 tree 命令或者写脚本生成,但这种方式有几个痛点:

  1. 不够直观:命令行操作对非技术人员不友好
  2. 缺乏灵活性:难以快速调整显示层级和过滤规则
  3. 移动端受限:在手机上无法方便地查看和分享
  4. 重复劳动:每次都要重新生成,没有历史记录

于是,我决定开发一个跨平台的移动端应用,让目录树生成变得简单、快速、优雅。

image-20251021194818129

需求分析

核心需求

  • ✅ 快速生成 GitCode 项目的目录树结构
  • ✅ 支持自定义显示深度和过滤规则
  • ✅ 一键复制到剪贴板,方便分享
  • ✅ 本地安全存储访问令牌

进阶需求

  • 🔄 深色模式支持
  • 📱 原生体验和流畅交互
  • 🎨 现代化的 UI 设计
  • 🔒 安全的数据管理

🛠️ 技术选型

为什么选择 uni-app x?

在技术选型阶段,我对比了多个跨平台框架:

框架 优势 劣势 适配性
uni-app x 原生性能、TypeScript、一次开发多端运行 生态相对较新 ⭐⭐⭐⭐⭐
React Native 生态成熟、组件丰富 性能略差、包体积大 ⭐⭐⭐⭐
Flutter 性能优秀、UI 精美 Dart 学习成本、包体积大 ⭐⭐⭐⭐
原生开发 性能最佳 开发成本高、维护困难 ⭐⭐⭐

最终选择 uni-app x 的原因

  1. 原生性能:基于原生渲染,性能接近原生应用
  2. TypeScript 支持:强类型带来更好的开发体验
  3. 一次开发,多端运行:同时支持 iOS、Android、HarmonyOS
  4. 开发效率高:Vue 语法简洁,开发速度快
  5. HarmonyOS 支持:未来趋势,提前布局

技术栈组成

┌─────────────────────────────────────┐  
│          应用层 (Application)        │  
│    GitCodeTree - 目录树生成器        │  
└─────────────────────────────────────┘  
                 ↓  
┌─────────────────────────────────────┐  
│          框架层 (Framework)          │  
│   uni-app x + Vue 3 + TypeScript    │  
└─────────────────────────────────────┘  
                 ↓  
┌─────────────────────────────────────┐  
│          API 层 (API Service)        │  
│        GitCode REST API v5          │  
└─────────────────────────────────────┘  
                 ↓  
┌─────────────────────────────────────┐  
│          平台层 (Platform)           │  
│  iOS / Android / HarmonyOS          │  
└─────────────────────────────────────┘

核心技术

  • 前端框架: uni-app x (Vue 3)
  • 编程语言: TypeScript / UTS
  • 数据请求: uni.request API
  • 状态管理: uni.storage (本地持久化)
  • UI 组件: 原生组件 + 自定义样式

🏗️ 架构设计

整体架构

采用单页面应用 (SPA) + 组件化的架构模式:

pages/  
└── gittree/  
    └── gittree.uvue          # 主页面组件  
        ├── Template          # 视图层  
        ├── Script            # 逻辑层  
        └── Style             # 样式层

数据流设计

用户输入  
  ↓  
输入验证  
  ↓  
解析项目信息 (owner/repo)  
  ↓  
API 请求  
  ├── 获取项目信息  
  └── 递归获取目录结构  
      ↓  
数据处理  
  ├── 过滤 (仅文件夹/完整)  
  ├── 深度控制 (1-5层/全部)  
  └── 格式化 (树形文本)  
      ↓  
UI 渲染  
  ├── 项目信息卡片  
  └── 目录树展示  
      ↓  
用户操作  
  ├── 复制到剪贴板  
  └── 下载/分享

组件结构

<GitTreePage>  
│  
├── <Header>                  # 顶部导航  
│   ├── Logo  
│   └── 主题切换按钮  
│  
├── <TokenSection>            # Token 配置区  
│   ├── 输入框  
│   ├── 保存按钮  
│   └── 提示信息  
│  
├── <MainSection>             # 主功能区  
│   ├── 项目输入框  
│   ├── 历史记录列表 (新)  
│   ├── 高级选项  
│   │   ├── 深度选择器  
│   │   └── 视图类型选择器  
│   ├── 生成按钮  
│   └── 测试按钮  
│  
├── <ProjectInfo>             # 项目信息卡片  
│   ├── 项目名称  
│   ├── 描述  
│   └── 统计信息  
│  
└── <DirectoryTree>           # 目录树展示  
    ├── 树形文本  
    └── 复制按钮

💻 核心功能实现

1. GitCode API 集成

API 认证

GitCode 使用 Personal Access Token (PAT) 进行身份验证:

// API 请求头配置  
const headers = {  
  'Authorization': `Bearer ${this.token}`,  
  'Accept': 'application/json',  
  'Content-Type': 'application/json'  
}

关键点

  • Token 必须在请求头中以 Bearer 前缀传递
  • 需要确保 Token 具有 user_infoprojects 权限
  • Token 存储在本地,使用 uni.setStorageSync 持久化

获取项目信息

async getProjectInfo(owner: string, repo: string) {  
  return new Promise((resolve, reject) => {  
    uni.request({  
      url: `https://api.gitcode.com/api/v5/repos/${owner}/${repo}`,  
      method: 'GET',  
      header: {  
        'Authorization': `Bearer ${this.token}`,  
        'Accept': 'application/json'  
      },  
      success: (res) => {  
        if (res.statusCode === 200) {  
          resolve(res.data)  
        } else {  
          reject(new Error(this.getErrorMessage(res.statusCode)))  
        }  
      },  
      fail: (err) => {  
        reject(new Error('网络连接失败,请检查网络设置'))  
      }  
    })  
  })  
}

错误处理策略

getErrorMessage(statusCode: number): string {  
  const errorMap: Record<number, string> = {  
    400: '请求参数错误',  
    401: '访问令牌无效或已过期,请重新配置',  
    403: '没有访问权限,请检查令牌权限设置',  
    404: '项目不存在,请检查项目路径',  
    429: '请求过于频繁,请稍后再试',  
    500: 'GitCode 服务器错误',  
    503: 'GitCode 服务暂时不可用'  
  }  
  return errorMap[statusCode] || `请求失败 (${statusCode})`  
}

2. 递归目录树生成

这是项目的核心算法,需要:

  • 递归遍历所有子目录
  • 支持深度限制
  • 处理异步请求
  • 优化性能

递归算法实现

async getProjectDirectory(  
  owner: string,  
  repo: string,  
  path: string = '',  
  currentDepth: number = 0,  
  maxDepth: number = 0  
): Promise<any[]> {  
  // 深度限制检查  
  if (maxDepth > 0 && currentDepth >= maxDepth) {  
    return []  
  }  

  try {  
    // 获取当前目录内容  
    const data: any = await this.fetchDirectoryContent(owner, repo, path)  

    if (!Array.isArray(data)) {  
      return []  
    }  

    // 并行处理所有子目录  
    const promises = data.map(async (item: any) => {  
      if (item.type === 'dir') {  
        item.children = await this.getProjectDirectory(  
          owner,  
          repo,  
          item.path,  
          currentDepth + 1,  
          maxDepth  
        )  
      }  
      return item  
    })  

    return await Promise.all(promises)  
  } catch (error) {  
    console.error(`获取目录失败: ${path}`, error)  
    return []  
  }  
}

性能优化点

  1. 并行请求:使用 Promise.all 同时处理多个子目录
  2. 深度限制:避免无限递归,减少 API 调用
  3. 错误隔离:单个目录失败不影响其他目录
  4. 缓存机制:同一项目避免重复请求(计划中)

时间复杂度分析

假设项目有 N 个目录,平均每个目录有 M 个子项:

  • 串行方式: O(N) - 总请求数 = N
  • 并行方式: O(log N) - 实际时间大幅减少
  • 深度限制: 最多 O(M^D) 其中 D 是最大深度

3. 树形文本格式化

生成美观的 ASCII 树形结构:

generateTreeText(  
  items: any[],  
  prefix: string = '',  
  isRoot: boolean = true,  
  viewType: string = 'all'  
): string {  
  let text = ''  

  // 过滤项目(如果只显示文件夹)  
  const filteredItems = viewType === 'folders'   
    ? items.filter(item => item.type === 'dir')  
    : items  

  filteredItems.forEach((item, index) => {  
    const isLast = index === filteredItems.length - 1  
    const connector = isLast ? '└── ' : '├── '  
    const icon = item.type === 'dir' ? '📁' : '📄'  

    // 构建当前行  
    text += `${prefix}${connector}${icon} ${item.name}\n`  

    // 递归处理子目录  
    if (item.type === 'dir' && item.children?.length > 0) {  
      const newPrefix = prefix + (isLast ? '    ' : '│   ')  
      text += this.generateTreeText(  
        item.children,  
        newPrefix,  
        false,  
        viewType  
      )  
    }  
  })  

  return text  
}

输出示例

📁 项目名称  
├── 📁 src  
│   ├── 📁 components  
│   │   ├── 📄 Button.vue  
│   │   └── 📄 Input.vue  
│   ├── 📁 pages  
│   │   └── 📄 Home.vue  
│   └── 📄 main.ts  
├── 📁 static  
│   └── 📄 logo.png  
└── 📄 package.json

4. 本地存储管理

使用 uni.storage API 实现数据持久化:

// 保存 Token  
saveToken() {  
  if (!this.token.trim()) {  
    this.showError('请输入访问令牌')  
    return  
  }  

  try {  
    uni.setStorageSync('gitcode_token', this.token)  
    this.isTokenSaved = true  
    this.showSuccess('访问令牌已保存')  
  } catch (error) {  
    this.showError('保存失败,请重试')  
  }  
}  

// 加载 Token  
loadToken() {  
  try {  
    const savedToken = uni.getStorageSync('gitcode_token')  
    if (savedToken) {  
      this.token = savedToken  
      this.isTokenSaved = true  
    }  
  } catch (error) {  
    console.error('加载 Token 失败', error)  
  }  
}  

// 保存主题设置  
saveTheme() {  
  uni.setStorageSync('theme', this.isDarkMode ? 'dark' : 'light')  
}  

// 加载主题设置  
loadTheme() {  
  const savedTheme = uni.getStorageSync('theme')  
  this.isDarkMode = savedTheme === 'dark'  
}

存储结构

LocalStorage  
├── gitcode_token        # GitCode 访问令牌  
├── theme               # 主题设置 (light/dark)  
├── project_history     # 项目历史记录 (新增)  
└── favorites           # 收藏项目 (计划中)

5. 用户体验优化

Toast 通知

替换传统的错误/成功消息显示:

// 错误提示  
showError(message: string) {  
  uni.showToast({  
    title: message,  
    icon: 'error',  
    duration: 3000  
  })  
  uni.vibrateShort() // 震动反馈  
}  

// 成功提示  
showSuccess(message: string) {  
  uni.showToast({  
    title: message,  
    icon: 'success',  
    duration: 2000  
  })  
  uni.vibrateShort()  
}

项目历史记录

实现智能输入建议:

// 数据结构  
data() {  
  return {  
    projectHistory: [] as string[],  // 历史记录列表  
    showHistory: false                // 控制显示  
  }  
}  

// 添加到历史  
addToHistory(project: string) {  
  // 移除重复项  
  const index = this.projectHistory.indexOf(project)  
  if (index !== -1) {  
    this.projectHistory.splice(index, 1)  
  }  

  // 添加到最前面  
  this.projectHistory.unshift(project)  

  // 限制最多 10 条  
  if (this.projectHistory.length > 10) {  
    this.projectHistory.pop()  
  }  

  this.saveHistory()  
}  

// 选择历史项  
selectHistoryItem(item: string) {  
  this.projectInput = item  
  this.showHistory = false  
}

触觉反馈

在关键操作点添加震动反馈:

// 复制成功  
copyTree() {  
  uni.setClipboardData({  
    data: this.directoryTree,  
    success: () => {  
      this.showSuccess('已复制到剪贴板')  
      uni.vibrateShort({ type: 'light' }) // 轻震动  
    }  
  })  
}  

// 生成完成  
async generateTree() {  
  // ... 生成逻辑 ...  

  this.showSuccess('生成成功')  
  uni.vibrateShort({ type: 'medium' }) // 中等震动  
}

🚀 性能优化

1. 并行请求优化

问题:串行请求导致大型项目生成时间过长

解决方案:使用 Promise.all 并行处理

// ❌ 串行方式 (慢)  
for (let item of items) {  
  if (item.type === 'dir') {  
    item.children = await getDirectory(item.path)  
  }  
}  

// ✅ 并行方式 (快)  
const promises = items.map(async (item) => {  
  if (item.type === 'dir') {  
    item.children = await getDirectory(item.path)  
  }  
  return item  
})  
await Promise.all(promises)

性能提升

  • 小型项目 (< 10 目录): 提升 30-50%
  • 中型项目 (10-50 目录): 提升 50-70%
  • 大型项目 (> 50 目录): 提升 70-85%

2. 深度控制优化

提供深度选项,避免不必要的请求:

const depthOptions = [  
  { label: '1层', value: 1 },  
  { label: '2层', value: 2 },  
  { label: '3层(推荐)', value: 3 },  
  { label: '4层', value: 4 },  
  { label: '5层', value: 5 },  
  { label: '全部', value: 0 }  
]

API 调用次数对比

假设每层平均 5 个子目录:

深度 API 调用次数 用时估算
1层 ~1 次 < 1s
2层 ~6 次 1-2s
3层 ~31 次 3-5s
4层 ~156 次 10-15s
5层 ~781 次 30-60s
全部 不确定 可能很长

建议

  • 快速预览:使用 2-3 层
  • 完整文档:使用 3-4 层
  • 详尽分析:使用全部(小心使用)

3. UI 渲染优化

虚拟滚动(计划中)

对于超大目录树,使用虚拟滚动:

// 只渲染可见区域的节点  
<scroll-view   
  scroll-y   
  :scroll-into-view="scrollIntoView"  
  @scroll="onScroll"  
>  
  <view v-for="item in visibleItems" :key="item.id">  
    {{ item.content }}  
  </view>  
</scroll-view>

分块加载

对于大型项目,分块显示:

// 分块渲染,避免卡顿  
async renderTree() {  
  const chunkSize = 100  
  let index = 0  

  while (index < this.treeLines.length) {  
    const chunk = this.treeLines.slice(index, index + chunkSize)  
    this.displayedTree += chunk.join('\n')  
    index += chunkSize  

    // 让出主线程,避免阻塞 UI  
    await new Promise(resolve => setTimeout(resolve, 0))  
  }  
}

🎨 UI/UX 设计

设计原则

遵循 Material DesigniOS Human Interface Guidelines

  1. 简洁直观:减少操作步骤,核心功能 3 步完成
  2. 视觉层次:使用卡片、阴影、颜色区分功能区
  3. 即时反馈:每个操作都有明确的视觉和触觉反馈
  4. 一致性:保持 UI 风格和交互模式统一

色彩系统

/* 主色调 - 渐变紫色 */  
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);  
--primary-color: #667eea;  
--primary-dark: #764ba2;  

/* 功能色 */  
--success-color: #10b981;  /* 成功/正面 */  
--error-color: #ef4444;    /* 错误/危险 */  
--warning-color: #f59e0b;  /* 警告 */  
--info-color: #3b82f6;     /* 信息 */  

/* 中性色 */  
--text-primary: #1a202c;   /* 主要文字 */  
--text-secondary: #718096; /* 次要文字 */  
--bg-primary: #ffffff;     /* 主背景 */  
--bg-secondary: #f8fafc;   /* 次背景 */  
--border-color: #e2e8f0;   /* 边框 */

响应式布局

/* 基础单位 rpx (responsive pixel) */  
/* 1rpx = 屏幕宽度 / 750 */  

.card {  
  margin: 20rpx 30rpx;  
  padding: 40rpx;  
  border-radius: 20rpx;  
}  

.input-field {  
  width: 100%;  
  height: 80rpx;  
  padding: 0 30rpx;  
  font-size: 28rpx;  
}  

/* 适配不同屏幕 */  
@media (max-width: 375px) {  
  .card {  
    margin: 15rpx 20rpx;  
    padding: 30rpx;  
  }  
}

交互动画

/* 按钮按下效果 */  
.btn:active {  
  transform: scale(0.98);  
  opacity: 0.9;  
}  

/* 卡片展开动画 */  
.card-content {  
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);  
  max-height: 0;  
  overflow: hidden;  
}  

.card-content.show {  
  max-height: 1000rpx;  
}  

/* 加载动画 */  
@keyframes spin {  
  from { transform: rotate(0deg); }  
  to { transform: rotate(360deg); }  
}  

.loading-icon {  
  animation: spin 1s linear infinite;  
}

🐛 踩坑经验

1. 滚动问题

问题描述:页面无法滚动,内容超出屏幕时无法查看

原因分析

  • uni-app x 中,<view> 组件默认不支持滚动
  • 需要使用 <scroll-view> 组件

解决方案

<!-- ❌ 错误写法 -->  
<view class="container">  
  <!-- 大量内容 -->  
</view>  

<!-- ✅ 正确写法 -->  
<scroll-view class="page" scroll-y="true">  
  <view class="container">  
    <!-- 大量内容 -->  
  </view>  
</scroll-view>

CSS 配置

.page {  
  width: 100%;  
  height: 100vh;  /* 必须设置高度 */  
  background-color: #f8fafc;  
}

2. 异步请求错误处理

问题描述:API 请求失败时,应用崩溃或无响应

原因分析

  • 没有正确处理 Promise 的 reject
  • 错误信息没有传递到 UI 层

解决方案

// ❌ 错误写法  
async getProjectInfo(owner, repo) {  
  const res = await uni.request({ /* ... */ })  
  return res.data  
}  

// ✅ 正确写法  
async getProjectInfo(owner, repo) {  
  return new Promise((resolve, reject) => {  
    uni.request({  
      url: `...`,  
      success: (res) => {  
        if (res.statusCode === 200) {  
          resolve(res.data)  
        } else {  
          reject(new Error(this.getErrorMessage(res.statusCode)))  
        }  
      },  
      fail: (err) => {  
        reject(new Error('网络连接失败'))  
      }  
    })  
  })  
}  

// 调用时使用 try-catch  
try {  
  const info = await this.getProjectInfo(owner, repo)  
  // 处理成功  
} catch (error) {  
  this.showError(error.message)  
}

3. TypeScript 类型问题

问题描述:uni-app x 的 API 类型定义不完整

解决方案

// 定义扩展类型  
interface UniRequestResponse {  
  statusCode: number  
  data: any  
  header: Record<string, any>  
  cookies: string[]  
}  

// 使用类型断言  
const res = await uni.request({ /* ... */ }) as UniRequestResponse  

// 或定义全局类型  
declare global {  
  interface Uni {  
    request(options: UniRequestOptions): Promise<UniRequestResponse>  
  }  
}

4. 存储同步问题

问题描述:多次快速操作导致数据丢失

原因分析

  • 异步存储没有等待完成
  • 并发写入导致数据覆盖

解决方案

// ❌ 错误写法  
saveHistory() {  
  uni.setStorage({  
    key: 'history',  
    data: this.history  
  })  
}  

// ✅ 正确写法(同步)  
saveHistory() {  
  try {  
    uni.setStorageSync('history', JSON.stringify(this.history))  
  } catch (error) {  
    console.error('保存失败', error)  
  }  
}  

// ✅ 或使用异步 + 防抖  
let saveTimer: number | null = null  

saveHistory() {  
  if (saveTimer) clearTimeout(saveTimer)  

  saveTimer = setTimeout(() => {  
    uni.setStorage({  
      key: 'history',  
      data: JSON.stringify(this.history),  
      success: () => console.log('保存成功'),  
      fail: (err) => console.error('保存失败', err)  
    })  
  }, 500)  
}

5. 递归深度限制

问题描述:深度过大导致调用栈溢出或请求超时

解决方案

// 添加深度限制和超时控制  
async getProjectDirectory(  
  owner: string,  
  repo: string,  
  path: string = '',  
  currentDepth: number = 0,  
  maxDepth: number = 5,  // 默认限制  
  timeout: number = 30000 // 30秒超时  
) {  
  // 深度检查  
  if (maxDepth > 0 && currentDepth >= maxDepth) {  
    return []  
  }  

  // 超时控制  
  const timeoutPromise = new Promise((_, reject) => {  
    setTimeout(() => reject(new Error('请求超时')), timeout)  
  })  

  const fetchPromise = this.fetchDirectoryContent(owner, repo, path)  

  try {  
    const data = await Promise.race([fetchPromise, timeoutPromise])  
    // 继续处理...  
  } catch (error) {  
    if (error.message === '请求超时') {  
      this.showError('获取目录超时,请尝试减小深度')  
    }  
    return []  
  }  
}

6. JSON 配置文件注释

问题描述pages.json 中的注释导致解析错误

原因:JSON 标准不支持注释

解决方案

// ❌ 错误写法  
{  
  "pages": [  
    // 这是首页  
    {  
      "path": "pages/index/index"  
    }  
  ]  
}  

// ✅ 正确写法  
{  
  "pages": [  
    {  
      "path": "pages/index/index"  
    }  
  ]  
}

📊 数据与性能

测试环境

  • 设备: mate 60 pro(鸿蒙6)
  • 测试项目: 中型开源项目 (约 50 个目录,200 个文件)

性能指标

操作 时间 优化后 提升
初始加载 0.8s 0.6s 25%
Token 保存 0.1s 0.05s 50%
API 连接测试 1.2s 0.9s 25%
生成目录树 (3层) 4.5s 2.8s 38%
生成目录树 (全部) 18s 10s 44%
复制到剪贴板 0.2s 0.1s 50%

内存占用

场景 内存占用 峰值
启动应用 45 MB 60 MB
生成小型树 50 MB 70 MB
生成大型树 80 MB 120 MB
长时间运行 55 MB 85 MB

优化措施

  • ✅ 及时清理临时变量
  • ✅ 分块处理大数据
  • ✅ 避免内存泄漏
  • 🔄 实现虚拟滚动(计划中)

🔐 安全性考虑

Token 安全

存储安全

// 使用 uni.storage 本地加密存储  
uni.setStorageSync('gitcode_token', this.token)  

// 未来计划:使用设备密钥加密  
import crypto from 'crypto'  

function encryptToken(token: string, key: string): string {  
  const cipher = crypto.createCipher('aes-256-cbc', key)  
  let encrypted = cipher.update(token, 'utf8', 'hex')  
  encrypted += cipher.final('hex')  
  return encrypted  
}

传输安全

  • ✅ 使用 HTTPS 协议
  • ✅ Token 仅在请求头传递
  • ✅ 不在 URL 中暴露 Token

使用建议

  • 🔐 定期更换 Token
  • 🔐 为应用单独生成 Token
  • 🔐 限制 Token 权限范围
  • 🔐 不要分享 Token

数据隐私

  • 本地处理:所有数据处理在本地完成
  • 无数据上传:不向第三方服务器发送数据
  • 权限最小化:仅请求必要的 API 权限
  • 透明度:开源代码,可审计

🎓 最佳实践总结

1. 代码组织

// ✅ 良好的代码结构  
export default {  
  data() {  
    // 1. 基础数据  
    // 2. UI 状态  
    // 3. 业务数据  
  },  

  onLoad() {  
    // 页面加载时的初始化  
  },  

  methods: {  
    // 1. 用户交互方法  
    // 2. API 请求方法  
    // 3. 数据处理方法  
    // 4. 工具方法  
  }  
}

2. 错误处理

// ✅ 完善的错误处理  
try {  
  const result = await this.apiCall()  
  this.handleSuccess(result)  
} catch (error) {  
  // 记录错误  
  console.error('操作失败:', error)  

  // 用户友好的提示  
  this.showError(this.getUserFriendlyMessage(error))  

  // 恢复 UI 状态  
  this.resetUIState()  
}

3. 用户体验

// ✅ 完整的用户反馈流程  
async performAction() {  
  // 1. 显示加载状态  
  this.isLoading = true  

  try {  
    // 2. 执行操作  
    const result = await this.doSomething()  

    // 3. 成功反馈  
    this.showSuccess('操作成功')  
    uni.vibrateShort()  

    // 4. 更新 UI  
    this.updateUI(result)  
  } catch (error) {  
    // 5. 错误反馈  
    this.showError(error.message)  
  } finally {  
    // 6. 清理状态  
    this.isLoading = false  
  }  
}

4. 性能优化

// ✅ 性能优化技巧  

// 1. 防抖  
const debouncedSearch = debounce(this.search, 300)  

// 2. 节流  
const throttledScroll = throttle(this.onScroll, 100)  

// 3. 懒加载  
const lazyLoadImages = () => {  
  // 仅加载可见区域图片  
}  

// 4. 缓存  
const cache = new Map()  
async function fetchWithCache(key) {  
  if (cache.has(key)) {  
    return cache.get(key)  
  }  
  const data = await fetch(key)  
  cache.set(key, data)  
  return data  
}

🚀 未来规划

v1.1.0 - 用户体验增强(开发中)

  • [x] Toast 通知替代传统提示
  • [x] 触觉反馈优化
  • [x] 项目历史记录
  • [ ] 深色模式完善
  • [ ] 收藏夹功能
  • [ ] 下拉刷新

v1.2.0 - 功能扩展(规划中)

  • [ ] 目录树导出为图片
  • [ ] 精美分享卡片
  • [ ] 项目搜索/过滤
  • [ ] 自定义样式主题
  • [ ] 批量处理项目

v1.3.0 - 高级功能(未来)

  • [ ] 离线缓存机制
  • [ ] Token 加密存储
  • [ ] 生物识别认证
  • [ ] 多语言支持
  • [ ] 云同步功能

v2.0.0 - 架构升级(愿景)

  • [ ] 支持更多 Git 平台(GitHub)
  • [ ] 插件系统
  • [ ] 自定义脚本
  • [ ] AI 智能分析项目结构
  • [ ] 协作功能

📚 参考资源

官方文档

相关项目

学习资源


💭 个人思考

技术选型的权衡

选择 uni-app x 是一个大胆的决定。它相对较新,生态还在完善中,但它的跨平台能力和原生性能让我觉得这是一个值得投资的技术方向。

在开发过程中,我深刻体会到:

  • 没有完美的技术,只有最适合的选择
  • 跨平台不是银弹,但能显著提高效率
  • 用户体验永远是第一位的

开发中的收获

  1. 深入理解异步编程:递归 + Promise 的组合让我对异步有了更深的认识
  2. API 设计的重要性:良好的 API 设计能让开发事半功倍
  3. 性能优化是持续的过程:不要过早优化,但也要时刻关注性能
  4. 用户反馈很重要:很多优化点都来自真实用户的反馈

给开发者的建议

  1. 从小做起:先实现核心功能,再逐步完善
  2. 注重细节:小的体验改进能带来大的满意度提升
  3. 持续学习:技术在不断进步,保持学习的热情
  4. 开源分享:分享你的代码,帮助更多人

🎉 总结

GitCodeTree 是我第一个使用 uni-app x 开发的完整应用,从技术选型到最终发布,整个过程充满挑战和收获。

核心成果

  • ✅ 实现了完整的跨平台目录树生成功能
  • ✅ 性能优化达到 40% 以上的提升
  • ✅ 用户体验优化,操作流畅自然
  • ✅ 代码结构清晰,易于维护和扩展

技术亮点

  • 🎯 递归算法 + 并行优化
  • 🎯 完善的错误处理机制
  • 🎯 优雅的 UI/UX 设计
  • 🎯 安全的数据存储

经验教训

  • 💡 性能优化要基于实际场景
  • 💡 用户体验细节决定产品质量
  • 💡 完善的错误处理能避免很多问题
  • 💡 持续迭代比一次完美更重要

📞 联系我

如果你对这个项目感兴趣,或者有任何问题和建议,欢迎联系我:


<div align="center">

⭐ 如果这篇文章对你有帮助,请给项目一个 Star!⭐

📖 更多技术文章,敬请期待!

GitCodeTree Logo

5 关注 分享
DCloud_CHB 刘星 唐家三少 DCloud_UNI_OttoJi DCloud_云服务_JRP

要回复文章请先登录注册