Administrator
发布于 2026-09-03 / 8,945 阅读
0
0

鸿蒙团队工程实践:代码规范、Code Review 与知识沉淀

鸿蒙百篇系列第 78 篇 · 专家

第 37 篇讲过 CI/CD,第 45 篇讲过测试,第 77 篇讲过设计模式。这篇聚焦一个更现实的问题:当鸿蒙团队从 3 人扩展到 10 人、20 人,怎么保证代码质量和协作效率。

小团队靠口口相传,大团队靠规范和流程。这不是形式主义——当你同时有 5 个开发者在改同一个 HSP 模块,没有规范和 Code Review,代码冲突和逻辑矛盾会让你的集成周期从一天变成一周。

一、ArkTS 代码规范:基于官方指南裁剪

华为有官方的 ArkTS 编码规范文档,覆盖命名、格式、编程实践。但官方规范有几十条,全部落地不现实。建议每个团队从中选取 15-20 条作为"红线规则",其余作为参考。

必选红线规则

以下是建议每个鸿蒙团队都纳入 Code Review 检查的规则:

规则

要求

为什么重要

类名/组件名 UpperCamelCase

建议

装饰器 @Component struct 需要大驼峰,框架强制

变量/方法 lowerCamelCase

建议

与 TypeScript 生态一致

常量全大写下划线分隔

建议

视觉区分可变与不可变

布尔变量加 is/has/can 前缀

建议

!isNotError 是阅读灾难

空格缩进,禁用 Tab

建议

跨编辑器一致性

行宽不超 120 字符

建议

可读性

if/for/while 必须大括号

建议

防止单行缩进歧义

禁止 any 类型

要求

ArkTS 禁用 any,编译会报错

禁止 ESObject(除非桥接)

要求

性能开销大

使用 T[] 而非 Array<T>

建议

编译器优化更好

finally 块禁用 return/throw

要求

会吞掉异常

一行只声明一个变量

要求

防止逗号变分号遗漏

单引号字符串

建议

与社区习惯一致

.eslintrc 配置

DevEco Studio 内置 ESLint,可以在工程根目录的 .eslintrc.json5 中配置:

{
  "rules": {
    // 命名规范
    "@typescript-eslint/naming-convention": [
      "error",
      {
        "selector": "class",
        "format": ["PascalCase"]
      },
      {
        "selector": "variable",
        "format": ["camelCase"]
      },
      {
        "selector": "const",
        "format": ["UPPER_CASE"]
      }
    ],
    // 禁止 any
    "@typescript-eslint/no-explicit-any": "error",
    // 最大嵌套深度
    "max-depth": ["error", 4],
    // 最大行宽
    "max-len": ["warn", { "code": 120, "ignoreUrls": true }],
    // 大括号风格
    "brace-style": ["error", "1tbs"],
    // 引号
    "quotes": ["warn", "single"]
  }
}

ArkTS 特有的约束

ArkTS 在语言层面比 TypeScript 更严格,有些规则不是 ESLint 配置,而是编译器强制:

  • 禁止 any 类型(编译报错)

  • 对象字面量必须符合接口定义(不支持动态属性)

  • @Builder 函数内不能有提前 return

  • @Component struct 不支持继承

  • 函数参数不支持默认值的复杂表达式

这些约束是好事。它们把很多运行时错误提前到了编译期。

二、Code Review 流程

审查清单

Code Review 不是找语法错误——那是 Linter 的活。Code Review 应该聚焦在 Linter 管不到的东西:

架构层面

  • [ ] 新代码放在了正确的分层(服务层不放 UI 逻辑,UI 层不直接操作数据库)

  • [ ] 新增模块的接口是否和第 71 篇的分层架构一致

  • [ ] 是否引入了循环依赖

状态管理

  • [ ] @State / @Prop / @Link / @StorageProp 选型是否正确

  • [ ] 是否在不必要的地方使用了 AppStorage(应该用 @State)

  • [ ] @Watch 回调中有没有重计算或异步操作

性能

  • [ ] 长列表是否使用 LazyForEach + cachedCount

  • [ ] 图片是否设置 sourceSize(避免加载原图)

  • [ ] 条件频繁切换是否用 visibility 替代 if/else

  • [ ] 是否有主线程同步阻塞调用

安全

  • [ ] 敏感数据是否加密存储(参考第 79 篇安全审计)

  • [ ] 网络请求是否校验证书

  • [ ] 日志是否泄露敏感信息

错误处理

  • [ ] 异步调用是否有 catch 分支(参考第 72 篇错误体系)

  • [ ] BusinessError 是否正确分类处理

  • [ ] 是否有全局兜底错误处理

Review 粒度

一个常见误区:Review 太大。一个 PR 改 2000 行,Reviewer 根本看不过来。建议:

PR 类型

最大行数

最大文件数

Review 要求

功能开发

500 行

10 个

至少 1 人 Review

Bug 修复

200 行

5 个

至少 1 人 Review

重构

800 行

15 个

至少 2 人 Review

紧急修复

100 行

3 个

事后补 Review

超过限制的 PR 必须拆分。第 71 篇的分层架构让拆分变得可行——改动集中在某一层,天然限制了影响范围。

三、模块间接口契约

大型鸿蒙应用的模块化拆分在第 71 篇和第 34 篇讲过。拆完之后,模块间怎么定义接口?靠口头约定是不行的。

接口定义文件

建议每个 HSP 模块在根目录维护一个 api.ets 文件,只导出对外暴露的接口:

// features/payment/api.ets
// 本文件是 payment 模块对外暴露的完整 API,其他模块只能通过本文件引用

// 类型导出
export { PaymentResult, PaymentConfig } from './src/main/ets/types'
export { PayErrorCode } from './src/main/ets/constants/ErrorCodes'

// 服务导出
export { PaymentService } from './src/main/ets/service/PaymentService'

// 组件导出
export { PaymentButton } from './src/main/ets/components/PaymentButton'

// 不导出内部实现:数据库操作、网络层、内部工具等

其他模块引用时:

// features/order/src/main/ets/pages/OrderConfirmPage.ets
import { PaymentService, PaymentConfig } from '@app/features/payment'

@Component
struct OrderConfirmPage {
  async onPay(): Promise<void> {
    const config: PaymentConfig = {
      orderId: '20260903001',
      amount: 99.00
    }
    const result = await PaymentService.getInstance().pay(config)
    // ...
  }
}

版本兼容

模块接口变更时,维护向后兼容:

// V1
interface PaymentConfig {
  orderId: string
  amount: number
}

// V2 新增字段,给默认值保证 V1 调用方不受影响
interface PaymentConfig {
  orderId: string
  amount: number
  currency?: string  // 可选,默认 CNY
  sandbox?: boolean   // 可选,默认 false
}

破坏性变更(删除字段、改类型)必须升大版本号,并在团队内同步迁移计划。

四、知识沉淀

组件文档

每个可复用组件应该有对应的文档。不是写 Word,是在组件目录下维护 README:

# PaymentButton

支付按钮组件,封装了支付状态、加载动画、错误提示。

## 用法

PaymentButton({
  amount: 99.00,
  orderId: 'ORDER_001',
  onPaySuccess: (result) => { ... },
  onPayFailed: (error) => { ... }
})

## Props

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| amount | number | 是 | 支付金额(元) |
| orderId | string | 是 | 订单 ID |
| disabled | boolean | 否 | 是否禁用 |
| onPaySuccess | callback | 否 | 支付成功回调 |
| onPayFailed | callback | 否 | 支付失败回调 |

## 依赖

- @app/features/payment(PaymentService)
- @app/common-ui(LoadingSpinner)

内部技术 Wiki

建议团队维护一个结构化知识库,按以下分类组织:

分类

内容

更新频率

架构文档

分层图、模块依赖图、数据流图

每月

API 约定

模块接口、错误码定义、事件协议

每次变更

踩坑记录

解决过的疑难问题、性能优化案例

随时

新人指南

环境搭建、代码走读路径、常用命令

每季度

性能基线

各页面冷启动时间、帧率、内存占用

每月

踩坑记录模板

## 问题:LazyForEach 在快速滚动时出现白屏

**日期**:2026-08-15
**模块**:首页信息流
**现象**:快速上滑时,列表项区域出现 200ms 白屏,然后内容才渲染出来。
**根因**:cachedCount 设置为 1,预渲染不足。滑动速度过快时,下一个 ListItem 的 build() 还没完成。
**解决**:cachedCount 调到 3,同时在 aboutToAppear 中预热数据。
**关联篇目**:第 52 篇高效列表渲染

这种记录的价值在于:下次有人遇到同样问题,不需要从零排查。

五、新人 Onboarding 路径

新人加入团队,给他们一条清晰的代码走读路径,而不是"你先看看代码":

Day 1: 环境搭建 + 跑通 Demo
  → 参考:第 1 篇 DevEco Studio 初体验
  → 目标:本地编译通过,模拟器能运行

Day 2-3: 理解架构
  → 阅读:架构文档 + 第 71 篇分层架构
  → 走读:entry/src/main/ets/entryability → pages → service → data
  → 目标:能画出模块依赖图

Day 4-5: 改一个 Bug
  → 从 Jira 认领一个低优先级 Bug
  → 参考:第 47 篇调试技巧
  → 目标:独立完成修复 + 提 PR + 通过 Review

Week 2: 开发一个小功能
  → 参考:第 3 篇 ArkUI 组件 + 第 6 篇网络请求
  → 目标:完整经历开发→测试→Review→合入流程

六、团队工具链

工具

用途

对应篇目

DevEco Studio Linter

代码静态检查

第 45 篇

Hvigor

构建编排

第 37 篇

Hypium

单元/UI 测试

第 45、46 篇

HiAppEvent

线上监控

第 38 篇

Profiler

性能分析

第 31、73 篇

Git Hook + ESLint

提交前检查

本文

Git pre-commit 钩子

#!/bin/bash
# .git/hooks/pre-commit

# 只检查暂存的 .ets 文件
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep "\.ets$" | tr '\n' ' ')

if [ -z "$FILES" ]; then
  exit 0
fi

echo "Running ESLint on staged files..."
npx eslint $FILES --fix

if [ $? -ne 0 ]; then
  echo "ESLint check failed. Please fix the errors before committing."
  exit 1
fi

echo "ESLint check passed."
exit 0

小结

团队工程实践的核心不是制定多少规范,而是让规范真正落地。三条建议:

第一,规范要少而精。20 条红线规则比 200 条"建议"管用。每条规则都要能说出"不遵守会出什么问题"。

第二,Code Review 是知识传递手段,不是质量门禁。Reviewer 的重点不是挑语法错误,而是确认架构一致性和逻辑正确性。

第三,知识沉淀要及时。解决一个 Bug 花了 4 小时,花 10 分钟写个踩坑记录,下次省 4 小时。这笔账怎么算都值。


评论