Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `App`. See https://fb.me/react-warning-keys for more information.
为什么在jsx中必须为组数的每一项添加一个key呢?React官方文档是这样写的:
Keys help React identify which items have changed, are added, or are removed.
1. 项目总结
数字人生
遇到的问题
前端项目没有任何的知识积累
虽然项目代号已经叫4.0了,但是对于前端er来说,业务知识几乎为0。
每一位新的前端开发加入到这个项目进来,都要经历这样一个过程: 没有文档,更没有前辈来告诉你整个业务的设计,前人踩过的坑在你这可能又要踩一次了,更别提有代码给你指引了,况且三无(无注释、无文档、无测试)代码能给的指引也是有限的甚至是反作用。
原型的缺乏完善性造成业务理解上的偏差
原型作为软件工程的建筑图纸,一份合格甚至是优秀的原型会大大减轻开发的压力。项目中的原型有以下几个问题:
项目进度落后于项目经理预估的进度
现状是整个项目拖延了三四周。 首先我们先简化我们的生产模型:
根据这个公式,分析以下几点原因:
由于缺乏大型项目的经验导致前期整个项目的失控,如果能有经验的前辈在带领的话应该会好很多,所以这也是拖慢项目进度的重要原因。
图中
横轴L
代表的是劳动力的投入量,纵轴TPL
代表的是总产量,图中可以看到在6
之前,每增加一单位劳动力都能有效带来生产力的增长(边际增长/每一个点的斜率
),但是到了6
之后,边际产量
反而开始减少了。虽然图中的生产模型并非对应我们的项目生产模型,但这个曲线是必然存在的,而目前项目的人员投入可能会拖延一定的项目进度。业务理解的偏差和过分乐观的态度导致预估时间的不准确。而在
劳动力质量
和人员不足
短期内不变的情况下,只能延长我们的开发时间来完成开发,以致于项目进度拖延。软件工程开发建议
这里直接引用网友对《人月神话》一书的总结,关于软件工程开发过程中的一些建议:
2. 领域驱动设计
现状分析
回想一下我们前端的主要工作,大部分都跟页面打交道:如何还原UI设计图、点击某个按钮发起某个请求。
当我们将重点放在了视图上的时候就很容易出现一个问题:我们极易被需求变更摧毁。
或许改改页面样式还是小问题,但是当你让将功能从
A->B->C
变成C-B-A
的时候,你会发现自己的设计没有任何弹性:扩容性极差
。而有一个被我们前端容易轻视的很重要的一环节就是业务,甚至你会听到有人说前端不需要关注业务,你只要按着原型一个页面一个页面做下来就好啦。虽然从某种层面上来讲这并没有错,因为真正存数据到数据库里的并不是我们前端,而是后端小哥。但是如果你是这样工作的,我想你的开发流程一定是这样的:
我们称之为
视图驱动设计
。上述流程看不到有关于对业务的抽象,极有可能给自己埋下坑,因为页面的抽象真的是及其脆弱的,需求改变很容易让你骂娘,而且对新人也及其不友好,试想一位新同事在浏览你的代码的时候,他只看到了这些页面共同的样式,共同的请求,对业务的理解又只能继续从原型上获取了。这是一个糟糕的开始,所以这时候极有必要引入一个概念
领域模型
。初识领域模型
当然对页面的抽象没有任何错误,只是在这个环节前缺漏了一个很重要的环节:
对业务的抽象
,我们重新整理一下流程:那么究竟什么是领域模型呢?
这里的核心概念指代的就是领域模型,也可以说是整个产品(项目)最核心的地方。这个核心概念理论上是建立之后就不应该去修改的,如果修改了,那又是一个新的产品或项目了。
领域模型是对业务的抽象,是贯穿整个项目的一个完整的业务知识体系。
建立领域模型
那么我们如何建立我们的领域模型呢?建立这个领域模型的工作究竟应该谁来做?
答案是各个环节的外科主刀人!
最早建立的这个领域模型的其实应该是产品经理,因为产品是他设计的,他是最清楚这个产品的人,但很遗憾,并不是每个产品都能抽象出这个领域模型出来的,虽然他们很清楚他们的产品是什么,但是输出只会是原型,并不会多给你一份
领域模型设计
的文档。这个概念应该后端人员并不陌生,因为他们需要设计一个非常重要的东西:
数据库
。一个经验丰富的后端开发一定会设计一个灵活且合理的数据库来满足需求上的变更,而不是一味的堆叠数据库表。那前端如何去建立领域模型呢?当业务越简单时,这个领域模型越容易建立,前端开发可能也能直接设计出领域模型。但是当我们面临比较复杂项目时可能缺乏这方面的经验容易导致设计上的偏差,这时候我们主要去借鉴后端的领域模型设计。
博客系统的领域模型
协同项目的领域模型
协同业务比较复杂,当初看原型看半天也看的相当片面,也是导致整个项目重写的原因之一。后面找后端设计人员拿了一张这样的图:
重新整理了一份领域模型:
领域驱动设计
当领域模型设计出来之后,一切就变得有章可循了。
目录结构设计
着重介绍下
pages
、services
、models
、components
pages
services
同领域模型一一对应
models
所有的数据的CRUD都存放在各自的文件下
components
路由设计
设计思路主要有两种
只有一级路由,所有的路由配置都在一个文件下显式声明
允许有二级路由(子路由)
这边以第二种
允许有二级路由(子路由)
做介绍,第一种设计方式只要去除子路由即可如此一来,我们就可以将路由同目录结构直接联系起来,当我们在开发中就可以直接通过浏览器中url来快速定位代码位置。
主导接口设计
后端的接口是为前端服务的,而前端需要什么样的服务前端最清楚。当我们在未建立领域模型的时候,我们可能由于对业务理解欠缺,无法做到主导接口的设计,但是当建立起领域模型的时候,我们很清楚的知道我们需要什么。
举个栗子,现有的注册接口是这样的:
因为我们有一个叫做
resource-register
的模型,完全只需要一个接口就足够了这样的情况在该项目中很多,看似两个不同的接口完全可以抽象成一个接口,而这种事如果没有一步到位的后,后面在调整就可能得费点神了,特别是在没有测试覆盖的情况下。
3. 其他
命名约定
总结一些我自己在使用的命名约定:
-
连接,并且遵循名词-动词
的结构命名on*
开头,内部自己使用的句柄全部以handle*
开头Options
结尾我们尽量在变量命名上达成共识,方便团队协作。
React中Key的原理及使用
Key的原理
我们经常会在开发中开到react在控制台给你这样的警告:
为什么在jsx中必须为组数的每一项添加一个key呢?React官方文档是这样写的:
所以这个key是给react自身用来更新元素用的,并不是给我们用的。
我们来看一下,如果我们有这样一个数组:
现在我们改变数组变成:
第二个数组相对于第一个数组,所以的元素索引都变了,如果是你来更新元素你要怎么做?重新渲染一遍吗?这是一种办法,但这个方法是肉眼可见的简单粗暴,必定存在着性能问题,所以这时候react就借助key来唯一标识某个元素,react只要将第三个元素append到第一个位置就好了,这样就完成了更新。
再来思考一个问题,为什么只有数组里的元素需要添加key? 看代码说话:
可以看到非数组元素的元素,他所在位置就是他天然的key,并且当用变量控制一个元素的显隐的时候,也必须用
null
占据一个位置,这也是为什么在jsx中不能if
的原因。Key的另类使用
上面提到,key是提供给react使用的,当一个组件的key改变时,这个组件会被重新渲染,利用这一点,我们在一些特殊场景可以处理的非常优雅。
想像一下有这样一个场景:
componentDidMount
中去获取数据componentWillReceiveProps
去监听componentDidMounut
其实已经做过一次了componentDidMount
和componentWillReceiveProps
中分别执行如果这时候我们借助key的原理,当组件的key改变的时候,重新触发组件的挂载,那我们在处理这件事来变得更优雅了。
高阶组件的使用
高阶组件的出现是为了替代
Mixins
而出现的,最早在react中,一些通用的代码可以抽象到Mixins
中,来进行逻辑的复用,比如受控表单的value, onChange
这里以改变组件的key举一个简单的例子:
recompose介绍
看文档
参考文章