前端顶层设计V2.0

1,027 阅读11分钟

前端顶层设计v1.0:www.yuque.com/docs/share/…

github地址:github.com/dyk98/std-r…

与v1.0前端设计区别:

1.根据现实情况使用反馈,将文件目录结构更加合理化
2.使用IOC框架inversify.js,定义新的编写规范,通过DI将状态注入页面实现解耦合
3.状态与函数使用明确的访问修饰符

0.前言

本次设计的目标是以设计来实现对最佳目标值的预控👍,能通过规范设计编写出高维护性,高可读性,易拓展的前端代码。
本次设计使用DDD的思想进行代码的分层处理,使用IOC的思想进行各层级间代码的编写、调用和组合。
以下将从,“已有问题阐述”,“顶层设计的意义”,”代码分层介绍“,“设计核心点”,”前端目录架构“,“开发注意事项”,“特殊问题处理”几个标题出发阐述本次的顶层设计。

1.已有问题阐述

(1) 不同的人有着不同的写法,不同的命名习惯,不同的文件结构,常常导致协同开发难度加大。
(2) 当项目需要旁人接手✋的时候,新开发需要花费大量时间去理解原项目的目录结构,文件结构,以及出现很多不明所以的函数名和变量名。
(3) 样式层冗杂大量逻辑代码,导致各个项目组件无法复用(复用需要删除大量耦合逻辑使组件恢复原始的样子)
(4) 文件引用混乱,引用部分代码混乱难看,要移动一个文件或者删除一个文件难以进行
(5) 状态没有数据结构,无法使用ts的类型结构检查,使用相同数据结构的多个状态每个都需要单独定义结构编写重复代码。
(6) 状态与样式之间耦合严重,复用性与可维护性极差

2.顶层设计的意义

顶层设计是运用系统论的方法,从全局的角度,对某项任务或者某个项目的各方面、各层次、各要素统筹规划,以集中有效资源,高效快捷地实现目标。
所有事情都需要进行顶层设计,只是顶层设计的或多或少。举个🌰子,当你去旅游的时候很难想象你不做旅行计划,☝️一个毫无计划的旅行必然是杂乱无章的,而旅行计划也是顶层设计,例如有些人很穷所以他们选择穷游,那旅游过程中的节俭就是这个顶层设计的核心点之一。有些人时间很少却想逛很多的景点,那高效就是这个顶层设计的核心点之一,之后依照这些核心点来制定相应的旅游计划。
在编写的过程中以一个或多个编写的核心点为主导,以编写的架构为基础来制定相应的编写计划,进行高维护性,高可读性,易拓展的前端代码编写。

3.代码分层介绍

使用IoC的思想从大体上将代码分为三大层:数据逻辑层,组件样式层,组合容器层,其中组合容器层即为IoC容器,将数据逻辑与组件样式进行控制反转以达到解耦。通常当组件需要数据时,需要从数据层取出数据,导致组件与数据高度耦合。通过DI的方式将数据和数据逻辑注入进IoC容器进行并传递给组件样式,使组件样式的主动取数据逻辑变成被动拿,数据和逻辑与样式之间完全解耦。

数据逻辑层:该层的主要功能是涵盖整个系统中数据状态从存放以及管理相关数据逻辑的编写(例如:调取接口获取数据,调取接口传递数据,定义数据结构,传递数据至其他层以供使用等)。
组件样式层:该层的主要功能是编写整个系统中的样式组件,并包含一些组件的私有数据及私有数据的逻辑。通过props接受数据并进行相应的展示,也通过props接受一些函数,在用户触发某些组件内的事件时调用(例如点击登录按钮调用props传递过来的登录相关逻辑函数)。
组合容器层:该层的主要功能是作为数据逻辑层与组件样式层的中间层进行调度管理,以合理的形式放置组件样式,并合理的将组件样式层所需的数据及相关逻辑操作从数据逻辑层取出进行传递。 细致分层结构:

pages(页面层)属于组合容器层

项目页面文件,引入component组成页面,引用store.config用DI的方式将store中的数据和方法注入,通过props传递给component。

components(私有页面组件层)属于组件样式层

非常用页面组件,仅需要被使用于某个页面,不需要与其他页面共享,内部存在仅需要自己维护和使用的状态使用state。

commonComponents(公共页面组件层)属于组件样式层

常用页面组件,被需要用于多个页面,内部存在仅需要自己维护和使用的状态使用state。

abstract class(页面数据接口层)属于数据逻辑层

定义页面数据的虚拟类,定义数据及数据逻辑操作的interface,并定义页面数据结构,仅需要被使用于某个页面,不需要与其他页面共享。

stores(页面数据层)属于数据逻辑层

继承页面数据的虚拟类,并进行实现。存放页面所需要的数据并维护,及与数据相关的操作函数(例如:直接修改数据,调用api接口修改数据),通过引入并调用api与服务端进行数据交换。

store.config(页面数据层)属于数据逻辑层

将页面的数据文件(实现类)和接口对应起来,并将数据及数据对应的逻辑操作注入到inversify生成的IoC容器中

services(页面数据交流层)属于数据逻辑层

store将自己的数据或调用自己的某数据操作函数黑盒暴露于service中,供其他store调用或者获取。用来做store间的数据交流。

commonTypes(公共页面数据结构定义层)属于数据逻辑层

常用页面数据结构,被需要用于多个页面。

utils(常用工具层)属于数据逻辑层

常用工具函数,例如请求函数,获取页面高度等工具性函数

apis(页面接口层)属于数据逻辑层

与服务端进行交互的接口存放的地方,被store调用

4.设计核心点

(1) 不同职责的代码进行分层,各司其职

各层级间代码相互独立,由容器进行各层级的组合,🚫禁止各层级间混乱的互相引用或通信导致代码难以维护

(2) 视图层尽可能薄

获得的数据能够直接使用到视图层中,禁止在视图层中对数据进行转换、筛选、计算等逻辑操作。

(3) 不写重复逻辑

遇到相同的逻辑尽可能复用而不是重写,逻辑函数尽可能写成可拓展可维护,暴露给团队其他成员。

(4) 组件样式层高复用性,数据逻辑层高维护性,组合容器层高易读性

编写代码时以该项为原则进行编写,尽量不写三低代码

(5) 组件中尽量保证组件文件的纯净,尽量避免引入其他文件

编写代码时为保证组件的高复用性和可读性,尽量避免引入其他文件和npm包,例如:有需要维护的数据状态,巧用state不引入mobx进行状态管理

(6) 所有的状态(数据)和函数操作必须严格使用访问修饰符

private:修饰的属性或方法是私有的,不能在声明它的类的外部访问。
protected:修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的。
public:修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的。

5.前端目录架构

Taro的目录结构举例

文件目录结构:
├── config                                配置目录
|   ├── dev.js                            开发时配置
|   ├── index.js                          默认配置
|   └── prod.js                           打包时配置
├── src                                   源码目录
|   ├── apis                                    接口目录
|   |   ├── index                         index 页面api目录
|   |   |   └── api.ts                    index 页面的api文件
|   ├── assets                            静态文件目录
|   |   ├── images                        静态图片目录
|   |   |   └── index                     index 页面的静态图片
|   ├── common                              公共文件目录
|   |   ├── components                    公共组件目录
|   |   |   └── Demo2                     页面公有组件Demo2
|   |   ├── types                         公共数据结构目录
|   |   |   └── userType.ts               用户相关公用数据结构
|   |   └── apis                          公共接口目录
|   |   |   └── userApi.ts                用户相关公用请求接口
|   ├── pages                             页面文件目录
|   |   ├── index                         index 页面目录
|   |   |   ├── Demo                      页面 index 私有组件Demo
|   |   |   ├── index.tsx                 index 页面逻辑
|   |   |   └── index.scss                index 页面样式
|   ├── services                            store数据交流目录
|   |   |   └── indexService.ts           index页面向外暴露的数据或逻辑
|   ├── stores                                store数据状态存储目录
|   |   |   ├── index                     index页面数据状态相关文件夹
|   |   |   |   ├── indexInversify.ts     index页面将虚拟类与实现结合的配置文件
|   |   |   |   ├── indexStore.ts         index页面数据状态虚拟类的实现
|   |   |   |   └── indexType.d.ts        index页面数据状态虚拟类及数据interface
|   ├── utils                             工具函数库
|   |   ├── wxPromise                     小程序request请求函数
|   |   └── h5Promise                     h5 request请求函数
|   ├── TYPE.ts                           声明每个inversify的IoC容器的唯一名字
|   ├── app.scss                          项目总通用样式
|   └── app.tsx                           项目入口文件
└── package.json

react-typescript的目录结构举例

文件目录结构:
├── public                                静态资源配置目录
|   ├── favicon.ico                       页面左上角小图标
|   ├── index.html                        静态入口页面文件
|   └── manifest.json                     静态配置
├── src                                   源码目录
|   ├── apis                                    接口目录
|   |   ├── index                         index 页面api目录
|   |   |   └── api.ts                    index 页面的api文件
|   ├── assets                            静态文件目录
|   |   ├── images                        静态图片目录
|   |   |   └── index                     index 页面的静态图片
|   ├── common                              公共文件目录
|   |   ├── components                    公共组件目录
|   |   |   └── Demo2                     页面公有组件Demo2
|   |   ├── types                         公共数据结构目录
|   |   |   └── userType.ts               用户相关公用数据结构
|   |   └── apis                          公共接口目录
|   |   |   └── userApi.ts                用户相关公用请求接口
|   ├── pages                             页面文件目录
|   |   ├── index                         index 页面目录
|   |   |   ├── Demo                      页面 index 私有组件Demo
|   |   |   ├── index.tsx                 index 页面逻辑
|   |   |   └── index.scss                index 页面样式
|   ├── services                            store数据交流目录
|   |   |   └── indexService.ts           index页面向外暴露的数据或逻辑
|   ├── stores                                store数据状态存储目录
|   |   |   ├── index                     index页面数据状态相关文件夹
|   |   |   |   ├── indexInversify.ts     index页面将虚拟类与实现结合的配置文件
|   |   |   |   ├── indexStore.ts         index页面数据状态虚拟类的实现
|   |   |   |   └── indexType.d.ts        index页面数据状态虚拟类及数据interface
|   ├── utils                             工具函数库
|   |   └── Promise                       request请求函数
|   ├── TYPE.ts                           声明每个inversify的IoC容器的唯一名字
|   ├── App.scss                          项目默认载入的页面的样式文件
|   └── App.tsx                           项目默认载入的页面文件(一般用来放置路由)
|   ├── index.scss                        项目总通用样式
|   └── index.tsx                         项目入口文件
└── package.json

6.开发注意事项

(1) 同层级之间不进行直接的数据信息交换,避免逻辑代码冗杂难以维护。

(2) 所有当前层的逻辑性操作只在当前层中完成,下层不需要帮上层进行逻辑性代码处理,上层对下层永远是黑盒

例如:component只知道点击某东西及调用page通过props传入进来的函数然后page调用store中的函数。component并不知道函数在page中的操作,page也不知道函数在store中的操作。store调用service也是一样。

(3) 命名规则

组件名:大驼峰
除组件外的文件及文件夹:小驼峰
style_class:xxx_xxx下划线连接
函数名:小驼峰
变量名:小驼峰
interface: 以大写i开头例如:IMyProps props传递函数:以on为开头例如:onHandleClick 传递布尔值:以is开头例如:isLogin

(4) 每个page都有自己私有的store api assets,每个store都有一个向外暴露的service,通过名字进行统一

index页面具有的即为indexStore indexInversify indexType.d, index页面对应的数据IoC容器名即为IIndexType api和静态文件存在的文件夹叫index,indexStore的service即为indexService。

(5) store中所有调用api接口必须使用try catch进行包裹

(6) 在编写组件的时候需要给定props默认值(写在class内)用来提高编译速度

public static defaultProps = {
    hhh: 123,
    user: 'user'
  }

(7) 所有store使用在config配置中通过设置inversify的IoC容器使用单例模式

(8) 所有常量及函数都需要写注释到别人看一眼就知道含义和怎么调用的程度。函数需要声明入参及出参类型。

test: (a:string) => string = (a = '1') => {
    return a
  }

(9) 尽量不要使用any作为类型检查

7.特殊问题处理

(1)问:假如需求来的非常紧急需要快速改完,严格按这个流程写会很花费时间,怎么办?

答:先以最快的速度完成需求,在完成交付之后再重写架构这部分需求。