# Lynx 前端开发最佳实践

## 性能优化

### 布局性能

#### 1. 选择合适的布局类型

```css
/* ✅ 简单列表使用 Linear（性能最好） */
.simple-list {
  /* 默认就是 linear，无需设置 display */
  padding: 16px;
}

/* ✅ 复杂弹性布局使用 Flex */
.complex-layout {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
}

/* ❌ 避免在简单场景使用 Flex */
.simple-column {
  display: flex;
  flex-direction: column; /* 不如直接用 Linear */
}
```

#### 2. 减少布局层级

```jsx
// ❌ 层级过多
<view className="wrapper">
  <view className="container">
    <view className="inner">
      <view className="content">
        <text>内容</text>
      </view>
    </view>
  </view>
</view>

// ✅ 扁平化结构
<view className="content">
  <text>内容</text>
</view>
```

#### 3. 避免动态改变布局类型

```jsx
// ❌ 避免在运行时切换 display 类型
const [isGrid, setIsGrid] = useState(false);

<view style={{ display: isGrid ? 'grid' : 'flex' }}>
  {/* 内容 */}
</view>

// ✅ 使用 opacity 或 transform 替代
<view className="flex-container">
  <view className="flex-view" style={{ opacity: isGrid ? 0 : 1 }} />
  <view className="grid-view" style={{ opacity: isGrid ? 1 : 0 }} />
</view>
```

### 样式计算优化

#### 4. 使用 CSS 类而非内联样式

```jsx
// ❌ 内联样式每次渲染都重新计算
<view style={{
  width: 100,
  height: 200,
  backgroundColor: '#fff'
}} />

// ✅ CSS 类只解析一次
<view className="card" />
```

```css
.card {
  width: 100px;
  height: 200px;
  background-color: #fff;
}
```

#### 5. 避免复杂的 CSS 选择器

```css
/* ❌ 复杂选择器增加匹配开销 */
.container > .wrapper .item:nth-child(2n + 1) .content {
  color: red;
}

/* ✅ 使用简单类选择器 */
.item-odd {
  color: red;
}
```

#### 6. GPU 加速优化

```css
/* ✅ 使用 transform 和 opacity 实现自动 GPU 加速 */
.animated-element {
  transition: transform 0.3s ease, opacity 0.3s ease;
}

/* ✅ 动画结束后无需手动清理，Lynx 自动管理 */
```

> **注意**：Lynx 会自动对 `transform` 和 `opacity` 属性启用 GPU 加速，无需手动声明。

### 渲染优化

#### 7. 图片优化

```css
/* ✅ 指定图片尺寸，避免布局抖动 */
.product-image {
  width: 200px;
  height: 200px;
  /* 或者只设置一边，保持比例 */
  width: 100%;
  aspect-ratio: 16 / 9;
}
```

```jsx
// ✅ 使用懒加载
<list>
  {items.map((item) => (
    <list-item key={item.id}>
      <image src={item.image} lazy-load={true} placeholder="placeholder.png" />
    </list-item>
  ))}
</list>
```

#### 8. 列表优化

```jsx
// ✅ 使用 list 组件替代 scroll-view + 多个 view
<list className="item-list" scroll-y>
  {items.map((item) => (
    <list-item key={item.id} item-key={item.id}>
      <view className="item">
        <text>{item.title}</text>
      </view>
    </list-item>
  ))}
</list>
```

```css
/* ✅ 为 list-item 设置固定高度（如果可以） */
list-item {
  height: 80px;
}
```

#### 9. 控制同时渲染的元素数量

```jsx
// ❌ 一次渲染太多元素
{
  items.map((item) => <HeavyComponent key={item.id} data={item} />);
}

// ✅ 分页或虚拟列表
<list>
  {visibleItems.map((item) => (
    <list-item key={item.id}>
      <HeavyComponent data={item} />
    </list-item>
  ))}
</list>;
```

## 布局最佳实践

### Flex 布局模式

#### 10. 响应式布局

```css
/* 移动端/默认 */
.container {
  display: flex;
  flex-direction: column;
}

/* 注意：Lynx 不支持 @media，请使用 JavaScript 动态调整或视口单位 */
/* JavaScript: if (viewportWidth >= 768) { setLayout('row') } */
```

#### 11. 弹性项比例

```css
/* ✅ 使用 flex 简写 */
.item {
  flex: 1; /* flex-grow: 1, flex-shrink: 1, flex-basis: 0% */
}

.item-large {
  flex: 2; /* 占据两倍空间 */
}

/* ❌ 不推荐单独设置 */
.item {
  flex-grow: 1;
  flex-shrink: 1;
  /* 缺少 flex-basis 可能导致意外行为 */
}
```

#### 12. 间距处理

```css
/* ✅ 使用 gap 属性 */
.grid {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

/* 传统方式（需要处理边距） */
.grid-item {
  width: calc(33.33% - 11px);
  margin-right: 16px;
  margin-bottom: 16px;
}

.grid-item:nth-child(3n) {
  margin-right: 0;
}
```

### Grid 布局模式

**注意**：Lynx **不支持** `@media` 媒体查询。

## 样式组织

### CSS 架构

#### 15. BEM 命名约定

```css
/* Block */
.card {
}

/* Element */
.card__title {
}
.card__content {
}
.card__button {
}

/* Modifier */
.card--featured {
}
.card__button--primary {
}
.card__button--disabled {
}
```

#### 16. CSS 变量（主题系统）

完整的主题变量（颜色、间距、字体、圆角、阴影）及暗色主题切换示例见 `patterns/theming.md`。使用 CSS 变量管理主题有以下建议：

- **命名规范**: `--category-property` 格式（如 `--color-primary`）
- **分层定义**: 颜色、间距、字体分开定义
- **语义化**: 使用 `--color-primary` 而非 `--color-red`
- **默认值**: 提供 fallback: `var(--prop, default)`

#### 17. 组件样式隔离

```css
/* 每个组件独立文件 */
/* Button.css */
.button {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px 24px;
}

/* Card.css */
.card {
  display: flex;
  flex-direction: column;
  padding: 16px;
}

/* 避免全局污染 */
/* ❌ 不推荐 */
view {
  box-sizing: border-box;
}

/* ✅ 使用类选择器 */
.reset-box {
  box-sizing: border-box;
}
```

## 响应式设计

### 适配策略

#### 18. 使用 rem + vw 适配（推荐）

> **推荐方案**：使用 `rem` 配合 `vw` 设置根字体大小，实现响应式适配。

```css
/* 在根元素设置基准字体大小 */
page {
  font-size: calc(100vw / 23.4375); /* 1rem = 16px @ 375px 宽度 */
}

/* 使用 rem 进行屏幕适配 */
.container {
  width: 100%; /* 全宽 */
  padding: 2rem; /* 左右各 2rem ≈ 32px @ 375px */
}

.card {
  width: 21.4rem; /* 约一半宽度，减去间距 */
  margin-bottom: 1.5rem;
}
```

**rpx 说明**：`rpx` 是 Lynx 特有单位，功能完整且自动适配屏幕宽度，但缺乏 Web 兼容性。使用 `rem` + `vw` 是更标准的跨平台响应式方案。

#### 19. 安全区域适配

```css
/* iPhone 刘海屏适配 */
.safe-area {
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
}

/* 底部固定按钮 */
.fixed-bottom {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  padding-bottom: env(safe-area-inset-bottom);
  background-color: #fff;
}
```

#### 20. 字体适配

⚠️ **注意**：Lynx **不支持** `@media`，请使用以下替代方案：

**方案 1：使用 rem（推荐）**

```css
/* 在根元素设置基准字体大小 */
page {
  font-size: calc(100vw / 23.4375); /* 1rem = 16px @ 375px 宽度 */
}

/* 使用 rem 自动适配 */
.title {
  font-size: 1.125rem; /* 约 18px @ 375px 屏幕 */
}

.body {
  font-size: 0.875rem; /* 约 14px @ 375px 屏幕 */
}
```

**方案 2：使用 vw 直接设置**

```css
/* 直接使用 vw */
.title {
  font-size: 4.8vw; /* 约 18px @ 375px 屏幕 */
}

.body {
  font-size: 3.73vw; /* 约 14px @ 375px 屏幕 */
}
```

**方案 3：使用 JavaScript 动态调整**

```javascript
// 根据屏幕宽度动态计算字体大小
const baseFontSize = viewportWidth >= 768 ? 18 : 16;
// 设置根元素字体大小
```

## 动画与过渡

### 性能优先的动画

#### 21. 使用 transform 和 opacity

```css
/* ✅ 性能友好的属性 */
.animated {
  transition: transform 0.3s ease, opacity 0.3s ease;
}

.animated:hover {
  transform: scale(1.1);
  opacity: 0.8;
}

/* ❌ 避免动画这些属性 */
.animated-bad {
  transition: width 0.3s, height 0.3s, margin 0.3s;
}
```

#### 22. 硬件加速

```css
/* 启用 GPU 加速 */
.gpu-accelerated {
  transform: translateZ(0);
  /* 或 */
  transform: translate3d(0, 0, 0);
}
```

#### 23. 关键帧动画

```css
@keyframes slideIn {
  from {
    transform: translateX(100%);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

.slide-in {
  animation: slideIn 0.3s ease-out;
}
```

## 可访问性

#### 24. 足够的触摸目标

```css
/* ✅ 最小 44x44 触摸区域 */
.button {
  min-width: 44px;
  min-height: 44px;
  padding: 12px 24px;
}
```

#### 25. 颜色对比度

```css
/* ✅ 确保足够的对比度 */
.text-primary {
  color: #333333; /* 在白色背景上对比度 12.6:1 */
}

.text-secondary {
  color: #666666; /* 对比度 5.7:1 */
}

/* ❌ 对比度不足 */
.text-weak {
  color: #cccccc; /* 对比度 1.9:1，难以阅读 */
}
```

## 调试技巧

调试用的 CSS 代码片段请参考 `quick-reference.md`。

#### 27. 性能监控

```jsx
// 测量渲染性能
import { useEffect, useRef } from '@lynx-js/react';

function PerformanceMonitor({ children }) {
  const startTime = useRef(performance.now());

  useEffect(() => {
    const endTime = performance.now();
    console.log(`Render time: ${endTime - startTime.current}ms`);
  });

  return children;
}
```

## 常见陷阱与解决方案

### 陷阱 1：Text 不换行

```css
/* 问题：Text 默认不换行 */
text {
  /* 默认 white-space: nowrap */
}

/* 解决 */
text {
  white-space: normal;
  /* 或 */
  max-width: 200px;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}
```

### 陷阱 2：图片不显示

```css
/* 问题：Image 必须设置尺寸 */
image {
  /* 没有尺寸不会显示 */
}

/* 解决 */
image {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  /* 或固定尺寸 */
  width: 200px;
  height: 200px;
}
```

### 陷阱 3：Flex 子元素被压缩

```css
/* 问题：子元素被压缩到 0 */
.flex-container {
  display: flex;
}

.flex-item {
  /* 可能被压缩 */
}

/* 解决 */
.flex-item {
  flex-shrink: 0; /* 不压缩 */
  /* 或 */
  min-width: 0; /* 允许压缩 */
  overflow: hidden; /* 配合 ellipsis */
}
```

### 陷阱 3 补充：内容项在 Lynx 和 Web 的主轴收缩下限不同

```css
/* row 方向宽度不足，或 column 方向高度不足时，Lynx 可能继续压缩内容项 */
.row {
  display: flex;
  width: 80px;
}

.row .content-item {
  width: 120px;
  flex-shrink: 1;
}

.column {
  display: flex;
  flex-direction: column;
  height: 80px;
}

.column .content-item {
  height: 120px;
  flex-shrink: 1;
}
```

```css
/* Web 预览页：取消主轴方向自动最小尺寸，贴近 Lynx 表现 */
.row .content-item {
  min-width: 0;
}

.column .content-item {
  min-height: 0;
}

/* Lynx：如果希望内容项不要被压缩，显式保护它 */
.content-item {
  flex-shrink: 0;
  /* 或设置主轴方向明确的 min-width / min-height */
}
```

### 陷阱 4：Sticky 不生效

```css
/* 问题：Sticky 需要正确的父容器 */
.container {
  /* 没有设置高度 */
}

.sticky-element {
  position: sticky;
  top: 0;
}

/* 解决 */
.container {
  height: 100vh; /* 或固定高度 */
  overflow: scroll; /* 需要滚动 */
}

.sticky-element {
  position: sticky;
  top: 0;
  z-index: 10; /* 确保在最上层 */
}
```

### Trap 5: z-index Causes Scroll Not to Follow

```css
/* Problem: In fold-view/scroll-view, child elements with z-index don't follow scroll */
fold-view-header .overlay {
  position: absolute;
  z-index: 100; /* Promoted to compositing layer, stays fixed in viewport during scroll */
}

/* Solution: Add z-index to parent container to establish the same stacking context */
fold-view-header {
  position: relative;
  z-index: 0; /* Key: Make header a stacking context */
}

fold-view-header .overlay {
  position: absolute;
  z-index: 100; /* Now correctly follows fold-view-header scroll */
}
```

**Other Solutions**:

```css
/* Option 1: Remove unnecessary z-index */
.header-item {
  /* No longer set z-index, control hierarchy via DOM order */
}

/* Option 2: Move z-index up to the container */
fold-view-header {
  position: relative;
  z-index: 10; /* Control hierarchy at container level */
}

/* Option 3: For Android platform use android-header-over-slot */
/* <fold-view android-header-over-slot={true}> */
```

### 陷阱 6：百分比高度不生效

```css
/* 问题：百分比高度需要父元素有高度 */
.parent {
  /* 没有高度 */
}

.child {
  height: 50%; /* 不生效 */
}

/* 解决 */
.parent {
  height: 400px; /* 或 */
  display: flex;
  flex-direction: column;
}

.child {
  height: 50%;
  /* 或 Flex 方式 */
  flex: 1;
}
```

## 代码检查清单

在提交代码前，检查以下项目：

- [ ] 使用了合适的布局类型（Linear/Flex/Grid/Relative）
- [ ] 避免过深的嵌套层级（建议不超过 5 层）
- [ ] 图片设置了明确的尺寸
- [ ] 文本内容放在 Text 组件中
- [ ] 使用了 CSS 类而非大量内联样式
- [ ] 动画使用了 transform 和 opacity
- [ ] 列表使用了 list 组件
- [ ] 触摸目标足够大（最小 44x44）
- [ ] 颜色对比度足够
- [ ] 在真机上测试过性能
