Skip to content

Commit

Permalink
test: add husky pre-commit to check the style
Browse files Browse the repository at this point in the history
  • Loading branch information
hustcc committed Sep 23, 2019
1 parent 53eb407 commit 09f5073
Show file tree
Hide file tree
Showing 38 changed files with 825 additions and 803 deletions.
6 changes: 0 additions & 6 deletions .idea/vcs.xml

This file was deleted.

8 changes: 8 additions & 0 deletions .lintmdrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"excludeFiles": [],
"rules": {
"no-long-code": 0,
"no-trailing-punctuation": 0,
"no-empty-code-lang": 1
}
}
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
nguage: node_js
node_js:
- "10"
before_install:
- npm i -g lint-md-cli
script: lint-md ./
62 changes: 31 additions & 31 deletions 201709/1.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# 如何管理好10万行代码的前端单页面应用
# 如何管理好 10 万行代码的前端单页面应用

>作者简介 导演 蚂蚁金服·数据体验技术团队
蚂蚁金服数据平台前端团队主要负责多个数据相关的PC Web单页面应用程序,业务复杂度类比Excel等桌面应用,业务前端代码量在几万行~几十万行,随着产品不断完善,破百万指日可待。管理好10万行级甚至百万行级代码的前端应用,是我们团队的核心挑战之一。
蚂蚁金服数据平台前端团队主要负责多个数据相关的 PC Web 单页面应用程序,业务复杂度类比 Excel 等桌面应用,业务前端代码量在几万行~几十万行,随着产品不断完善,破百万指日可待。管理好 10 万行级甚至百万行级代码的前端应用,是我们团队的核心挑战之一。

接下来的系列文章,我会尝试从以下几个角度介绍我们团队应对挑战的方法:

Expand All @@ -20,8 +20,8 @@

先介绍下我们团队的产品特点:

* ToB产品,业务复杂度高、业务理解门槛高;
* 前端代码量巨大(数据分析产品从零开始经历8个月迭代业务代码8万行,仅实现了产品长期规划需求的20%)
* ToB 产品,业务复杂度高、业务理解门槛高;
* 前端代码量巨大(数据分析产品从零开始经历 8 个月迭代业务代码 8 万行,仅实现了产品长期规划需求的 20%)

## 架构方案

Expand All @@ -39,7 +39,7 @@ https://user-gold-cdn.xitu.io/2017/9/27/5aba5e68f9bf18e2ed1178582e69627b)
划分原则:

* **纵向**:通过业务功能(可根据视图模块判断)划分
* **横向**通过Model-View-Controller三种不同职能划分
* **横向**通过 Model-View-Controller 三种不同职能划分


![module](https://user-gold-cdn.xitu.io/2017/9/27/34674760d7326bfc515e520151eb6ed7)
Expand All @@ -57,29 +57,29 @@ https://user-gold-cdn.xitu.io/2017/9/27/5aba5e68f9bf18e2ed1178582e69627b)

##### 3.1.1 领域模型

领域模型是业务数据,往往要持久化到数据库或localStorage中,属于可跨模块复用的公共数据,如:
领域模型是业务数据,往往要持久化到数据库或 localStorage 中,属于可跨模块复用的公共数据,如:

* Users 用户信息
* Datasets 数据集信息
* Reports 报表信息

领域模型作为公共数据,建议统一存放在一个叫做**Domain Model Layer**的架构独立分层中(前端业界一般对这层的命名为ORM层)。
领域模型作为公共数据,建议统一存放在一个叫做**Domain Model Layer**的架构独立分层中(前端业界一般对这层的命名为 ORM 层)。

下沉到Domain Model Layer(领域模型层)有诸多利处:
下沉到 Domain Model Layer(领域模型层)有诸多利处:

* **跨模块数据同步问题不复存在**,例如:之前Users对象在A和B两个业务模块中单独存储,A模块变更Users对象后,需将Users变更同步到B模块中,如不同步,A、B模块在界面上呈现的User信息不一致,下沉到领域模型层统一管理后,问题不复存在;
* **除领域模型复用外,还可复用领域模型相关的CRUD Reducer**,例如:之前Users对象对应的Create Read Update Delete方法可能在A和B两个业务模块各维护一套,下沉到领域模型层统一管理后,减少了代码重复问题;
* **跨模块数据同步问题不复存在**,例如:之前 Users 对象在 A 和 B 两个业务模块中单独存储,A 模块变更 Users 对象后,需将 Users 变更同步到 B 模块中,如不同步,A、B 模块在界面上呈现的 User 信息不一致,下沉到领域模型层统一管理后,问题不复存在;
* **除领域模型复用外,还可复用领域模型相关的 CRUD Reducer**,例如:之前 Users 对象对应的 Create Read Update Delete 方法可能在 A 和 B 两个业务模块各维护一套,下沉到领域模型层统一管理后,减少了代码重复问题;
* **自然承担了部分跨模块通信职责**,之前数据同步相关的跨模块通信代码没有了存在的必要性;

##### 3.1.2 应用状态模型

应用状态模型是与视图相关的状态数据,如:

* 当前页面选中了列表的第n行 currentSelectedRow: someId
* 当前页面选中了列表的第 n 行 currentSelectedRow: someId
* 窗口是否处于打开状态 isModalShow: false
* 某种视图元素是否在拖拽中 isDragging: true

这些数据与具体的视图模块或业务功能强相关,建议存放在业务模块的Model中
这些数据与具体的视图模块或业务功能强相关,建议存放在业务模块的 Model 中

#### 3.2 视图层组件

Expand All @@ -90,7 +90,7 @@ https://user-gold-cdn.xitu.io/2017/9/27/5aba5e68f9bf18e2ed1178582e69627b)

##### 3.2.1 容器型组件

容器型组件是与store直连的组件,为展示型组件或其它容器组件提供数据和行为,尽量避免在其中做一些界面渲染相关的事情。
容器型组件是与 store 直连的组件,为展示型组件或其它容器组件提供数据和行为,尽量避免在其中做一些界面渲染相关的事情。

##### 3.2.2 展示型组件

Expand All @@ -100,8 +100,8 @@ https://user-gold-cdn.xitu.io/2017/9/27/5aba5e68f9bf18e2ed1178582e69627b)

#### 3.3 公共服务

* 所有的HTTP请求放在一起统一管理
* 日志服务、本地存储服务、错误监控、Mock服务等统一存放在公共服务层
* 所有的 HTTP 请求放在一起统一管理
* 日志服务、本地存储服务、错误监控、Mock 服务等统一存放在公共服务层


按照上面三点合并同类项后,业务架构图变更为
Expand All @@ -112,8 +112,8 @@ https://user-gold-cdn.xitu.io/2017/9/27/5aba5e68f9bf18e2ed1178582e69627b)

模块粒度逐渐细化,会带来更多的跨模块通信诉求,为避免模块间相互耦合、确保架构长期干净可维护,我们规定:

* 不允许在一个模块内部直接调用其他模块的Dispatch方法(写操作、变更其他模块的state
* 不允许在一个模块内部直接读取其他模块的state方法(读操作)
* 不允许在一个模块内部直接调用其他模块的 Dispatch 方法(写操作、变更其他模块的 state
* 不允许在一个模块内部直接读取其他模块的 state 方法(读操作)

我们建议将跨模块通信的逻辑代码放在父模块中,或者在一个叫做**Mediator**层中单独维护。

Expand All @@ -123,9 +123,9 @@ https://user-gold-cdn.xitu.io/2017/9/27/5aba5e68f9bf18e2ed1178582e69627b)

## 数据流管理

刚刚从空间维度讲了架构管理的方案,现在从时间维度说说应用的数据流转 --- Redux单向数据流
刚刚从空间维度讲了架构管理的方案,现在从时间维度说说应用的数据流转 --- Redux 单向数据流

Redux架构的设计核心是单向数据流,应用中所有的数据都应该遵循相同的生命周期,确保应用状态的可预测性。
Redux 架构的设计核心是单向数据流,应用中所有的数据都应该遵循相同的生命周期,确保应用状态的可预测性。

![redux](https://user-gold-cdn.xitu.io/2017/9/27/083814aacb490c89d331b18e976f2104)

Expand All @@ -136,7 +136,7 @@ Redux架构的设计核心是单向数据流,应用中所有的数据都应该

### 2. Reducer

每个Action都会对应一个数据处理函数,即Reducer。特别强调,Reducer必须是纯函数(pure function),这个规定带来一个非常大的好处,数据处理层代码变的非常容易写单元测试。
每个 Action 都会对应一个数据处理函数,即 Reducer。特别强调,Reducer 必须是纯函数(pure function),这个规定带来一个非常大的好处,数据处理层代码变的非常容易写单元测试。

纯函数的特征是入参相同的情况下,返回值恒等,举个栗子🌰:

Expand All @@ -161,17 +161,17 @@ function now() {

### 3. Store

Store 数据存放的地方,store保存从进入页面开始所有Action操作生成的数据状态(state),每次Action引发的数据变更都必须生成一个新的state对象,且确保旧的state对象不被修改。这样做可以保证
应用的状态的可预测、可追溯,也方便设计Redo/Undo功能
Store 数据存放的地方,store 保存从进入页面开始所有 Action 操作生成的数据状态(state),每次 Action 引发的数据变更都必须生成一个新的 state 对象,且确保旧的 state 对象不被修改。这样做可以保证
应用的状态的可预测、可追溯,也方便设计 Redo/Undo 功能

我们团队使用轻量级的immutable方案`immutability-helper`,相比完全拷贝一份(deep clone)性能更优、存储空间利用率更高。
我们团队使用轻量级的 immutable 方案`immutability-helper`,相比完全拷贝一份(deep clone)性能更优、存储空间利用率更高。

![immutability-helper](https://user-gold-cdn.xitu.io/2017/9/27/23e0683ed79b600951ded9d592d3a4fa)

`immutability-helper`的API不够友好,我们写了一个库[immutability-helper-x](https://github.com/ProtoTeam/immutability-helper-x)增强它的易用性。
`immutability-helper`的 API 不够友好,我们写了一个库[immutability-helper-x](https://github.com/ProtoTeam/immutability-helper-x)增强它的易用性。


immutability-helper API风格
immutability-helper API 风格

```js
import update from 'immutability-helper';
Expand All @@ -185,7 +185,7 @@ const newData = update(myData, {
});
```

immutability-helper-x API风格
immutability-helper-x API 风格

```js
import update from 'immutability-helper-x';
Expand All @@ -195,13 +195,13 @@ const newData = update.$set(myData, 'x.y.z', 7);

### 4. 统一渲染视图

React/Redux是一种典型的数据驱动的开发框架(Data-Driven-Development),在开发中,我们可以将更多的精力集中在数据(领域模型+状态模型)的操作和流转上,再也不用被各种繁琐的DOM操作代码困扰,当Store变更时,React/Redux框架会帮助我们自动的统一渲染视图
React/Redux 是一种典型的数据驱动的开发框架(Data-Driven-Development),在开发中,我们可以将更多的精力集中在数据(领域模型+状态模型)的操作和流转上,再也不用被各种繁琐的 DOM 操作代码困扰,当 Store 变更时,React/Redux 框架会帮助我们自动的统一渲染视图

监听Store变更刷新视图的功能是由react-redux完成的
监听 Store 变更刷新视图的功能是由 react-redux 完成的

* \<Provider\> 组件通过`context`属性向后代\<connect\>组件提供(provide)store对象
* \<connect\> 是一个高阶组件,作用是将store与view层组件连接起来(这里重复提一句,redux官方将\<connect\>直接连接的组件定义为container component),\<connect\>向开发者开放了几个回调函数钩子(mapStateToProps, mapDispatchToProps...)用于自定义注入container component的props的姿势
* react-redux监听redux store的变更,store改变后通知每一个connect组件刷新自己和后代组件,为了减少不必要的刷新提升性能,connect实现了shouldComponentUpdate方法,如果props不变的话,不刷新connect包裹的container component;
* \<Provider\> 组件通过`context`属性向后代\<connect\>组件提供(provide)store 对象
* \<connect\> 是一个高阶组件,作用是将 store 与 view 层组件连接起来(这里重复提一句,redux 官方将\<connect\>直接连接的组件定义为 container component),\<connect\>向开发者开放了几个回调函数钩子(mapStateToProps, mapDispatchToProps...)用于自定义注入 container component 的 props 的姿势
* react-redux 监听 redux store 的变更,store 改变后通知每一个 connect 组件刷新自己和后代组件,为了减少不必要的刷新提升性能,connect 实现了 shouldComponentUpdate 方法,如果 props 不变的话,不刷新 connect 包裹的 container component;


## 总结
Expand Down
Loading

0 comments on commit 09f5073

Please sign in to comment.