Open HuangHongRui opened 7 years ago
高内聚:指的是把逻辑紧密相关的内用放在一起。用户界面主要是内容&交互行为&样式。传统上,内容由HTML表示,交互由Js文件,样式由css文件定义。虽然满足功能模块的需要,却要放在三个不同文件中!!这是不满足高内聚的原则...但React不同,展示内容的JSX,定义行为的Js代码,甚至定义Css都可放在一个Js文件中,因为它们本身就是为实现同一个目的存在的..所以React天生具备高内聚特点。
低耦合: 指的是不同组件之间的依赖关系要尽量弱化,也就是每个组件尽量独立。保持整个系统的低耦合度,需要对系统中的功能有充分的认识,然后根据功能点划分模块,让不同的组件去实现不同的功能,这个功夫还在开发者身上,不过React组件的对外接口非常规范,方便开发者设计低耦合的系统。
对于React,要充分应用以上2原则,需要对React特性有充分了解。
差劲的程序猿操心代码,优秀的程序猿操心数据结构和它们之间的关系
React组件的数据分为 prop
和 state
, 无论是哪者改变,都可能引起组件之间的重新渲染。
prop 是组件的对外接口, state是组件的内部状态,对外用prop,内部用state。
[React 的 prop]
prop
是 property的简写,是从外部传递给组件的数据,
一个组件通过定义自己能够接受的prop就定义了自己的对外公共接口。
每个组件都是独立存在的模块, 组件之间的一切都是外部世界,外部世界就是通过prop来和组件对话的。
[给 prop 赋值]
下面JSX代码片段中,就使用了prop
<SampleButton id = ''Rui" borderWidth = {2} onClick = {onButtonClick} style = {{color: "red"}} />
该组件使用了名为 id
, boderWidth
, 'onClick' 和 'style' 的prop,
React的组件prop很像HTML元素的属性。
React组件的 prop 所能支持的类型丰富,除了字符串, 可以说任何一种Js语言支持的数据类型。
当 prop 的类型部署字符串类型时,在JSX中必须用 花括号 把prop值包住, 所以 style
的值会有两层花括号(外层{}是JSX语法,内层{}是对象)
当外部世界要传数据给组件,最直接方式就是通过prop
,
组件要反馈数据到外部世界,也可用prop
,
因为它的类型不限于纯数据,可以是函数,
函数类型的prop
等于让父组件交给子组件一个回调函数,
子组件在恰当的实际调用函数类型的prop,可带上必要的参数,
这样就反过来把信息传递给外部世界
[ 读取 prop 值 ]
如果一个组件需要定义组件的构造函数, 一定要记得在构造函数的第一行通过 super 调用父类(React.Component)的构造函数。
如在构造函数中没调用 super(props)
,那么组件实力被构造后,
类实例的所有成员函数就无法通过 this.props
访问到父组件传递过来的props值。
this.props
赋值是 React.Component
构造函数的工作之一
在构造函数中给函数绑定了当前this的执行环境,是因Es6方法创建的组件类并不自动给我们绑定this到当前实例对象。
在构造函数中可以通过参数props获得传入的prop值, 在其他函数中则可通过this.props访问传入prop的值。
[ propTypes ] 检查
prop是组件的对外接口,那么应该有某种方式让组件生命组件的接口规范。
一个组件应该可规范以下这些方面:
React通过propTypes来支持这些功能 在Es6方法定义的组件类中,可通过增加类的propTypes属性来定义prop规格, 这不只是声明,而是一种限制,运行时和静态代码检查时, 都可根据propTypes判断外部世界是否正确地使用了组件的属性。
Counter.propTypes = {
caption: PropTypes.string.isRequired,
initValue: PropTypes.number
};
Code例中,要求caption必须是string类型,initValue必须是number类型。 caption带上了 isRequried ,表明使用Counter组件必须要指定caption; 而initValue因没有isRequired,则表示如果没有也没关系。。
验证其作用,违反其规定使用Count实例,例如:
<Counter caption={123} initValue={20} /> //值设为数字
将会报错 => caption属性预期字符串,却得到数字类型。
<Counter initValue={20} /> // 将caption移除.
将会提示 =>caption是Counter必须的属性,却没有赋值
有了propTypes 的检查,可以容易发现对prop不正确的使用方法,尽早发现Code中错误。
没有 propTypes 定义,组件依然能正常工作,而且即使上例子中检查出错了,组件也能工作。 也就是说propTypes检查只是一个辅助开发功能,并不改变组件的行为。
使用方式: 在开发代码中定义propTypes, 避免在开发过程中犯错, 但在发布产品Code时,将它去掉,最终部署到产品环境的代码就会更优。 【现有的babel-react-optimize 有这个功能,可以在发布产品时自动将其去掉,但确保只在发布产品代码时使用】
[ React 的 state ]
驱动组件渲染过程的除了prop,还有 state ,它代表组件的内部状态。。 由于React组件不能修改传入的prop,所以需要记录自身数据变化,就得用state。
在Counter组件中,最初显示初始计数,是通过initValue这个prop来定制,在Counter已经被显示之后,用户会点击+&-按钮来改变该数,此刻变化数据就要Counter组件自己通过 state 来存储了。
[初始化 state]
通常在组件类的构造函数结尾处初始化 state,在Counter构造函数中,通过对this.state 的赋值完成了对组件state的初始化,Code:
constructor(props) {
...
this.state = {
count : props.initValue || 0
}
}
因initValue是一可选的props(无propTypes设置isRequired),要考虑 父组件没有指定这个props值的情况,所以优先使用传入属性的initValue,如没有,就使用默认值 0
组件的state
必须是一个Js对象
上面Code的判断逻辑充斥在组件的构造函数之中并不是美观的事情,而且容易有遗漏。
可使用 React 的 defaultProps
功能,让代码更加容易的读懂。
给Counter组件添加 defaultProps :
Counter.defaultProps = {
initValue: 0
};
有此设定,Counter 构造函数中的 this.state 初始化中可省去判断。 就算以后无指定initValue值, 在组件中也会收到一个默认属性值 0
[ 读取和更新state ]
通过给 button 的 onClick 属性挂载点击事件处理函数, 可以改变组件的state,以点击 + 按钮的响应函数为例: Code:
onClickDecrementButton() {
this.setState({count: this.state.count - 1});
}
代码中,通过 this.state 可以读取到组件的当前state。
改变state必须使用 this.setState
函数,不可直接修改this.state
。
直接修改this.state 的值,虽然事实上改变了数组的内部状态,但是只是野蛮修改了而言,却没驱动组件重新渲染,既然无重新渲染,当然不会反应this.state值的变化... 而this.setState()函数所做的事情,首先是改变了this.state的值,然后驱动组件经历更新过程渲染,这样才能让this.state里的值渲染到界面。
[ prop 和 state 的对比 ]
总结一下区别:
组件的state相当于 组件的记忆,存在的意义就是被修改&更新,每次通过this.setState 函数修改 state 就改变了组件的状态,然后通过渲染过程把变化体现出来。
组件不可修改传入的props值!! 设想父组件含多个子组件,如其中某个组件通过改变了这个props值,那么其他子组件读取得到的会是什么值?这种操作会导致程序陷入一片混乱,违背React设计初衷。
公式:UI = render(data)
React组件扮演的是render函数角色,应是一没有副作用的纯函数。修改props值会导致副作用~so要避免。
严格来说,React并无办法阻止修改传入的props对象,所以每个开发猿就当做是个规矩,在编码中一定不要踩这红线,不然会遇到预想不到的Bug..
易于维护组件的设计要素
任何一个复杂的应用,都是由一个简单的组件发展而来,当应用换很简单的时候(功能很少),可能一个组件就足够了, 但! 如果随着功能增多,把越来越多的功能都放在这个组件中,那么就会先得非常臃肿和难以管理。
就像一个人,只专注做好一件事一样,组件也应该保持一个组件只做一件事(尽量)。
当你发现组件功能太多功能和代码量太多时,就需要考虑拆分开这个组件,分解成多个小组件。 每个小组件只关注实现单个功能,单这些功能组合起来,也能满足复杂的实际需求。
这就是分而治之的策略。。把问题分解为多个小问题,这样既可 容易解决 & 便于维护。【但记得只有必要时才去分解,不要滥用】
拆分组件的关键 就是确定组件的边界,如果两个组件逻辑太紧密,无法清晰定义各自的责任,那也许这两个插件不该被拆开分解,而是作为同一个组件更合理。。
作为软件设计的通则,组件划分要满足