随着Vue 和 React的不断推广,带来的是组件化和数据驱动的开发方式,抛弃了依托于jQuery那种意大利面的开发方式。
依托于 Webpack 等构建工具,使得前端代码具备了后端编程语言的代码组织能力,摆脱了传统的「一泻而下」式的代码编写。至此,作为前端也该对自己的代码有更高的要求,尤其是对组件的设计、规划方面。
组件分类
一个组件只做一件事,基于功能做好职责划分。
类似于一个方法只做一件事的思想。
无状态组件
React// 无状态组件const noStatus = props =>{props.title}
复制代码
看起来就像一个简单的模版渲染过程。
Vue 中没有无状态组件的概念,但实际上也存在类似功能的组件形式。比如图标组件,只接收 props 渲染模版,不做多余的动作。
Vue 复制代码
UI组件(木偶组件)
UI 组件指的是界面扩展类组件,比如:输入框、表格、树、下拉框等。像 Element、Vux 等组件库均属于此类组件。
此类组件的特点是:复用性强,只通过 props、events 和 slots 等组件接口与外部通信。 更像是一个对 HTML 的扩展标签。
业务组件(智能组件)
业务组件通常是根据最小业务状态抽象而出,有些业务组件也具有一定的复用性,但大多数是一次性组件。
那么业务组件的数据来自哪呢?
- props
- global state
容器组件(一个模块)
这类组件就是一个盒子,一般当作一个业务子模块的入口,比如一个路由指向的组件。
通常是这种形式:
复制代码
- 容器组件内的子组件通常具有业务或数据依赖关系。
- 如果没有使用全局状态管理,那么容器组件就是负责通过 props 分发数据到各个子组件,在通过 events 处理各个子组件的业务响应。此时容器组件需要做数据请求工作。
- 如果使用了全局状态管理,那么容器内部的业务组件可以自行调用全局状态处理业务。但并不是说此时容器组件什么都不用干了。即使不需要请求数据,还是有许多组件间或一个业务模块内的诸多统筹工作要做。
把上面的各类组件组装到一起就组成一个业务模块。
对应的文件目录结构实例:
注:仅供参考复制代码
组件设计原则
尽可能的减少状态
- 如果一个数据可以由另一个 state 变换得到,那么这个数据就不是一个 state。只需要写一个变换的处理函数,在 Vue 中可以使用计算属性。
- 如果你的 state 是一个数组,而模版最外层是渲染这个数组,那么你需要做的事是把渲染的项作为一个组件,只接受一个单级对象形式的数据,由外部决定这个组件的展示次数。
- 如果一个数据是固定的,不会变化的常量,那么这个数据就如同 HTML 固定的站点标题一样,写死或作为全局配置属性等,不属于 state。
- 如果一个数据需要从外部得到,它应该属于 props。
- 如果组件和兄弟组件拥有相同的 state,那么这个 state 应该放到更高的层级中,使用 props 传递到两个组件中。
合理的依赖关系
- 父组件不依赖子组件。要做到当我们把子组件删除后,只是丢失了一个功能,或一个模块等,而不会造成父组件及兄弟组件功能异常。
- 子组件基于父组件传递 props 作出个性化展示。
扁平化参数
像 HTML 原生元素那样,只接受原始类型(字符串、数值、布尔值和函数)作为属性,避免复杂的对象。当然,数据除外。
复制代码
良好的接口设计
- 把组件内部可以完成的工作做到极致。虽然提倡拥抱变化,但接口不是越多越好。
- 如果常量变为 props 能应对更多的场景,那么就可以作为 props。原有的常量可作为默认值。
- 如果组件不能提供调用者所需求的功能,那么这个组件的接口还不够完善。
- 如果需要为了某一调用者编写大量特定需求的代码,那么可以考虑通过扩展等方式构建一个新的组件。
- 保证组件的属性和事件足够的给大多数的组件使用。
信息冗余
尽量避免信息冗余,如果某个 state 可以由其他 state 计算得到,那么就删除这个 state,例如
错误
getInitialState: function(){ return { fullName: this.props.firstName + this.props.lastName; }}render: function(){ return {this.state.fullName}}复制代码
正确
render: function(){ return {this.props.firstName + this.props.lastName}}复制代码
api 尽量和已知概念保持一致
如果 api 可以和已知概念保持一致,那么就用已知的 api
错误
复制代码
eventKey 用来唯一标示 tabs 的 tabpane,同时 tabs 通过 activeKey 来匹配 eventKey 来确定哪个 tabpane 是当前 active 的。
正确
复制代码
我们可以复用 key 的 api,key 唯一标示了 tabs 的 某个 tabpane,并且对于后期更新也更高效
使用标签嵌套
尽量使用标签嵌套而不是属性配置。
错误:
1}, {tab:'t2',pane: 2}]}/>复制代码
正确
复制代码 1 2
避免使用 ref
使用父组件的 state 控制子组件的状态而不是直接通过 ref 操作子组件
错误
{ handleClick(){ this.refs.x.setState({count:count}); } render(){ return}}复制代码
正确
{ handleClick(){ this.setState({count:count}); } render(){ return}}复制代码