前端技术栈更新飞快,日新月异,各种层出不穷的框架也是一个接一个,就目前来看是一个React、Vue、AngulaJs 三足鼎立的大局面。 AngulaJs了解不多暂且不说,但是像React、Vue这种我们经常使用的开发框架,我们要能理解其精髓,才能用的好。

React举例来说,最为核心的思想就是 数据驱动视图、组件化 ,而组件化解决的最大问题就是 代码复用。

而用好组件化的前提就是能够做好 模块拆分,模块拆分说白了就是一个化繁为简,提取公共部分和类似部分的过程

举例而言:
聊聊React - 图1
这是PMP系统潜客详情的一个页面,可以很清晰得到看到,凡是用红色框框住的部分都是可以组件化的部分,

有些甚至都不需要维护自身的state,更无须和状态管理器(Redux)相关联。

这样一来又引出了React世界里的组件的分类:

组件分类

按有无状态分类可分

  • stateful component 状态组件

  • stateless component 无状态组件

所谓的状态组件就是在组件的内部维护自身数据的组件,通过setState 修改自身的state,进而更新视图,但是不会影响到其他的组件和页面,

这种组件的使用场景一般是:

  • 不需要与其他组件通信

  • 不依赖其他组件的数据

  • 不与其他组件的变化相关联

所谓的无状态组件就是连自身的数据都不需要维护的组件,也可以称之为展示型组件,比如说一个Label,

单纯的只是做文本的展示,没有任何的变化和交互,就算有一些样式的变化,也完全可以通过暴露属性来定制。

比如:

| ```javascript import React from ‘react’;

const Label = ({ text=’label component’, style={} }) => { return (); }

export default Label;

  1. |
  2. | --- |
  3. **社区和广大开发者推荐,组件最好使用单文件夹的方式来书写,这样可以更好的划分代码,也符合复用的思想,出了问题也更好定位问题位置。**
  4. **而Vue得益于一个组件可以将html代码、js代码以及样式代码书写在一个文件内,所以一个文件就可以定义一个完整的组件。**
  5. 又上面代码可以看出,这就是一个简单的函数而已,接受一个option参数,通过ES6的对象延展语法取出两个属性:textstyle并且都有默认值。
  6. React的世界里所有的组件都可以用函数来实现,但是这种方式实现的组件仅仅满足于展示性的需求,
  7. 想要更多的交互和数据的更改、提交之类的复杂操作则需要**类组件(状态组件)**。
  8. 比如:
  9. | ```javascript
  10. import React from 'react';
  11. import Label from '../Label/Label.react';
  12. class StatefulComponent extends React.Component {
  13. constructor(props) {
  14. super(props);
  15. this.state = {
  16. text: 'default text'
  17. }
  18. }
  19. render() {
  20. const { text } = this.state;
  21. return (
  22. <div >
  23. <Label text={text} />
  24. <input value={text} onChange={e => {
  25. let text = e.currentTarget.value;
  26. this.setState({ text });
  27. }} />
  28. </div>
  29. );
  30. }
  31. }
  32. export default StatefulComponent;

| | —- |

不难看出,状态组件的编写方式就是通过ES6语法Class 的方式编写的,这种组件有着完备的生命周期过程(继承自React.Component),

可以在这些方法内做很多的文章,这里不多讲,后续会单独的做一些总结。

这个组件实现的效果很简单,自身state上的text会展示在Label组件上,输入框的value也和state.text绑定,输入的过程会调用onChange方法(一个匿名函数),

进而通过获取value值,setState,更新state.text值。

这只是一个简单的示例,其实上面的代码有一个缺点,那就是input标签的onChange方法使用了箭头匿名函数的方式书写,

这样的确很快捷和方便,但是对于组件的性能而言,并没有好处。看下面的代码:

| ```javascript import React from ‘react’; import Label from ‘../Label/Label.react’;

class StatefulComponent extends React.Component { constructor(props) { super(props); this.state = { text: ‘’ } }

handleChange = e => { let text = e.currentTarget.value; this.setState({ text }); }

render() { const { text } = this.state; return (

{/ { let text = e.currentTarget.value; this.setState({ text }); }} /> /}
); } }

export default StatefulComponent;

  1. |
  2. | --- |
  3. 能看到,我们把onChange方法提取出来,写到了组件的内部作为一个私有方法的方式存在,
  4. 而在标签的onChange那里,只传递一个handleChange函数的引用就行了。
  5. 这样的好处就是,当我们的组件有接收外界传入的props作为数据的时候,当props里的数据发生变化,
  6. 进而引起该组件的更新渲染,如果数据并没有与这个input有任何关系,那么这个input标签就不会进行渲染。
  7. 原因是这样的,每次当挂载一个组件的时候,都会重新在虚拟DOM层做一层对比(**Diff算法**),进而判断这个标签(组件)有没有必要重新渲染。
  8. 当我们通过匿名函数的方式实现onChange方法的时候,每一次挂载都会重新挂载一个崭新的input标签出来,虽然并没有打的影响,但是出于性能的考虑,我们就应该做的更好。
  9. 那就是通过传递引用的方式实现onChange方法,这样当Diff算法对这个input便签做对比的时候,会因为这个input标签的onChange方法的引用并没有发生变化,而不会重新挂载一个崭新的input标签上来。
  10. 这也正胡思我们需要达到的目的,因为完全没必要换一个新的input标签。这样一来我们就从最小的细节处优化了组件的性能。
  11. 说道性能优化不得不提一下**React.PureComponent**,有兴趣的自行Google
  12. <a name="yuargt"></a>
  13. ### 按书写方式分类
  14. - 函数组件
  15. - 类组件
  16. <a name="3zlqtn"></a>
  17. ### 按功能分类
  18. - 展示型组件
  19. - 容器组件
  20. - 复杂交互组件
  21. 关于展示型组件,Label组件的代码足以说明,复杂的交互组件,stateful组件的存在即使为了解决这个问题,而容器组件也很纯粹,只是一个Box(盒子)而已。
  22. 比如:
  23. | ```javascript
  24. //容器组件Box代码
  25. import styles from './Box.css';
  26. import React from 'react';
  27. const Box = ({ props, children }) => <div className='box' {...props}>{children}</div>;
  28. export default Box;
  29. //在类组件内部使用Box
  30. import React from 'react';
  31. import Label from '../Label/Label.react';
  32. import Box from '../Box/Box';
  33. class StatefulComponent extends React.Component {
  34. constructor(props) {
  35. super(props);
  36. this.state = {
  37. text: 'default text'
  38. }
  39. }
  40. handleChange = e => {
  41. let text = e.currentTarget.value;
  42. this.setState({ text });
  43. }
  44. render() {
  45. const { text } = this.state;
  46. return (
  47. <Box props={{ style: { padding: 10, border: '1px solid red' } }}>
  48. <Label text={text} />
  49. <br />
  50. <input value={text} onChange={this.handleChange} />
  51. </Box>
  52. );
  53. }
  54. }
  55. export default StatefulComponent;

| | —- |

效果如图:
聊聊React - 图2

生命周期

聊聊React - 图3
React 16.3 version

如上图所示,React stateful 组件的生命周期分为三个阶段:

  1. Mounting (挂载阶段)

  2. Updating(更新阶段)

  3. Unmounting (卸载阶段)

其实严格意义上还有一个阶段:存在阶段,也就是没有发生任何变化,存在在DOM树上的阶段。

下面看一段代码:

| ```javascript import styles from ‘./LifeCircle.css’

import React from ‘react’ import reactDom from ‘react-dom’

class LifeCircle extends React.Component { constructor(props) { //构造方法,初始化阶段 super(props) console.log(‘constructor’) this.state = { text: ‘Button’ } }

componentWillMount() { //即将挂载,挂载前的准备阶段 console.log(‘componentWillMount’) }

handleClick = () => { //触发点击事件,通过setState方法修改状态,引发生命周期对的再次执行,进而一起视图的变化 console.log(‘click’) this.setState({ text: ‘anything’ }) }

componentWillReceiveProps(nextProps) { console.log(‘componentWillReceiveProps’) //这是当父组件传递进来的属性值发生变化的时候执行的钩子函数 // 可以在这里做一些操作数据的行为 }

shouldComponentUpdate(nextProps, nextState) { console.log(‘shouldComponentUpdate’) //该生命周期方法有两个参数,即nextProps, nextState,可以在这个方法内做一些判断 //进行组件的性能优化,该方法必须有返回值, //类型为Boolean(true 代表可以发生更新过程,false代表即使state或者props发生了变化也不会引发视图的更新) return true }

componentWillUpdate() { console.log(‘componentWillUpdate’) //即将更新视图前的阶段 }

render() { console.log(‘render’) //render方法贯穿于挂载和更新阶段,是视图发生变化的核心方法 return (

) }

componentDidUpdate(preProps, preState) { console.log(‘componentDidUpdate’) //这两个参数分别是上一刻的属性和状态值 //视图更新完毕后的钩子函数 }

componentDidMount() { console.log(‘componentDidMount’) //挂载完毕的方法,在这里虚拟DOM对应的节点均已挂载到真实的DOM树上,可以访问到任何一个节点。比如: const btnDom = document.getElementById(‘button’) btnDom ? console.log(‘button is in the dom tree for real!’) : ‘’ }

componentWillUnmount() { console.log(‘componentWillUnmount’) //组件卸载前的钩子函数 }

} export default LifeCircle ``` | | —- |

效果图结合代码食用更佳

首次挂载:
聊聊React - 图4
点击更新:
聊聊React - 图5
卸载挂载:
聊聊React - 图6