1. 组件的生命周期
需求:定义组件实现以下功能:
1. 让指定的文本做显示 / 隐藏的渐变动画
2. 从完全可见,到彻底消失,耗时2S
3. 点击“不活了”按钮从界面中卸载组件
1) 引出生命周期
//生命周期回调函数 <=> 生命周期钩子函数 <=> 生命周期函数 <=> 生命周期钩子
//render调用的时机是2个:初始化渲染、状态更新之后
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset = "UTF-8">
<meta name = "viewport" content="width=device-width, initial-scale=1.0">
<title>1_引出生命周期</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id = "test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"> </script>
<!-- 引入react-dom 用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转化为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
//创建组件
//生命周期回调函数 <=> 生命周期钩子函数 <=> 生命周期函数 <=> 生命周期钩子
class Life extends React.Component{
state = {opacity:1}
death = () => {
// //清除定时器
// clearInterval(this.timer)
//卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
//组件挂载完毕调用
componentDidMount(){
this.timer = setInterval(() => {
//获取原状态
let {opacity} = this.state
//减小0.1
opacity -= 0.1
if(opacity <= 0) opacity = 1
//设置新的透明度
this.setState({opacity})
},200)
}
//组件将要卸载时调用
componentWillUnmount(){
//清除定时器
clearInterval(this.timer)
}
//render调用的时机是2个:初始化渲染、状态更新之后
render(){
return(
<div>
<h2 style={{opacity:this.state.opacity}}>React学不会怎么办?</h2>
<button onClick={this.death}>不活了</button>
</div>
)
}
}
//渲染组件
ReactDOM.render(<Life/>,document.getElementById('test'))
</script>
</body>
</html>
2)组件生命周期的三个阶段(旧)
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1. constructor()
2. componentWillMount()
3. render()
4. componentDidMount()
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1. shouldComponentUpdate()
2. componentWillUpdate()
3. render()
4. componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset = "UTF-8">
<meta name = "viewport" content="width=device-width, initial-scale=1.0">
<title>2_react生命周期(旧)</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id = "test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"> </script>
<!-- 引入react-dom 用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转化为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
/*
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1. constructor()
2. componentWillMount()
3. render()
4. componentDidMount() ===> 常用**
一般在这个钩子中,做一些初始化的事。例如:开启定时器,发送网络请求,订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1. shouldComponentUpdate()
2. componentWillUpdate()
3. render() ===> 必用**
4. componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount() ===> 常用**
一般在这个钩子中做一些收尾的事。例如:关闭定时器,取消订阅消息,
*/
//创建组件
class Count extends React.Component{
constructor(props){
console.log('Count-constructor');
super(props)
//初始化状态
this.state = {count:0}
}
//加1按钮的回调
add = () => {
//获取愿状态
const{count} = this.state
//更新状态
this.setState({count:count+1})
}
//卸载组件按钮的回调
death = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
//强制更新按钮的回调
force = () => {
this.forceUpdate()
}
//组件将要挂载的钩子
componentWillMount(){
console.log("Count-componentWillMount");
}
//组件挂载完毕的钩子
componentDidMount(){
console.log('Count-componentDidMount');
}
//组件将要卸载的钩子
componentWillUnount(){
console.log('Count-componentWillUnount');
}
//控制组件更新的阀门
shouldComponentUpdate(){
console.log('Count-shouldComponentUpdate');
return false
}
//组件将要更新的钩子
componentWillUpdate(){
console.log('Count-componentWillUpdate');
}
//组件更新完毕的钩子
componentDidUpdate(){
console.log('Count-componentDidUpdate');
}
render(){
console.log('Count-render');
const{count} = this.state
return(
<div>
<h2>当前求和为:{count}</h2>
<button onClick={this.add}>点我+1</button>
<button onClick={this.death}>卸载组件</button>
<button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button>
</div>
)
}
}
//父组件A
class A extends React.Component{
//初始化状态
state={carName:'benz'}
changeCar = () => {
this.setState({carName:'auto'})
}
render(){
return(
<div>
<div>我是A组件</div>
<button onClick={this.changeCar}>换车</button>
<B carName={this.state.carName}/>
</div>
)
}
}
//子组件B
class B extends React.Component{
//组件将要接收新的props的钩子
componentWillReceiveProps(){
console.log('B-componentWillReceiveProps');
}
//控制组件更新的阀门
shouldComponentUpdate(){
console.log('B-shouldComponentUpdate');
return true
}
//组件将要更新的阀门
componentWillUpdate(){
console.log('B-componentWillUpdate');
}
//组件更新完毕的阀门
componentDidUpdate(){
console.log('B-componentDidUpdate');
}
render(){
console.log('B-render');
return(
<div>我是B组件,接收到的车是:{this.props.carName}</div>
)
}
}
//渲染组件
ReactDOM.render(<A/>,document.getElementById('test'))
</script>
</body>
</html>
3) 组件的生命周期(新)
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1. constructor()
2. getDerivedStateFromProps
3. render()
4. componentDidMount() ===> 常用**
一般在这个钩子中,做一些初始化的事。例如:开启定时器,发送网络请求,订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1. getDerivedStateFromProps
2. shouldComponentUpdate()
3. render() ===> 必用**
4. getSnapshotBeforeUpdate
5. componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount() ===> 常用**
一般在这个钩子中做一些收尾的事。例如:关闭定时器,取消订阅消息,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset = "UTF-8">
<meta name = "viewport" content="width=device-width, initial-scale=1.0">
<title>3_react生命周期(新)</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id = "test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/17/react.development.js"> </script>
<!-- 引入react-dom 用于支持react操作DOM -->
<script type="text/javascript" src="../js/17/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转化为js -->
<script type="text/javascript" src="../js/17/babel.min.js"></script>
<script type="text/babel">
//创建组件
class Count extends React.Component{
constructor(props){
console.log('Count-constructor');
super(props)
//初始化状态
this.state = {count:0}
}
//加1按钮的回调
add = () => {
//获取愿状态
const{count} = this.state
//更新状态
this.setState({count:count+1})
}
//卸载组件按钮的回调
death = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
//强制更新按钮的回调
force = () => {
this.forceUpdate()
}
//从props得到一个衍生的状态,使用场景及其罕见
//若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
static getDerivedStateFromProps(props,state){
console.log('getDrivedStateFromProps',props,state);
return null
}
//在更新之前获取快照,此用法并不常见
getSnapshotBeforeUpdate(){
console.log('getSnapshotBeforeUpdate');
return 'atguigu'
}
//组件挂载完毕的钩子
componentDidMount(){
console.log('Count-componentDidMount');
}
//组件将要卸载的钩子
componentWillUnount(){
console.log('Count-componentWillUnount');
}
//控制组件更新的阀门
shouldComponentUpdate(){
console.log('Count-shouldComponentUpdate');
return true
}
//组件更新完毕的钩子
componentDidUpdate(preProps,preState,snapshotValue){
console.log('Count-componentDidUpdate',preProps,preState,snapshotValue);
}
render(){
console.log('Count-render');
const{count} = this.state
return(
<div>
<h2>当前求和为:{count}</h2>
<button onClick={this.add}>点我+1</button>
<button onClick={this.death}>卸载组件</button>
<button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button>
</div>
)
}
}
//渲染组件
ReactDOM.render(<Count count={199}/>,document.getElementById('test'))
</script>
</body>
</html>
4) 重要的钩子与即将废弃的钩子
重要的钩子
1. render:初始化渲染或更新渲染调用
2. componentDidMount:开启监听, 发送ajax请求
3. componentWillUnmount:做一些收尾工作, 如: 清理定时器
即将废弃的钩子
1. componentWillMount
2. componentWillReceiveProps
3. componentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。
5) getSnapshotBeforeUpdate的使用场景
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset = "UTF-8">
<meta name = "viewport" content="width=device-width, initial-scale=1.0">
<title>4_getSnapshotBeforeUpdate的使用场景</title>
<style>
.list{
width:200px;
height: 150px;
background-color: skyblue;
overflow: auto;
}
.news{
height: 30px;
}
</style>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id = "test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/17/react.development.js"> </script>
<!-- 引入react-dom 用于支持react操作DOM -->
<script type="text/javascript" src="../js/17/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转化为js -->
<script type="text/javascript" src="../js/17/babel.min.js"></script>
<script type="text/babel">
class NewList extends React.Component{
state = {newsArr:[]}
componentDidMount(){
setInterval(() => {
//获取状态
const{newsArr} = this.state
//模拟一条新闻
const news = '新闻'+(newsArr.length+1)
//更新状态
this.setState({newsArr:[news,...newsArr]})
},1000)
}
getSnapshotBeforeUpdate(){
return this.refs.list.scrollHeight
}
componentDidUpdate(preProps,preState,height){
this.refs.list.scrollTop += this.refs.list.scrollHeight - height
}
render(){
return(
<div className="list" ref='list'>
{
this.state.newsArr.map((n,index) => {
return <div key = {index} className = 'news'>{n}</div>
})
}
</div>
)
}
}
ReactDOM.render(<NewList/>,document.getElementById('test'))
</script>
</body>
</html>
6) 虚拟DOM与DOM Diffing算法
基本原理图
a. 验证Diffing算法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset = "UTF-8">
<meta name = "viewport" content="width=device-width, initial-scale=1.0">
<title>1_验证Diffing算法</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id = "test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/17/react.development.js"> </script>
<!-- 引入react-dom 用于支持react操作DOM -->
<script type="text/javascript" src="../js/17/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转化为js -->
<script type="text/javascript" src="../js/17/babel.min.js"></script>
<script type="text/babel">
class Time extends React.Component{
state = {date:new Date()}
componentDidMount(){
setInterval(() => {
this.setState({
date:new Date()
})
,1000})
}
render(){
return(
<div>
<h1>Hello</h1>
<input type="text" />
<span>
现在是:{this.state.date.toTimeString()}
<input type="text" />
</span>
</div>
)
}
}
ReactDOM.render(<Time/>,document.getElementById('test'))
</script>
</body>
</html>
b. key的作用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset = "UTF-8">
<meta name = "viewport" content="width=device-width, initial-scale=1.0">
<title>2_key的作用</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id = "test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/17/react.development.js"> </script>
<!-- 引入react-dom 用于支持react操作DOM -->
<script type="text/javascript" src="../js/17/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转化为js -->
<script type="text/javascript" src="../js/17/babel.min.js"></script>
<script type="text/babel">
class Person extends React.Component{
state = {
persons:[
{id:1,name:'tim',age:11},
{id:2,name:'tom',age:1},
]
}
add = () => {
const{persons} = this.state
const p = {id:persons.length+1,name:'小王',age:20}
this.setState({persons:[p,...persons]})
}
render(){
return(
<div>
<h2>展示人员信息</h2>
<button onClick={this.add}>添加一个小王</button>
<h3>使用index(索引值)作为key</h3>
<ul>
{
this.state.persons.map((personObj,index) => {
return <li key={index}>{personObj.name}----{personObj.age} <input type="text"/> </li>
})
}
</ul>
<hr/>
<hr/>
<h3>使用id(数据的唯一标识)作为key</h3>
<ul>
{this.state.persons.map((personObj) => {
return <li key={personObj.id}>{personObj.name}----{personObj.age} <input type="text"/> </li>
})}
</ul>
</div>
)
}
}
ReactDOM.render(<Person/>,document.getElementById('test'))
</script>
</body>
</html>