React组件化开发

React组件思想 – 分而治之

根据组件定义方式

  • 函数组件:functional Component

    • 没有生命周期 也会被更新并挂载,但是没有生命周期函数
    • 没有this对象
    • 没有内部状态
  • 类: Class Component

    • 组件名称大写字符开头(不管类还是函数组件)
    • jsx中标签要小写
      • 因为标签大写会被当成组件
    • 类组件需要继承自 React.Component
    • 类组件必须实现render函数

Render函数的返回值

当render被调用 ,他会检查 this.props和this.state的变化,并返回一下类型之一:

  • React元素
    • 通常由JSX创建
  • 数组或者fragments:使render方法可以返回多个元素
  • Portals
    • 可以渲染子节点到不同的DOM子树中
  • 字符串 数值类型
  • 布尔类型或者null :什么都不渲染

根据组件内部是否有状态需要维护

  • 无状态组件
  • 有状态组件

根据组件的职责

  • 展示型组件
  • 容器型组件

生命周期

-从销毁到创建的过程

生命周期 – 分为很多阶段

  • 装载阶段(Mount)
  • 更新过程(Updating)
  • 卸载过程(Unmounting)

生命周期函数

-React通过生命周期函数告诉我们当前处于什么阶段

生命周期解析

  • Constructor

    • 如果不初始化state 或不进行方法绑定,则不需要为React组件实现构造函数
    • Constructor通常只做两件事情
      • 通过给this.state赋值对象来初始化内部的state
      • 为事件绑定实例(this).
  • componentDidMount

    • componentDidMount()会在组件挂载后(插入DOM树中)立即调用
    • componentDidMount中通常进行的操作:
      • 依赖DOM操作可以在这里进行
      • 在这里发送网络请就是最好的地方
      • 可以在此处添加一些订阅(会在componentWillUnmount取消订阅)
  • componentDidUpdate(prevProps,prevState,snapshot)

    • 会在更新后会被立即调用,首次渲染不会执行此方法。
    • 当组件更新后,可以在此处对 DOM 进行操作;
    • 如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求;(例如,当 props 未发生变化时,则不会执行网络请求)。
  • componentWillUnmount

    • componentWillUnmount() 会在组件卸载及销毁之前直接调用。
    • 在此方法中执行必要的清理操作

不常用生命周期函数

  • getDerivedStateFromProps:
    • state 的值在任何时候都依赖于 props时使用;该方法返回一个对象来更新state;
  • getSnapshotBeforeUpdate:
    • 在React更新DOM之前回调的一个函数,可以获取DOM更新前的一些信息(比如说滚动位置);
  • shouldComponentUpdate(SCU):
    • 决定render渲染

组件数据传递

组件的嵌套

组件之间通信

父组件传递给子组件 - 函数|类组件

类组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import React, { Component } from 'react'


class ChildCpn extends Component {
// 不用写构造器 因为默认 constructor(props){super(props)}

render() {
const { name, age, height } = this.props //实际上是自己的props
return (
<div>
<h2>子组件展示:{name + " " + age + " " + height}</h2>
</div>
)
}
}

export default class App extends Component {
render() {
return (
<div>
<ChildCpn name="hypp" age="18" height="1.80" />
<ChildCpn name="emi" age="44" height="1.78" />
</div>
)
}
}

源码中:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* Base class helpers for the updating state of a component.
*/
Component 传入了props 并且对props进行了保存
function Component(props, context, updater) {
this.props = props;
this.context = context;
// If a component has string refs, we will assign a different object later.
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}

就是将父组件中的数据放在了props super(props), super(props)相当于将Component中this 改变为子类中的this Component中保存传入props 所以保存了props

继承的本质:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 var Person = /*#__PURE__*/function () {
function Person(name, age) {
_classCallCheck(this, Person);

this.name = name;
this.age = age;
}

_createClass(Person, [{
key: "run",
value: function run() {
console.log('run');
}
}]);

return Person;
}();

var Student = /*#__PURE__*/function (_Person) {
_inherits(Student, _Person); //inherits实现继承 让Student和Person产生联系

var _super = _createSuper(Student); //拿到super的构造函数

function Student(name, age, sno) {
var _this;

_classCallCheck(this, Student);

_this = _super.call(this, name, age); //将Student的this 给了父类的调用
_this.sno = sno;
return _this;
}

return Student;
}(Person);
函数组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React, { Component } from 'react'


function ChildCpn(props){
const{name,age,height}=props

return (
<h2>{name+age+height}</h2>
)
}

export class App extends Component {
render() {
return (
<div>
<ChildCpn name="hypp" age="18" height="1.80" />
<ChildCpn name="emi" age="44" height="1.78" />
</div>
)
}
}

export default App

属性验证
  • prop-types

子组件传递父组件

某些情况,我们也需要子组件向父组件传递消息:

  • 在vue中是通过自定义事件来完成的;
  • 在React中同样是通过props传递消息,只是让父组件给子组件传递一个回调函数,在子组件中调用这个函数即可;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import React, { Component } from 'react'


class Counterbtn extends Component {
render() {
const{ btnClick } = this.props
return (
<button onClick={btnClick}>+1</button>
)
}
}


export default class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
}
}

render() {
return (
<div>
<h2>当前计数:{this.state.counter}</h2>
<button onClick = {e=>this.increment()}>+</button>
<Counterbtn btnClick={e=>this.increment()} name = "hyp" ></Counterbtn>
</div>
)
}
increment() {
this.setState({
counter: this.state.counter + 1
})
}
}

React 实现插槽 (slot)

源码中的createElement

React.createElement的函数调用 config中存放的属性 然后对config进行了遍历 然后放到了props里面 props.children = childArray,由于源码中的这步操作 所以props中的children 里面存放的是标签中的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
export function createElement(type, config, children) {
let propName;

// Reserved names are extracted
const props = {};

let key = null;
let ref = null;
let self = null;
let source = null;

if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;

if (__DEV__) {
warnIfStringRefCannotBeAutoConverted(config);
}
}
if (hasValidKey(config)) {
key = '' + config.key;
}

self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// Remaining properties are added to a new props object
for (propName in config) {
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}

// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}

// Resolve default props
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (__DEV__) {
if (key || ref) {
const displayName =
typeof type === 'function'
? type.displayName || type.name || 'Unknown'
: type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (ref) {
defineRefPropWarningGetter(props, displayName);
}
}
}
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
实现插槽两种方案:
  • 1.0

  • //App.js
    
    <NavBar>
          <span>1</span>
          <strong>1</strong>
          <span>1</span>
    </NavBar>
    
    //NavBar.js
    <div className="nav-bar nav-item">
            <div className="nav-left">
            {this.props.children[0]}
            </div>
            <div className="nav-center">
            {this.props.children[1]}
            </div>
             <div className="nav-right">
            {this.props.children[2]}
            </div>
    </div>
    
    <!--hexoPostRenderEscape:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">* 2.0</span><br><span class="line"></span><br><span class="line">* &#96;&#96;&#96;js</span><br><span class="line">  &#x2F;&#x2F;App.js</span><br><span class="line">  </span><br><span class="line">  &lt;NavBar2 leftSlot&#x3D;&#123;&lt;span&gt;1&lt;&#x2F;span&gt;&#125;</span><br><span class="line">      centerSlot&#x3D;&#123;&lt;strong&gt;1&lt;&#x2F;strong&gt;&#125;</span><br><span class="line">      rightSlot &#x3D; &#123; &lt;span&gt;3&lt;&#x2F;span&gt;&#125;</span><br><span class="line">  &#x2F;&gt;</span><br><span class="line">                     </span><br><span class="line">  &#x2F;&#x2F;NavBar2.0.js</span><br><span class="line">  &lt;div className&#x3D;&quot;nav-bar nav-item&quot;&gt;</span><br><span class="line">      &lt;div className&#x3D;&quot;nav-left&quot;&gt;</span><br><span class="line">      &#123;leftSlot&#125;</span><br><span class="line">      &lt;&#x2F;div&gt;</span><br><span class="line">      &lt;div className&#x3D;&quot;nav-center&quot;&gt;</span><br><span class="line">          &#123;centerSlot&#125;</span><br><span class="line">      &lt;&#x2F;div&gt;</span><br><span class="line">      &lt;div className&#x3D;&quot;nav-right&quot;&gt;</span><br><span class="line">      &#123;rightSlot&#125;</span><br><span class="line">      &lt;&#x2F;div&gt;</span><br><span class="line">  &lt;&#x2F;div&gt;</span><br></pre></td></tr></table></figure>:hexoPostRenderEscape-->