组件的渲染机制
- render 渲染的时候,如果发现type是一个
- 字符串: 创建元素标签
- 函数:把函数执行,把解析出来的props当实参传递给函数
- 单闭合调用: 不能书写子节点 [没有children]
- 双闭合调用: 可以有children -> 实现出类似vue中插槽(slot)的概念, 有助于复用性
- 类: 把类基于 new 执行,创造其它一个实例
- children 的情况
- 无: children -> undefined
- 1个: children -> 等传过来的东西
- 多个: children -> 是一个数组
- props: 是被冻结的对象 -> Object.isFrozen(props) -> true
- 不能删除/不能新增/不能劫持
- 给组件设置默认值: 如下的 FuncComponent.defaultProps
- 给props设置规则:
- 安装 prop-type: 具体去 prop-types 官网 github.com/facebook/pr…
- or 使用 typescript
// => App.jsx
import FuncComponent from "./pages/funcComp";
function App() {
const render = () => {
return <div>我是render函数</div>
}
return (
<div className="App">
<FuncComponent
x={20}
y="30"
arr={[10,20,30]}
render={render}
>
// => 命名插槽的的模拟
<span className="slot" slot="header">我是children-头部</span>
<span className="slot" slot="footer">我是children-尾部</span>
</FuncComponent>
</div>
);
}
export default App;
// => FuncComponent.jsx
import React from 'react'
import PropTypes from 'prop-types'
function FuncComponent(props) {
const {
x,
y,
arr,
children,
render,
num
} = props;
console.log(Object.isFrozen(props)); // true
// => 命名插槽的的模拟
const headSlots = children.filter(item => item.props.slot === 'header');
const footSlots = children.filter(item => item.props.slot === 'footer');
return (
<div>
<h1>FuncComponent</h1>
{ headSlots }
<h1>children: { children[0] }</h1>
<p>x: {x}</p>
<p>y: {y}</p>
{
arr && arr.map((item, index) => {
return (
<div key={index}>{ item }</div>
)
})
}
<div>
render: { render() }
</div>
<h2>children: { children[1] }</h2>
{ footSlots }
</div>
)
}
// 默认值
FuncComponent.defaultProps = {
num: 100
}
// 给props设置规则
FuncComponent.prototype = {
x: PropTypes.number.isRequired,
y: PropTypes.string,
arr: PropTypes.array
}
export default FuncComponent
函数组件的视图更新: 就是让函数重新执行
- 函数组件是静态组件
- 不具备状态/生命周期/ref等
- 第一次渲染完毕,除非父组件控制渲染,否则内容不会再更新
- 优势渲染速度快
- 弊端: 静态组件,无法实现组件动态更新
类组件
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class ClassComp extends Component {
// 默认值
static defaultProps = {
x: 0,
y: 0
}
// 属性规则
static propTypes = {
x: PropTypes.number,
y: PropTypes.number,
}
shouldComponentUpdate(nextProps, nextState) {
console.log(this.props, this.state, nextProps, nextState, 'shouldComponentUpdate');
return true;
}
state = {
num: 10
}
constructor(props) {
super();//写了constructor 必须super
console.log(this.props, 'constructor');
}
render() {
let { num } = this.state;
console.log(this.props, this.state, 'render');
return (
<div>
<p>{ num }</p>
<button onClick={() => {
this.setState({
num : 200
},() => {
console.log(this.state.num, 'state callback')
})
}}>plus</button>
</div>
)
}
}
// undefined 'constructor'
// {x: 0, y: 0} {num: 10} 'render'
// {x: 0, y: 0} {num: 10} {x: 0, y: 0} {num: 200} 'shouldComponentUpdate'
// {x: 0, y: 0} {num: 200} 'render'
// 200 'state callback'
- 渲染流程总结
new ClassComp([props])
@1 getDefaultProps && 属性规则校验
@2 初始化
+ 把constructor执行,把处理好的props传递给constructor
* super() 等价于 React.Component.call(this)
* this.props = undefined
* this.refs = {}
* this.context = undefined
* this.updater ={...}
* super(props) 此处直接把传递进来的props挂载到实例上去
* this.props = {...}
* ...
@3 初始化结束后,会把props/context这些信息挂载到实例上去
+ this.props={...}
+ this.context = {...}
@4 触发一个生命周期 componentWillMount: 第一次render之前
+ 不安全的周期函数
+ UNSAFE_componentWillMount [在React.StrictMode严格模式下会报错]
@5 触发render函数
+ 把render执行返回的JSX元素对象(虚拟dom对象)进行渲染
+ **render函数必须有**,必须返回jsx元素
@6 触发componentDidMount生命周期函数: 第一次渲染完
+ 获取真实的DOM
+ 从服务器获取数据
+ 设置定时器或者监听器等
基于setState修改状态,通知视图更新
@a 触发shouldComponentUpdate:是否允许更新
+ 返回true: 允许更新,继续执行后续的步骤
+ 返回false: 停止更新,状态/属性值也不会进行修改,视图也不会更新
+ 可以基于这个生命周期做项目的性能优化(一般不会去动这个函数)
@b 触发componentWillUpdate: 不安全的周期函数
+ 进行到这一步,属性和状态还没修改为最新的值
@c 修改状态/属性为新的值,基于this.props/state访问,获取最新的值
@d 触发render: 视图重新渲染
@e 触发componentDidUpdate视图更新完毕
@f 如果setState有回调函数,则在视图更新完毕后(componentDidUpdate后),执行回调函数
+ 特殊: 即便shouldComponentUpdate返回false,此回调函数也会被触发 获取this.state.xxx是新的只是不刷新
组件销毁
@1 触发componentWillUnMount 生命周期函数: 销毁之前
+ 把监听器,定时器等手动释放掉
+ 把组件中的一些信息做缓存(信息草稿箱)
@2 销毁