# 性能优化

Doubao Apps SDK 应用的性能优化指南和最佳实践。

---

## ⚡ 渲染性能优化

### 1. 避免不必要的渲染

#### 使用 useMemo 缓存计算结果

```tsx
// ✅ 好的做法 - 缓存昂贵的计算
function DataList({ items, filter }: Props) {
  // 只在 items 或 filter 改变时重新计算
  const filteredAndSorted = useMemo(() => {
    console.log('Computing filtered list...');
    return items
      .filter(item => item.type === filter)
      .sort((a, b) => b.score - a.score);
  }, [items, filter]);

  return (
    <list>
      {filteredAndSorted.map(item => (
        <ItemCard key={item.id} {...item} />
      ))}
    </list>
  );
}

// ❌ 不好的做法 - 每次渲染都重新计算
function DataList({ items, filter }: Props) {
  // 每次组件渲染都会执行
  const filteredAndSorted = items
    .filter(item => item.type === filter)
    .sort((a, b) => b.score - a.score);

  return <list>{/* ... */}</list>;
}
```

#### 使用 useCallback 稳定函数引用

```tsx
// ✅ 好的做法 - 稳定的回调函数
function ParentComponent({ items }: Props) {
  const [selectedId, setSelectedId] = useState<string | null>(null);

  // 函数引用保持稳定，子组件不会因此重新渲染
  const handleSelect = useCallback((id: string) => {
    setSelectedId(id);
    console.log('Selected:', id);
  }, []);

  return (
    <view>
      {items.map(item => (
        <ItemCard
          key={item.id}
          item={item}
          onSelect={handleSelect}  // 函数引用不变
        />
      ))}
    </view>
  );
}

// ❌ 不好的做法 - 每次渲染创建新函数
function ParentComponent({ items }: Props) {
  const [selectedId, setSelectedId] = useState<string | null>(null);

  return (
    <view>
      {items.map(item => (
        <ItemCard
          key={item.id}
          item={item}
          onSelect={(id) => setSelectedId(id)}  // 每次创建新函数
        />
      ))}
    </view>
  );
}
```

### 2. 列表渲染优化

#### 虚拟列表

```tsx
// ✅ 好的做法 - 使用 Lynx list 组件的虚拟滚动
function VirtualList({ items }: { items: Item[] }) {
  return (
    <list
      className="virtual-list"
      list-type="single"
      span-count={1}
      scroll-orientation="vertical"
      enable-nested-scroll={true}
      // Lynx 会自动复用不可见的列表项
    >
      {items.map(item => (
        <list-item key={item.id} item-key={item.id}>
          <ItemCard item={item} />
        </list-item>
      ))}
    </list>
  );
}

// ❌ 不好的做法 - 渲染所有项
function AllItemsList({ items }: { items: Item[] }) {
  // 如果有 1000+ 项，会严重影响性能
  return (
    <scroll-view>
      {items.map(item => (
        <ItemCard key={item.id} item={item} />
      ))}
    </scroll-view>
  );
}
```

#### 分页加载

```tsx
// ✅ 好的做法 - 分页加载大量数据
function InfiniteList() {
  const [items, setItems] = useState<Item[]>([]);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const [loading, setLoading] = useState(false);

  const loadMore = async () => {
    if (loading || !hasMore) return;

    setLoading(true);
    try {
      const newItems = await fetchItems(page, 20);
      setItems(prev => [...prev, ...newItems]);
      setPage(prev => prev + 1);
      setHasMore(newItems.length === 20);
    } finally {
      setLoading(false);
    }
  };

  return (
    <list onScrollToLower={loadMore}>
      {items.map(item => (
        <list-item key={item.id}>
          <ItemCard item={item} />
        </list-item>
      ))}
      {loading && <LoadingIndicator />}
    </list>
  );
}
```

### 3. 图片优化

#### 懒加载

```tsx
// ✅ 好的做法 - 图片懒加载
function LazyImage({ src, placeholder }: Props) {
  const [loaded, setLoaded] = useState(false);
  const imageClassName = loaded
    ? 'lazy-image__actual lazy-image__actual--visible'
    : 'lazy-image__actual lazy-image__actual--hidden';

  return (
    <view className="lazy-image">
      {!loaded && <image src={placeholder} className="lazy-image__placeholder" />}
      <image
        src={src}
        className={imageClassName}
        onLoad={() => setLoaded(true)}
        onError={() => console.error('Failed to load image')}
      />
    </view>
  );
}
```

```scss
.lazy-image__actual--hidden {
  display: none;
}

.lazy-image__actual--visible {
  display: block;
}
```

#### 图片尺寸优化

```tsx
// ✅ 好的做法 - 使用合适尺寸的图片
interface ImageProps {
  src: string;
  size: 'thumbnail' | 'medium' | 'large';
}

function OptimizedImage({ src, size }: ImageProps) {
  const sizeParams = {
    thumbnail: '?w=200&h=200&q=80',
    medium: '?w=600&h=600&q=85',
    large: '?w=1200&h=1200&q=90'
  };

  return <image src={`${src}${sizeParams[size]}`} />;
}

// ❌ 不好的做法 - 加载原图后缩小
function UnoptimizedImage({ src }: Props) {
  // 加载 5MB 的原图，然后显示为 100x100
  return <image src={src} style={{ width: '100rpx', height: '100rpx' }} />;
}
```

---

## 🧠 内存优化

### 1. 及时清理资源

```tsx
// ✅ 好的做法 - 清理定时器和订阅
function TimerComponent() {
  const [count, setCount] = useState(0);
  const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);

  useEffect(() => {
    // 创建定时器
    timerRef.current = setInterval(() => {
      setCount(c => c + 1);
    }, 1000);

    // 清理函数
    return () => {
      if (timerRef.current) {
        clearInterval(timerRef.current);
        timerRef.current = null;
      }
    };
  }, []);

  return <text>Count: {count}</text>;
}

// ❌ 不好的做法 - 忘记清理
function TimerComponent() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setInterval(() => {
      setCount(c => c + 1);
    }, 1000);
    // 没有清理！组件卸载后定时器仍在运行
  }, []);

  return <text>Count: {count}</text>;
}
```

### 2. 避免内存泄漏

```tsx
// ✅ 好的做法 - 检查组件是否已卸载
function DataFetcher() {
  const [data, setData] = useState(null);
  const mountedRef = useRef(true);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const result = await api.getData();
        // 只在组件仍挂载时更新状态
        if (mountedRef.current) {
          setData(result);
        }
      } catch (error) {
        console.error(error);
      }
    };

    fetchData();

    return () => {
      mountedRef.current = false;
    };
  }, []);

  return <view>{/* render data */}</view>;
}
```

### 3. 大数据处理

```tsx
// ✅ 好的做法 - 分批处理大数据
async function processBigData<T, R>(
  data: T[],
  processBatch: (items: T[]) => Promise<R[]>
): Promise<R[]> {
  const BATCH_SIZE = 100;
  const results: R[] = [];

  for (let i = 0; i < data.length; i += BATCH_SIZE) {
    const batch = data.slice(i, i + BATCH_SIZE);
    const processed = await processBatch(batch);
    results.push(...processed);

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

  return results;
}

// ❌ 不好的做法 - 一次性处理所有数据
async function processBigData<T>(data: T[]) {
  // 如果数据量大，会阻塞 UI
  return data.map(item => expensiveOperation(item));
}
```

---

## 📦 包体积优化

### 1. 代码分割

```tsx
// ✅ 好的做法 - 动态导入大组件
type HeavyComponent = () => JSX.Element;

function HeavyFeature() {
  const [Component, setComponent] = useState<HeavyComponent | null>(null);

  useEffect(() => {
    // 只在需要时加载
    import('./HeavyComponent').then(module => {
      setComponent(() => module.default);
    });
  }, []);

  if (!Component) {
    return <LoadingSpinner />;
  }

  return <Component />;
}
```


## 📱 启动性能优化

### 1. 延迟非关键代码

```tsx
// ✅ 好的做法 - 优先渲染关键内容
import { useEffect, useState } from '@doubao-dev/framework';

export default function Page() {
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    void loadMainContent().finally(() => setLoading(false));
    const timer = setTimeout(() => {
      void loadAnalytics();
      void loadRecommendations();
    }, 1000);

    return () => clearTimeout(timer);
  }, []);

  return loading ? <Skeleton /> : <Content />;
}
```

### 2. 预加载关键资源

```tsx
// ✅ 好的做法 - 预加载下一页可能需要的数据
function ListPage({ items }: Props) {
  const [nextPageData, setNextPageData] = useState<Item[]>([]);

  useEffect(() => {
    // 预加载下一页数据
    const prefetchNextPage = async () => {
      try {
        const data = await fetchItems(2);
        setNextPageData(data);
      } catch (error) {
        console.error('Prefetch failed:', error);
      }
    };

    // 在用户可能操作前预加载
    const timer = setTimeout(prefetchNextPage, 2000);
    return () => clearTimeout(timer);
  }, []);

  return <ItemList items={items} />;
}
```

## 🔗 相关文档

- [最佳实践](./best-practices.md) - 代码规范
- [组件开发完整指南](./component-development.md) - Page 和 Widget 开发
- [故障排查](./troubleshooting.md) - 常见问题
