HuangHongRui / Notebook

:pencil2: Yeah.. This's My NoteBook...:closed_book:
0 stars 0 forks source link

React[4](设计高质量的组件) #39

Open HuangHongRui opened 7 years ago

HuangHongRui commented 7 years ago

不要只满足于编写出可运行的代码, 而要了解代码背后的工作原理。 不要只满足于编写的程序能够运行, 还要让代码可读而且易于维护。

易于维护组件的设计要素

任何一个复杂的应用,都是由一个简单的组件发展而来,当应用换很简单的时候(功能很少),可能一个组件就足够了, 但! 如果随着功能增多,把越来越多的功能都放在这个组件中,那么就会先得非常臃肿和难以管理。

就像一个人,只专注做好一件事一样,组件也应该保持一个组件只做一件事(尽量)

当你发现组件功能太多功能和代码量太多时,就需要考虑拆分开这个组件,分解成多个小组件。 每个小组件只关注实现单个功能,单这些功能组合起来,也能满足复杂的实际需求。

这就是分而治之的策略。。把问题分解为多个小问题,这样既可 容易解决 & 便于维护。【但记得只有必要时才去分解,不要滥用】

拆分组件的关键 就是确定组件的边界,如果两个组件逻辑太紧密,无法清晰定义各自的责任,那也许这两个插件不该被拆开分解,而是作为同一个组件更合理。。

作为软件设计的通则,组件划分要满足

HuangHongRui commented 7 years ago

高内聚:指的是把逻辑紧密相关的内用放在一起。用户界面主要是内容&交互行为&样式。传统上,内容由HTML表示,交互由Js文件,样式由css文件定义。虽然满足功能模块的需要,却要放在三个不同文件中!!这是不满足高内聚的原则...但React不同,展示内容的JSX,定义行为的Js代码,甚至定义Css都可放在一个Js文件中,因为它们本身就是为实现同一个目的存在的..所以React天生具备高内聚特点。

低耦合: 指的是不同组件之间的依赖关系要尽量弱化,也就是每个组件尽量独立。保持整个系统的低耦合度,需要对系统中的功能有充分的认识,然后根据功能点划分模块,让不同的组件去实现不同的功能,这个功夫还在开发者身上,不过React组件的对外接口非常规范,方便开发者设计低耦合的系统。

对于React,要充分应用以上2原则,需要对React特性有充分了解。

HuangHongRui commented 7 years ago

React 组件的数据

差劲的程序猿操心代码,优秀的程序猿操心数据结构和它们之间的关系

React组件的数据分为 propstate , 无论是哪者改变,都可能引起组件之间的重新渲染。

prop 是组件的对外接口, state是组件的内部状态,对外用prop,内部用state。

[React 的 prop]

prop 是 property的简写,是从外部传递给组件的数据, 一个组件通过定义自己能够接受的prop就定义了自己的对外公共接口。

每个组件都是独立存在的模块, 组件之间的一切都是外部世界,外部世界就是通过prop来和组件对话的。

HuangHongRui commented 7 years ago

[给 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,可带上必要的参数, 这样就反过来把信息传递给外部世界

HuangHongRui commented 7 years ago

[ 读取 prop 值 ]

如果一个组件需要定义组件的构造函数, 一定要记得在构造函数的第一行通过 super 调用父类(React.Component)的构造函数。

如在构造函数中没调用 super(props) ,那么组件实力被构造后, 类实例的所有成员函数就无法通过 this.props 访问到父组件传递过来的props值。 this.props 赋值是 React.Component 构造函数的工作之一

在构造函数中给函数绑定了当前this的执行环境,是因Es6方法创建的组件类并不自动给我们绑定this到当前实例对象。

在构造函数中可以通过参数props获得传入的prop值, 在其他函数中则可通过this.props访问传入prop的值。

HuangHongRui commented 7 years ago

[ 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属性预期字符串,却得到数字类型。 含义:caption属性预期字符串,却得到数字类型。

<Counter initValue={20} /> // 将caption移除.

将会提示 =>caption是Counter必须的属性,却没有赋值 含义:caption是Counter必须的属性,却没有赋值

有了propTypes 的检查,可以容易发现对prop不正确的使用方法,尽早发现Code中错误。

没有 propTypes 定义,组件依然能正常工作,而且即使上例子中检查出错了,组件也能工作。 也就是说propTypes检查只是一个辅助开发功能,并不改变组件的行为。

使用方式: 在开发代码中定义propTypes, 避免在开发过程中犯错, 但在发布产品Code时,将它去掉,最终部署到产品环境的代码就会更优。 【现有的babel-react-optimize 有这个功能,可以在发布产品时自动将其去掉,但确保只在发布产品代码时使用】

HuangHongRui commented 7 years ago

[ 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里的值渲染到界面。

HuangHongRui commented 7 years ago

[ prop 和 state 的对比 ]

总结一下区别:

组件的state相当于 组件的记忆,存在的意义就是被修改&更新,每次通过this.setState 函数修改 state 就改变了组件的状态,然后通过渲染过程把变化体现出来。

组件不可修改传入的props值!! 设想父组件含多个子组件,如其中某个组件通过改变了这个props值,那么其他子组件读取得到的会是什么值?这种操作会导致程序陷入一片混乱,违背React设计初衷。

公式:UI = render(data) React组件扮演的是render函数角色,应是一没有副作用的纯函数。修改props值会导致副作用~so要避免。

严格来说,React并无办法阻止修改传入的props对象,所以每个开发猿就当做是个规矩,在编码中一定不要踩这红线,不然会遇到预想不到的Bug..