React TypeScript 开发

文件架构

我们强制规定使用规范的文件目录结构。

CRA Project
|-- .gitignore # Git Ignore
|-- .prettierrc # Prettier 语法配置
|-- package.json # 依赖定义文件
|-- README.md # 项目说明
|-- tsconfig.json # TypeScript 配置
|-- yarn.lock # 依赖版本定义文件
|-- .github # GitHub 配置
|   |-- ...
|-- .idea # Idea 配置(应被写入 ignore 文件)
|-- public # 公开文件
|   |-- favicon.ico # 必须放置 ico 文件
|   |-- ...
|-- src
    |-- App.tsx # 路由配置和全局配置
    |-- index.tsx # 主要组件、Provider 配置
    |-- react-app-env.d.ts
    |-- reportWebVitals.ts # 统计配置
    |-- setupProxy.js # 开发时后端接口代理配置
    |-- setupTests.ts # 测试配置
    |-- components # 组件库
    |   |-- Uploader # 单一组件
    |   |   |-- index.tsx # 组件默认导出
    |   |   |-- ...
    |   |-- Layout # 样式组件
    |   |-- Admin # 部分组件库
    |   |   |-- index.tsx # 子页面路由
    |   |   |-- Layout # 子页面样式组件
    |   |   |   |-- ...
    |   |   |-- ...
    |   |-- ...
    |-- i18n # 国际化配置
    |   |-- index.ts # 国际化实例
    |   |-- translation # 翻译配置
    |       |-- index.ts # 默认翻译集
    |       |-- template.ts # 翻译模板库
    |       |-- zh-cn.ts # 基于模板的翻译
    |       |-- en-us.ts # 基于模板的翻译
    |-- middleware # 中间件库
    |   |-- Api # Api 实例
    |   |   | -- ...
    |   |-- Route # 路由中间件(更好地区分用户是否登录)
    |       |-- index.ts # 默认导出
    |       |-- AuthRoute.tsx # 只有登录的用户才能访问(例如后台页面)
    |       |-- CommonRoute.tsx # 所有用户均可访问(例如主页)
    |       |-- NoAuthRoute.tsx # 只能由未登录的用户访问(例如登录页面和注册页面)
    |-- model # 数据模型
    |   |-- base # 基础数据(例如 UserData)
    |   |   |-- userData.ts
    |   |   |-- ...
    |   |-- request # 发送 Api 请求时需要的数据
    |   |   |-- index.ts # 全局请求数据模型
    |   |   |-- imageDeletePayload.ts # 一种请求数据模型,以“Payload”结尾
    |   |   |-- imageUploadPayload.ts
    |   |   |-- loginPayload.ts
    |   |   |-- ...
    |   |   |-- admin # 管理页面的 Api 请求
    |   |   |   |-- overviewDataPayload.ts
    |   |   |   |-- ...
    |   |-- response # Api 请求返回的数据
    |       |-- index.ts # 全局响应数据模型
    |       |-- batchImagesResponse.ts # 返回的数据通常以基础数据为基础,以“Response”结尾
    |       |-- loginResponse.ts
    |       |-- uploadResponse.ts
    |       |-- ...
    |       |-- admin # 管理页面的 API 响应
    |       |   |-- overviewDataResponse.ts
    |       |   |-- ...
    |-- redux # 全局状态管理器,有效解耦
    |   |-- hooks.ts # 自定义 Hook
    |   |-- store.ts # Redux store
    |   |-- reducers # Redux reducers
    |       |-- uploader.ts
    |       |-- viewUpdate.ts
    |       |-- ...
    |-- services # 基于 redux 的 service
    |   |-- api.ts # 基于 api 中间件和 redux-toolkit 的 Api 实例
    |-- utils # 工具库
        |-- md5.ts
        |-- ...

Prettier

使用 Prettier 管理项目代码风格。

Install

yarn add --dev prettier # 添加为开发依赖

.prettierrc 配置样例

{
  "tabWidth": 4,
  "printWidth": 100
}

绝对路径

tsconfig.json 中配置以使用绝对路径。

{
  "compilerOptions": {
    "baseUrl": "./src/",
    "paths": {
      "~/*": ["*"]
    }
  }
}

命名

所有前端变量、函数、类的命名遵循以下原则:

  1. 对所有变量使用小驼峰命名法。
  2. 所有文件名使用小驼峰命名法。
  3. 函数使用小驼峰命名法,例如 getUser()
  4. 未导出的接口使用小驼峰命名法。
  5. 导出的接口使用大驼峰命名法。
  6. 组件使用大驼峰命名法,如 <Component/>
  7. 所有 class 都使用大驼峰命名法。

此外,变量、函数、类和接口的名称必须与其定义和用途相关联。

例如:

  1. 表示站点配置的变量,我们使用 siteConfig
  2. 表示用户名的变量,我们使用 username
  3. 表示获取站点配置的函数,我们使用 getSiteConfig
  4. 表示站点配置返回值类型的接口,我们使用 SiteConfigResponse
  5. 表示查询用户界面请求值的接口,我们使用 UserGetPayload
  6. 表示上传图片所需参数的接口,我们使用 ImageUploadPayload
  7. 表示表示组件参数类型的接口,我们使用 ComponentProps

我们也有一些硬性要求:

  1. 花括号的开头必须紧跟在左侧,不得换行。
  2. 任何情况下都禁止在代码中使用 varfunction 关键字。
  3. 代码必须经过 Prettier 格式化,个人不得擅自修改 Prettier 配置文件。
  4. 代码必须通过项目配置的 ESLint 检查,任何人未经许可不得更改 ESLint 配置。

Components

存储 Redux JSX 组件。

  1. 我们希望一个文件中只有一个主要组件,拆分的小部件放在不同的文件中。
  2. 每个组件都使用一个单独的子文件夹,默认引入位于 index.tsx
  3. 组件的子目录名使用小驼峰命名法。
  4. 我们鼓励使用函数组件。
  5. 单行代码宽度不得超过 120 字节。
// /src/components/exampleComponent/index.tsx

// 虽然这不是运行所必需的,但我们要求我们从 import React 开始
import React from "react"; // 必须以分号结尾
import SubComponent from "./subComponent"; // 同一组件内的子组件使用相对路径
import { useAppDispatch } from "~/redux/hooks"; // 组件外部的函数使用绝对路径导入

// 组件参数单独定义Props接口,必须导出
// 命名为 组件名称+Props ,使用大驼峰命名法
export interface ExampleComponentProps {
    text: string; // 必须以分号结尾
}

// 对于仅在组件内部使用的方法,使用小驼峰命名法
const getHelloWorld = () => {
    return "Hello world."
}

// 组件必须使用箭头函数,带有类型声明,使用大驼峰命名法
const ExampleComponent:React.FC<ComponentProps> = (props) => {
    const dispatch = useAppDispatch();
    
    // 空行是必要的 
    // 将相同用途的变量放在一个段落中,在不同段落之间使用空行分割
    
    const helloWorldText = getHelloWorld();
    
    return (
        <div>
            <p>Hello world.</p>
            <span>{helloWorldText}</span> {/* 不要在 JSX 中使用函数 */}
            <p>{props.text}</p>
            <SubComponent /> {/* 没有子属性的组件使用封闭 XML 区块 */}
        </div>
    )
}

export default ExampleComponent; // 默认导出主要组件,必须以分号结尾

命名

  1. 文件名使用小驼峰命名法
  2. 组件 props 接口使用大驼峰命名法
  3. 组件使用大驼峰命名法

代码缩进

使用 4 个空格缩进,您可以使用 Prettier 更轻松地管理代码格式。

复杂度控制

我们尽量避免一个组件过于复杂,所以我们可以尽可能将它拆分成更小的组件。

{
  "tabWidth": 4
}

Interface

我们需要遵循以下规则:

  1. 默认导出的接口必须位于 model 文件夹中,并且是一个独立的文件
  2. 所有解耦后的接口都必须导出

主界面文件放在 src/model 目录的子目录下,并以 .ts 后缀命名。 接口定义以外的语句不得出现在文件中。

// src/model/response/index.ts

// 使用默认值为 any 的泛型封装统一数据类型
export default interface Response<T = any> { 
	code: number;
	msg: string;
	data?: T;
	error?: string;
}
// src/model/base/user.ts

// Base 接口必须是最完整的
export default interface User {
    name: string;
	password: string;
    age: number;
    birthday: Date;
}
// src/model/response/userResponse.ts
import User from "~/model/base/user";

// 没有 Response 嵌套
type UserResponse = Omit<User, "password">
export default UserResponse;

实际应用:

// ...
import Response from "~/model/response";
import UserResponse from "~/model/response/userResponse";
// ...

const apiResult:Response<UserResponse> = useApiResult();
// ...