目录
1. React的概述
什么是react?
React 是一个用于构建用户界面的 JavaScript 库。
用户界面:HTML页面(前端) React 主要用来写HTML页面,或构建Web应用
如果从 MVC 的角度来看,React 仅仅是视图层(V),也就是只负责视图的渲染,而并非提供了 完整的 M 和 C 的功能。
1.1 React的特点
- 声明式
- 基于组件
- 学习一次,随处使用
声明式:
TableLogic你只需要描述 UI(HTML)看起来是什么样,就跟写HTML一样 React 负责渲染 UI,并在数据变化时更新 UI
const jsx = <div className="app">
<h1>Hello React! 动态变化数据:{count}</h1></div>
1.2 React的基本使用
安装React:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"></meta>
<meta name="viewport" content="width-device-width,initial-scale=1.0"></meta>
<meta http-equiv="X-UA-Compatible" content="ie=edge"></meta>
<title>01-react的基本使用</title>
</head>
<body>
<div id="root"></div>
<!--1. 引入react和react-dom两个文件
但是注意: 需要先引入react 在引入react-demo -->
<script src="./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
<script>
//2. 创建react元素
//参数1: 元素名称
//参数2: 元素属性
//参数3: 元素的子节点 第三个参数以后的都是子节点
const title=React.createElement("h1",{
id:"我是id",
name:"我是名字"
},"hello world",React.createElement("span",{id:"span_id"},"我是span"))
//3.渲染react元素
//需要渲染的react元素
//挂载点
ReactDOM.render(title,document.getElementById("root") )
</script>
</body>
</html>
1.3 React手脚架的使用
1.3.1 在脚手架中使用React:
//1. 导入react
import React from 'react'
import ReactDOM from 'react-dom'
//函数组件:
/* function Hello(){
return(
<div>这是我的第一个组件</div>
)
} */
const Hello=()=><div>这是我的第一个组件</div>
//渲染react元素
ReactDOM.render(<Hello/>,document.getElementById("root"))
1.4 JSX 的基本使用
JSX 简介:
1.4.1 使用步骤:
const Hello=()=><div>这是我的第一个组件</div>
//渲染react元素
ReactDOM.render(<Hello/>,document.getElementById("root"))
1.4.2 JSX 中使用JavaScript 表达式
嵌入JS 表达式
数据存储在JS中
语法:{ JavaScript表达式}
注意:语法中是单大括号,不是双大括号!
//3. 使用jsx创建react元素
const name="tjk"
const title=(
<h1 className='title'>Hello react {name}</h1>
1.4.3 JSX 的条件渲染
/*条件渲染 */
const isLoading=false
const loadData=()=>{
if(isLoading){
return <div>loading......</div>
}
return <div>数据加载完成</div>
}
const title2=(
<h2>条件渲染:
{loadData()}
</h2>
ReactDOM.render(title2,document.getElementById("root"))
1.4.4 JSX 的列表渲染
//1. 导入react
import React from 'react'
import ReactDOM from 'react-dom'
//引入css
import './css/index.css'
/**
* 列表渲染:
*
*/
//歌曲列表
const songs=[
{id:1,name:"爱囚"},
{id:2,name:"老鼠爱大米"},
{id:3,name:"南山南"}
]
const list=(
<ul className='title' style={{color:"red"}}>
{songs.map(item=><li key={item.id}>{item.name}</li>)}
</ul>
)
//渲染react元素
ReactDOM.render(list,document.getElementById("root"))
1.4.5 JSX 的样式处理
const list=(
<ul className='title' style={{color:"red"}}>
{songs.map(item=><li key={item.id}>{item.name}</li>)}
</ul>
1.5 React 组件的两种创建方式
//1. 导入react
import React from 'react'
import ReactDOM from 'react-dom'
//函数组件:
/* function Hello(){
return(
<div>这是我的第一个组件</div>
)
} */
const Hello=()=><div>这是我的第一个组件</div>
//渲染react元素
ReactDOM.render(<Hello/>,document.getElementById("root"))
/* 类组件:使用ES6的class创建组件
1. 类名称必须以大写字母开头
2. 类组件应该继承React.Component父类,从而可以使用父类中提供的属性和方法
3. 类组件必须提供render()方法
4. render方法必须有返回值,表示该组件的结构
*/
//1. 导入react
import React from 'react'
import ReactDOM from 'react-dom'
//类组件:
/* 类组件:使用ES6的class创建组件
1. 类名称必须以大写字母开头
2. 类组件应该继承React.Component父类,从而可以使用父类中提供的属性和方法
3. 类组件必须提供render()方法
4. render方法必须有返回值,表示该组件的结构
*/
class Hello extends React.Component{
render(){
return (
<div>这是我的第一个类组件</div>
)
}
}
//渲染react元素
ReactDOM.render(<Hello/>,document.getElementById("root"))
1.6 抽离为独立JS 文件
hello.js
import React from 'react'
/**
* 抽离组件到独立的js文件中
*/
//创建组件
class Hello extends React.Component{
render(){
return(
<div>这是我的第一个抽离到js文件中的组件</div>
)
}
}
//导出组件
export default Hello
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import Hello from './hello.js'
/**
* 抽离组件到独立的js文件中
*/
//渲染组件
ReactDOM.render(<Hello/>,document.getElementById("root"))
2. React事件处理
2.1 事件绑定
- React事件绑定处理和DOM事件语法相似
- 语法:on+事件名称={事件处理程序} 比: onClick={()=》{}}
- 注意:React事件采用驼峰命名法 onClick
在类组件中绑定事件
import React from 'react'
import ReactDOM from 'react-dom'
/**
* React事件处理
* 类组件事件处理
*/
class App extends React.Component{
handleClick(){
alert("点我干啥???")
}
render(){
return(
<button onClick={this.handleClick}>点击一下你的快乐</button>
)
}
}
ReactDOM.render(<App />,document.getElementById("root"))
在函数中绑定事件
import React from 'react'
import ReactDOM from 'react-dom'
/**
* React事件处理
* 函数组件事件处理
*/
function App(){
function handleClick(){
alert("点我干啥???")
}
return(
<button onClick={handleClick}>点击一下你的快乐</button>
)
}
ReactDOM.render(<App />,document.getElementById("root"))
2.2 事件对象
import React from 'react'
import ReactDOM from 'react-dom'
/**
* React事件对象
*/
class App extends React.Component{
handleClick(e){
e.preventDefault()
alert("点我干啥???")
}
render(){
return(
<a href='http://itcast.cn' onClick={this.handleClick}>点击一下你的快乐</a>
)
}
}
ReactDOM.render(<App />,document.getElementById("root"))
本来点击按钮后会进行链接跳转,但是因为有了事件对象组织了浏览器的默认行为,所以这里只会进行alert
2.3 有状态组件和无状态组件
函数组件又叫 无状态组件 类组件又叫做 有状态组件
状态(state)即数据
函数组件没有自己的状态,只负责数据的展示(静)
类组件有自己的状态,负责更新ui,让页面动起来
2.3.1 组件中的state 和setState
state的基本使用:
import React from 'react'
import ReactDOM from 'react-dom'
/**
* state 状态的基本使用
*/
class App extends React.Component{
//第一种
/* constructor(){
super()
this.state={
count:0
}
} */
//第二种:
state={
count:10
}
handleClick(e){
e.preventDefault()
alert("点我干啥???")
}
render(){
return(
<h1>计数器:{this.state.count}</h1>
)
}
}
ReactDOM.render(<App />,document.getElementById("root"))
import React from 'react'
import ReactDOM from 'react-dom'
/**
* setState 状态的基本使用
*/
class App extends React.Component{
//第二种:
state={
count:0
}
render(){
return(
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={()=>{
this.setState({
count:this.state.count+1
})
}}>+1</button>
</div>
)
}
}
ReactDOM.render(<App />,document.getElementById("root"))
2.3.2 从JSX 中抽离事件处理程序:
import React from 'react'
import ReactDOM from 'react-dom'
/**
* setState 状态的基本使用
*/
class App extends React.Component{
//第二种:
state={
count:0
}
onIncrement(){
console.log("事件处理程序中的");
this.setState({
count:this.state.count+1
})
}
render(){
return(
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={this.onIncrement}>+1</button>
{/* onClick={()=>{
this.setState({
count:this.state.count+1
})
}}>+1 */}
</div>
)
}
}
ReactDOM.render(<App />,document.getElementById("root"))
this指向不明确,出现undefined
2.3.3 事件绑定this 指向:
import React from 'react'
import ReactDOM from 'react-dom'
/**
* setState 状态的基本使用
*/
class App extends React.Component{
//第二种:
state={
count:0
}
onIncrement(){
console.log("事件处理程序中的");
this.setState({
count:this.state.count+1
})
}
render(){
return(
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={()=>this.onIncrement()}>+1</button>
</div>
)
}
}
ReactDOM.render(<App />,document.getElementById("root"))
3. 表单处理
3.1 受控组件
HTML 中的表单元素是可输入的,也就是有自己的可变状态 ,而React 中可变状态通常保存在 state 中,并且只能通过 setState() 方法来修改 React将 state 与表单元素值value绑定到一起,由 state 的值来控制表单元素的值 受控组件:其值受到 React 控制的表单元素
步骤:
import React from 'react'
import ReactDOM from 'react-dom'
/**受控组件: 其值受到React控制的表单元素
* 操作文本框的值
*/
class App extends React.Component{
//第二种:
state={
txt:'',
content:'',
select:'',
isChecked:''
}
handlechange = (e) =>{
this.setState({
txt:e.target.value
})
}
handleContent = (e) =>{
this.setState({
content:e.target.value
})
}
handleSelect =e =>{
this.setState({
select:e.target.value
})
handleChecked = e =>{
this.setState({
isChecked:e.target.checked
})
}
render(){
return(
<div>
{/* <!--文本框--> */}
姓名: <input type="text" value={this.state.txt} onChange={this.handlechange}/>
{/* 富文本框 */}
描述:<textarea value={this.state.content} onChange={this.handleContent}> </textarea>
{/* 下拉框 */}
归属地:<select value={this.state.select} onChange={this.handleSelect}>
<option value="sh">上海</option>
<option value="sz">深圳</option>
<option value="bj">北京</option>
</select>
性别:<input type="checkbox" checked={this.state.isChecked} onChange={this.handleChecked}/>
</div>
)
}
}
ReactDOM.render(<App />,document.getElementById("root"))
多表单元素优化步骤:
import React from 'react'
import ReactDOM from 'react-dom'
/**受控组件: 其值受到React控制的表单元素
* 操作文本框的值
*/
class App extends React.Component{
//第二种:
state={
txt:'',
content:'',
select:'',
isChecked:''
}
handleFrom = (e) =>{
//获取当前的dom对象
const target=e.target
//根据类型获取值
const value=target.type==="checkbox"
?target.checked:target.value
//获取name
const name=target.name
this.setState({
[name]:value
})
}
render(){
return(
<div>
{/* <!--文本框--> */}
姓名: <input type="text" name='txt' value={this.state.txt} onChange={this.handleFrom}/>
{/* 富文本框 */}
描述:<textarea name="content" value={this.state.content} onChange={this.handleFrom}> </textarea>
{/* 下拉框 */}
归属地:<select name='select' value={this.state.select} onChange={this.handleFrom}>
<option value="sh">上海</option>
<option value="sz">深圳</option>
<option value="bj">北京</option>
</select>
性别:<input type="checkbox" name='isChecked' checked={this.state.isChecked} onChange={this.handleFrom}/>
</div>
)
}
}
ReactDOM.render(<App />,document.getElementById("root"))
3.2 非受控组件
//1. 导入react
import React from 'react'
import ReactDOM from 'react-dom'
/*
非受控组件
*/
class App extends React.Component{
constructor(){
super()
this.txtref=React.createRef()
}
//获取文本框的值
getTxt=()=>{
console.log("文本框的值为:",this.txtref.current.value);
}
render(){
return(
<div>
<input type="text" ref={this.txtref}/>
<button onClick={this.getTxt}>获取文本框的内容</button>
</div>
)
}
}
//渲染react元素
ReactDOM.render(<App/>,document.getElementById("root"))
发表评论案例:
//1. 导入react
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component{
state={
comments:[
{id:1,name:"jack",content:"沙发"},
{id:2,name:"tom",content:"沙发aaa"},
{id:3,name:"tjk",content:"沙发bbb"},
{id:4,name:"zjj",content:"沙发ccc"},
],
userName:'',
userContent:''
}
renderList=()=>{
const {comments}=this.state
if(comments.length===0){
return (<div className='no-comment'>暂无评论,快去评论吧</div>)
}else{
return ( <ul>
{comments.map(item=>(
<li key={item.id}>
<h3>评论人:{item.name}</h3>
<p>评论内容:{item.content}</p>
</li>
))}
</ul>)
}
}
handleFrom=(e)=>{
const {name,value}=e.target
this.setState({
[name]:value
})
}
addComments=()=>{
const {userName,userContent,comments}=this.state
// console.log(username,userContent)
const newComments=[{id:Math.random(),
name:userName,
content:userContent},
...comments]
//非空校验:
if(userName.trim()===''||userContent.trim()===''){
alert("请进行输入")
return;
}
this.setState({
comments:newComments,
userName:'',
userContent:'',
})
}
render(){
const {userName,userContent}=this.state
return(
<div className='app'>
<div>
<input className='user'
type="text"
placeholder='请输入评论人'
value={userName}
name="userName"
onChange={this.handleFrom}/ >
<br/>
<textarea className='content' cols={30} rows="10" placeholder='请输入评论内容'
value={userContent}
name="userContent" onChange={this.handleFrom}/>
<br/>
<button onClick={this.addComments}>发表评论</button>
</div>
{/* 通过条件渲染 */}
{
this.renderList()
}
</div>
)
}
}
//渲染react元素
ReactDOM.render(<App/>,document.getElementById("root"))
4. React 组件进阶
4.1 组件通讯介绍
4.2. 组件的props
特点:
4.3. 组件通讯的三种方式
4.3.1 父组件传递数据给子组件
import React from 'react'
import ReactDOM from 'react-dom'
class Parent extends React.Component{
state={
name:"tjk"
}
render(){
return (
<div>
<Child name={this.state.name}/>
</div>
)
}
}
const Child=(props)=>{
return(
<div>
子组件收到的数据:{props.name}
</div>
)
}
ReactDOM.render(<Parent/>,document.getElementById("root"))
4.3.2 子组件传递数据给父组件
4.3.3 兄弟组件
4.4 Context
<Provider>
<div className="App">
<Child1 />
</div>
</Provider>
<Consumer>
{data => <span>data参数表示接收到的数据--{data}</span>}
</Consumer>
总结:
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
/*
Context
*/
// 创建context得到两个组件
const { Provider, Consumer } = React.createContext()
class App extends React.Component {
render() {
return (
<Provider value={"pink"}>
<div className="app">
<Node />
</div>
</Provider>
)
}
}
const Node = props => {
return (
<div className="node">
<SubNode />
</div>
)
}
const SubNode = props => {
return (
<div className="subnode">
<Child />
</div>
)
}
const Child = props => {
return (
<div className="child">
<Consumer>
{
data=> <span>我是子节点---{data} </span>
}
</Consumer>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
4.5 props 深入
4.5.1 children 属性
children 属性:表示组件标签的子节点。当组件标签有子节点时,props就会有该属性
//1. 导入react
import React from 'react'
import ReactDOM from 'react-dom'
//children属性
const App=props=>{
console.log(props);
return(
<div>
<h1>组件标签的子节点:{props.children}</h1>
</div>
)
}
//渲染react元素
ReactDOM.render(<App>我是子节点</App>,document.getElementById("root"))
4.5.2 props 校验
对于组件来说,props 是外来的,无法保证组件使用者传入什么格式的数据
如果传入的数据格式不对,可能会导致组件内部报错
解决:
约束规则:
1.常见类型:array、bool、func、number、object、string
2.React元素类型:element
3.必填项:isRequired
// 添加props校验
// 属性 a 的类型: 数值(number)
// 属性 fn 的类型: 函数(func)并且为必填项
// 属性 tag 的类型: React元素(element)
// 属性 filter 的类型: 对象({area: '上海', price: 1999})
App.propTypes = {
a: PropTypes.number,
fn: PropTypes.func.isRequired,
tag: PropTypes.element,
filter: PropTypes.shape({
area: PropTypes.string,
price: PropTypes.number
})
}
4.5.3 props 的默认值
场景:分页组件每页显示条数
作用:给props 设置默认值,在未传入props 时生效
4.6 组件的生命周期
4.6.1 组件的生命周期概述
意义:组件的生命周期有助于理解组件的运行方式、完成更复杂的组件功能、分析组件错误原因
组件的生命周期:组件从被创建到挂载到页面中运行,再到组件不用时卸载的过程
生命周期的每个阶段总是伴随着一些方法调用,这些方法就是生命周期的钩子函数。
钩子函数的作用:为开发人员在不同阶段操作组件提供了时机。
只有类组件才有生命周期。
4.6.2 生命周期的三个阶段
1.每个阶段的执行时机
2.每个阶段钩子函数的执行顺序
3.每个阶段钩子函数的作用
1. 创建时(挂载阶段)
执行时机:组件创建时(页面加载时)
执行顺序:
2. 更新时(更新阶段)
执行时机:1. setState() 2. forceUpdate() 3. 组件接收到新的props
说明:以上三者任意一种变化,组件就会重新渲染
3. 卸载时(卸载阶段)
执行时机:组件从页面中消失
4.7. render-props和高阶组件
4.7.1 React组件复用概述
思考:如果两个组件中的部分功能相似或相同,该如何处理?
处理方式:复用相似的功能(联想函数封装)
复用什么?1. state 2.操作state的方法(组件状态逻辑)
两种方式:1. render props模式2. 高阶组件(HOC)
注意:这两种方式 不是新的 API ,而是利用 React 自身特点的编码技巧,演化而成的固定模式(写法)
4.7.2 render props 模式
- 思路:将要复用的 state 和操作 state 的方法封装到一个组件中- 问题 1 :如何拿到该组件中复用的 state ?在使用组件时,添加一个值为 函数的 prop ,通过函数 参数 来获取(需要组件内部实现)- 问题 2 :如何渲染任意的 UI ?使用 该函数的返回值 作为要渲染的 UI 内容(需要组件内部实现)
<Mouse render={(mouse) => {}}/>
<Mouse render={(mouse) => (
<p>鼠标当前位置{mouse.x},{mouse.y}</p>)}/>
使用步骤
import React from 'react'
import ReactDOM from 'react-dom'
import img from './image/cat.png'
import PropTypes from 'prop-types'
//创建Mouse组件
class Mouse extends React.Component{
//鼠标位置state
state={
x:0,
y:0
}
//鼠标移动事件的事件处理程序
handleMouseMove=e=>{
this.setState({
x:e.clientX,
y:e.clientY
})
}
//监听鼠标移动事件
//这个钩子函数 是在组件初次被渲染的时候进行执行
componentDidMount(){
window.addEventListener("mousemove",this.handleMouseMove)
}
render(){
return this.props.render(this.state)
}
}
class App extends React.Component{
render(){
return (
<div>
<h1>render props模式</h1>
{ <Mouse render={(mouse)=>{
return(
<p>
鼠标位置:{mouse.x} {mouse.y}
</p>
)
}}
/> }
</div>
}
}
ReactDOM.render(<App/>,document.getElementById("root"))
<Mouse render={(mouse) => <p>鼠标当前位置{mouse.x},{mouse.y}</p>}/>
import React from 'react'
import ReactDOM from 'react-dom'
import img from './image/cat.png'
import PropTypes from 'prop-types'
//创建Mouse组件
class Mouse extends React.Component{
//鼠标位置state
state={
x:0,
y:0
}
//鼠标移动事件的事件处理程序
handleMouseMove=e=>{
this.setState({
x:e.clientX,
y:e.clientY
})
}
//监听鼠标移动事件
//这个钩子函数 是在组件初次被渲染的时候进行执行
componentDidMount(){
window.addEventListener("mousemove",this.handleMouseMove)
}
//推荐:在组件卸载时移除事件绑定
componentWillUnmount(){
window.removeEventListener("mousemove",this.handleMouseMove)
}
render(){
// return this.props.render(this.state)
return this.props.children(this.state)
}
}
//添加props校验
Mouse.propTypes={
children:PropTypes.func.isRequired
}
class App extends React.Component{
render(){
return (
<div>
<h1>render props模式</h1>
{/* <Mouse render={(mouse)=>{
return(
<p>
鼠标位置:{mouse.x} {mouse.y}
</p>
)
}}
/> */}
<Mouse >
{mouse=>{
return (
<p>
鼠标位置:{mouse.x} {mouse.y}
</p>
)
}}
</Mouse>
{/*猫捉老鼠*/}
<Mouse>
{
mouse=>{
return <img src={img} alt="猫" style={{
position:"absolute",
top:mouse.y-64,
left:mouse.x-64,
width:150,
height: 150,
}}
/>
}
}
</Mouse>
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById("root"))
1.
//添加props校验
Mouse.propTypes={
children:PropTypes.func.isRequired
}
2.
//推荐:在组件卸载时移除事件绑定
componentWillUnmount(){
window.removeEventListener("mousemove",this.handleMouseMove)
}
4.7.3 高阶组件
constEnhancedComponent= withHOC(WrappedComponent)
// 高阶组件内部创建的类组件:
class Mouse extends React.Component{
render() {
return <WrappedComponent{...this.state} />
}
}
import React from 'react'
import ReactDOM from 'react-dom'
import img from './image/cat.png'
/**
* 高阶组件
*/
//创建高阶组件
function withMouse(WrappedComponent){
//该组件提供复用的状态逻辑
class Mouse extends React.Component{
//鼠标状态
state={
x:0,
y:0
}
handleMouseMove=e=>{
this.setState({
x:e.clientX,
y:e.clientY
})
}
//控制鼠标状态逻辑 绑定事件
componentDidMount(){
window.addEventListener("mousemove",this.handleMouseMove)
}
//解绑事件
componentWillUnmount(){
window.removeEventListener("mousemove",this.handleMouseMove)
}
render(){
return<WrappedComponent {...this.state}></WrappedComponent>
}
}
//返回创建好的组件
return Mouse
}
const Position=props=>(
<p>
鼠标当前的位置:(x:{props.x},y:{props.y})
</p>
)
const Cat=props=>(
<img src={img} style={{
position:"absolute",
top:props.y-64,
left:props.x-64,
width:150,
height:150,
} }/>
)
const MouseCat=withMouse(Cat)
//获取增强后的组件
const MousePosition=withMouse(Position)
class App extends React.Component{
render(){
return (
<div>
<h1>高阶组件</h1>
{/* 渲染增强后的组件 */}
<MousePosition></MousePosition>
<MouseCat></MouseCat>
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById("root"))
传递props:
<WrappedComponent{...this.state} {...this.props} />
5. React 原理:
5.1 setState() 的说明
5.1.1 更新数据
this.state= { count: 1 }
this.setState({
count: this.state.count+ 1
})
console.log(this.state.count) // 1
5.1.2 推荐语法
this.setState((state, props) => {
return {
count: state.count+ 1
}
})
console.log(this.state.count) // 1
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component{
state={
count:1
}
handleclick=()=>{
//此处更新state
/* this.setState({
count:this.state.count+1//1+1
})
this.setState({
count:this.state.count+1//1+1
})*/
//推荐语法
//注意:这种方法也是异步更新的state的
this.setState((state,props)=>{
return{
count:state.count+1 //1+1
}
},()=>{
console.log("状态更新完成");
})
this.setState((state,props)=>{
return{
count:state.count+1 //2+1
}
})
console.log("count:",this.state.count);
}
render(){
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={this.handleclick}>+1</button>
</div>
)
}
}
ReactDOM.render(<App/>,document.getElementById("root"))
5.1.3 第二个参数
5.2. JSX 语法的转化过程
5.3. 组件更新机制
5.4. 组件性能优化
5.4.1 减轻state
5.4.2 避免不必要的重新渲染
案例:随机数:
5.4.3 纯组件
5.5 虚拟DOM 和Diff 算法
执行过程:
代码演示: