bunny笔记|React-进阶篇(理解生命周期)
第二章 进阶篇
React的生命周期
生命周期.png- 概念
组件的生命周期可以帮助我们清楚的剖析一个组件从创建到销毁的全部流程:
如果能够做到知其然且知其所以然,那么在后期多组件、中大型项目开发过程中,就能够很好的把控项目的性能细节。
创建时:constructor -> render -> React更新DOM和refs -> componentDidMount
在组件初始化阶段会执行
constructor
static getDerivedStateFromProps()
componentWillMount/USAFE_componentWillMount
render()
componentDidMount()
更新时:(New props、setState、forceUpdate)-> render -> React更新DOM和refs ->componentDidUpdata
props或state的改变可能会引起组件的更新,组件重新渲染的过程中会调用以下的方法
componentWillReceiveProps()
UNSAFE_componentWillReceiveProps()
static getDerivedStateFromProps()
shouldComponentUpdate()
componentWillUpdata()/USAFE_componentWillUpdata()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
卸载时(销毁):componentDidUnmount
componentWillUnmount()
错误处理
componentCatch()
- 钩子函数运转
钩子函数
class Life extends React.Component{
// 1)初始化阶段
constructor(props){
super(props);
console.log('constructor(props)');
}
componentWillMount(){
console.log('componentWillMount');
}
render(){
console.log('render()');
return (
<div>
<h2>测试组件的钩子函数!</h2>
</div>
)
}
componentDidMount(){
console.log('componentDidMount');
}
// 2) 更新阶段
componentWillUpdate(){
console.log('componentWillUpdate()');
}
componentDidUpdate(){
console.log('componentDidUpdate()');
}
// 3) 卸载阶段
componentWillUnmount(){
console.log('componentWillUnmount()');
}
// 4) 错误处理
componentDidCatch(){
console.log('componentDidCatch()');
}
}
ReactDOM.render(<Life/>, document.getElementById('app'));
- 钩子函数原理剖析
定时器演示钩子函数
class Life extends React.Component{
// 1)初始化阶段
constructor(props){
super(props);
console.log('constructor(props)');
this.state = {
age: 1
}
}
componentWillMount(){
console.log('componentWillMount');
}
render(){
console.log('render()');
return (
<div>
<h2>我是树妖,今年{this.state.age}岁了!</h2>
<button
onClick={()=>ReactDOM.unmountComponentAtNode(document.getElementById('app'))}>
我被砍了
</button>
</div>
)
}
componentDidMount(){
console.log('componentDidMount');
// 开启定时器
this.intervalId = setInterval(()=>{
console.log('定时器在工作了');
// 更新状态
this.setState({
age: this.state.age + 1
})
}, 1000);
}
// 2) 更新阶段
componentWillUpdate(){
console.log('componentWillUpdate()');
}
componentDidUpdate(){
console.log('componentDidUpdate()');
}
// 3) 卸载阶段
componentWillUnmount(){
console.log('componentWillUnmount()');
// 清除定时器
clearInterval(this.intervalId);
}
// 4) 错误处理
componentDidCatch(){
console.log('componentDidCatch()');
}
}
ReactDOM.render(<Life/>, document.getElementById('app'));
- Diff算法和虚拟DOM
虚拟DOM和diff算法是React中非常核心的两个概念,我们需要对此有一个很全面的认知!
对于我们用脚手架开发项目,尤其是企业中前后端分离的项目(类
似:后台管理系统)等有很大的帮助!
虚拟DOM内部执行流程
1)用JavaScript 对象结构表示DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中;
2)当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异:
3)把步骤2所记录的差异应用到步骤1所构建的真正的DOM树上视图就更新了。
虚拟DOM原理剖析
Virtual DOM 本质上就是在JS 和DOM 之间做了一个缓存。可以
类比CPU 和硬盘,硬盘读取速度比较慢,我们会就在它们之间加加缓存条,反之,既然DOM 运行速度慢,那么我们就在JS 和DOM 之间加个
缓存。JS只操作Virtual DOM,最后的时候再把变更的结果写入 DOM。
Diff算法
a)如果两棵树的根元素类型不同,React会销毁旧树,创建新树
b)对于类型相同的React DOM元素,React会对比两者的属性是否相同,只更新不同的属性:当外理完这个DOM节点,React就会递归处理子节点。
c)遍历插入元素,如果没有key,React将改变每一个子删除重新创建;为了解决这个问题,React提供了一个 key属性。当子节点带有key属性,Resct会通过key来匹配原始树和后来的树。
执行过程:通过绑定key,React就知道带有key '1024'的元素时新的,对于'1025'和'1026'仅仅移动位置即可。
key的使用注意点
九宫格布局案例
(要求:按照九宫格算法的要求,添加/删除盒子,并且做好边界值处理 考查知识点:虚拟DOM Diff算法 state管理)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>React 练习</title>
<style>
.box {width: 320px;height: 600px;background: url("images/bg.jpg") no-repeat;margin: 30px auto;}
.top {height: 60px;display: flex;justify-content: space-around;align-items: center;}
.top button {border: none;width: 100px;height: 36px;border-radius: 5px;color: #fff;}
.top button:first-child {background-color: orange;}
.top button:last-child {background-color: orangered;}
.bottom {width: 96%;height: 70%;margin-top: 15px;background-color: transparent;position: relative;margin-left: 2%;}
.item {display: flex;flex-direction: column;justify-content: center;align-items: center;position: absolute;}
span{font-size: 12px;}
</style>
</head>
<body>
<div id="app"></div>
<script src="../js/react.development.js"></script>
<script src="../js/react-dom.development.js"></script>
<script src="../js/babel.min.js"></script>
<script type="text/babel">
// 1. 组件类
class LKFlexView extends React.Component{
constructor(props){
super(props);
this.state = {
// 购物车数组
shopArr: []
}
}
static defaultProps = {
dataArr: [
{
"icon": "f1",
"name": "番茄"
},
{
"icon": "f2",
"name": "苹果"
},
{
"icon": "f3",
"name": "水蜜桃"
},
{
"icon": "f4",
"name": "香蕉"
},
{
"icon": "f5",
"name": "蓝莓"
},
{
"icon": "f6",
"name": "菠萝"
},
{
"icon": "f7",
"name": "草莓"
},
{
"icon": "f8",
"name": "猕猴桃"
},
{
"icon": "f9",
"name": "橙子"
}
]
};
render(){
return (
<div className="box">
{/*上部分*/}
<div className="top">
<button onClick={()=>this._addShop()}>Add</button>
<button onClick={()=>this._removeShop()}>Remove</button>
</div>
{/*下部分*/}
<div className="bottom">
{this.state.shopArr}
</div>
</div>
)
}
_addShop(){
// 1. 相关的变量
const cols = 3, shopW = 100, shopH = 120, width = 320, height = 420;
// 2. 取出数据
const {dataArr} = this.props;
// 3. 取出下标
const index = this.state.shopArr.length;
if(index >= 9){
alert('已经买了很多水果,不能再买了~');
return;
}
// 4. 求出子组件的行和列
const row = Math.floor(index / cols);
const col = Math.floor(index % cols);
console.log(row, col);
// 5. 求出当前的盒子的left和top
const xSpace = (width - cols * shopW) / (cols - 1);
const ySpace = (height - 3 * shopW) / 2;
const left = col * (shopW + xSpace);
const top = row * (shopH + ySpace);
// 6. 创建子组件装入数组
const shopView = (
<div className="item" style={{left, top}} key={index}>
<img src={'images/' + dataArr[index].icon + '.png'}
style={{width: shopW * 0.8, height:shopW*0.8}}
/>
<span>{dataArr[index].name}</span>
</div>
);
// 7. 更新状态
const tempArr = this.state.shopArr;
tempArr.push(shopView);
this.setState({
shopArr: tempArr
})
}
_removeShop(){
const tempArr = this.state.shopArr;
if(tempArr.length === 0){
alert('购物车空空如也~');
return;
}
tempArr.pop();
this.setState({
shopArr: tempArr
});
}
}
// 2.渲染组件
ReactDOM.render(
<LKFlexView/>,
document.getElementById('app')
)
</script>
</body>
</html>