欢迎光临石四片叶子网
详情描述

一、基础规范

1.1 文件命名

// ✅ 推荐
user-profile.tsx
shopping-cart-service.ts
order-utils.ts
constants.ts

// ❌ 避免
UserProfile.tsx  // PascalCase只用于组件
userProfile.ts   // 混用命名风格

1.2 目录结构

src/
├── components/     # 通用组件
│   ├── common/    # 全平台通用
│   └── business/  # 业务组件
├── pages/         # 页面组件
├── services/      # API服务层
├── hooks/         # 自定义hooks
├── utils/         # 工具函数
├── constants/     # 常量定义
├── types/         # TypeScript类型
└── styles/        # 样式文件

二、React/TypeScript规范

2.1 组件设计

// ✅ 函数式组件 + TypeScript
interface ProductCardProps {
  productId: string;
  title: string;
  price: number;
  onAddToCart?: (id: string) => void;
}

const ProductCard: React.FC<ProductCardProps> = ({
  productId,
  title,
  price,
  onAddToCart
}) => {
  const [isLoading, setIsLoading] = useState(false);

  const handleClick = useCallback(async () => {
    setIsLoading(true);
    try {
      await onAddToCart?.(productId);
    } finally {
      setIsLoading(false);
    }
  }, [productId, onAddToCart]);

  return (
    <div className="product-card">
      <h3>{title}</h3>
      <p>¥{price.toFixed(2)}</p>
      <button 
        onClick={handleClick}
        disabled={isLoading}
        aria-label={`添加${title}到购物车`}
      >
        {isLoading ? '添加中...' : '加入购物车'}
      </button>
    </div>
  );
};

// 添加displayName便于调试
ProductCard.displayName = 'ProductCard';

2.2 Hooks使用规范

// ✅ 自定义hook
const useProductDetail = (productId: string) => {
  const [product, setProduct] = useState<Product | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let mounted = true;

    const fetchProduct = async () => {
      try {
        const data = await productService.getDetail(productId);
        if (mounted) {
          setProduct(data);
        }
      } catch (error) {
        console.error('Failed to fetch product:', error);
      } finally {
        if (mounted) {
          setLoading(false);
        }
      }
    };

    fetchProduct();

    return () => {
      mounted = false;
    };
  }, [productId]);

  return { product, loading };
};

// ❌ 避免在循环/条件中使用hooks
if (condition) {
  const [state, setState] = useState(); // 错误!
}

三、样式规范

3.1 CSS Modules(推荐)

/* ProductCard.module.css */
.productCard {
  border: 1px solid var(--border-color);
  border-radius: 8px;
  padding: 16px;
}

.title {
  font-size: 16px;
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 8px;
}

.price {
  color: var(--price-color);
  font-size: 20px;
  font-weight: bold;
}

3.2 CSS-in-JS规范

import styled from '@emotion/styled';

const StyledButton = styled.button<{ $primary?: boolean }>`
  padding: 12px 24px;
  border-radius: 4px;
  border: none;
  background-color: ${props => 
    props.$primary ? 'var(--primary-color)' : 'var(--secondary-color)'};
  color: white;
  font-size: 14px;
  cursor: pointer;

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  &:hover:not(:disabled) {
    opacity: 0.9;
  }
`;

四、性能优化

4.1 代码分割

// 动态导入
const ProductList = lazy(() => import('./ProductList'));

// React.lazy + Suspense
<Suspense fallback={<LoadingSpinner />}>
  <ProductList />
</Suspense>

4.2 内存优化

// ✅ 使用useCallback避免重复创建函数
const handleSubmit = useCallback((values: FormValues) => {
  submitOrder(values);
}, []);

// ✅ 使用useMemo缓存计算结果
const filteredProducts = useMemo(() => {
  return products.filter(p => 
    p.price >= minPrice && p.price <= maxPrice
  );
}, [products, minPrice, maxPrice]);

五、错误处理

5.1 API错误处理

class ApiService {
  private async request<T>(
    endpoint: string, 
    options?: RequestInit
  ): Promise<T> {
    try {
      const response = await fetch(`${API_BASE}${endpoint}`, {
        ...options,
        headers: {
          'Content-Type': 'application/json',
          ...options?.headers,
        },
      });

      if (!response.ok) {
        throw new HttpError(response.status, await response.text());
      }

      return await response.json();
    } catch (error) {
      if (error instanceof HttpError) {
        // 处理HTTP错误
        Sentry.captureException(error);
      }
      throw error;
    }
  }
}

5.2 React错误边界

class ErrorBoundary extends React.Component<
  { children: ReactNode; fallback?: ReactNode },
  { hasError: boolean }
> {
  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    // 上报错误
    errorReporter.log({
      error,
      errorInfo,
      timestamp: Date.now(),
    });
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback || <DefaultErrorPage />;
    }
    return this.props.children;
  }
}

六、代码质量

6.1 ESLint配置

{
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended"
  ],
  "rules": {
    "@typescript-eslint/explicit-function-return-type": "warn",
    "@typescript-eslint/no-explicit-any": "error",
    "react/prop-types": "off",
    "no-console": ["warn", { "allow": ["warn", "error"] }]
  }
}

6.2 Git提交规范

feat: 新增商品详情页骨架屏
fix: 修复购物车价格计算错误
perf: 优化首屏加载速度
docs: 更新README使用说明
style: 调整按钮hover样式
refactor: 重构订单状态管理
test: 添加购物车单元测试
chore: 更新依赖版本

七、测试规范

7.1 单元测试

// __tests__/utils/priceFormatter.test.ts
describe('priceFormatter', () => {
  it('应该正确格式化价格', () => {
    expect(formatPrice(1999)).toBe('¥1,999.00');
    expect(formatPrice(0)).toBe('¥0.00');
  });

  it('应该处理负数和NaN', () => {
    expect(formatPrice(-100)).toBe('-¥100.00');
    expect(formatPrice(NaN)).toBe('¥0.00');
  });
});

7.2 组件测试

// __tests__/components/AddToCartButton.test.tsx
describe('AddToCartButton', () => {
  it('点击时应该调用onClick回调', async () => {
    const mockOnClick = jest.fn();
    const { getByRole } = render(
      <AddToCartButton productId="123" onClick={mockOnClick} />
    );

    const button = getByRole('button');
    await userEvent.click(button);

    expect(mockOnClick).toHaveBeenCalledWith('123');
  });
});

八、监控与日志

8.1 性能监控

// 关键性能指标
const reportWebVitals = (metric: any) => {
  analytics.send({
    type: 'performance',
    name: metric.name,
    value: metric.value,
    timestamp: Date.now(),
  });
};

// 用户行为追踪
const trackUserAction = (action: string, data?: Record<string, any>) => {
  if (process.env.NODE_ENV === 'production') {
    userBehaviorTracker.log({
      action,
      data,
      page: window.location.pathname,
      timestamp: new Date().toISOString(),
    });
  }
};

总结要点

类型安全优先:全面使用TypeScript,避免any类型 组件职责单一:每个组件只关注一个功能点 性能意识:合理使用memoization、代码分割 错误可追溯:完善的错误处理和日志记录 代码可测试:编写易于测试的纯函数和组件 保持一致性:团队统一的命名、结构和规范 渐进式优化:先实现功能正确性,再逐步优化

快手电商场景特别需要注意:

  • 高并发下的性能表现
  • 移动端兼容性
  • 电商特有的无障碍访问
  • 数据敏感性和安全性
  • 快速迭代中的代码可维护性

建议配合Prettier、Husky、Commitlint等工具自动化检查,确保规范落地执行。