React-组件原理
setState原理
setState()是异步更新数据的注意:使用该语法的时候,后面的 setState()不要依赖于前面的setState()
可以多次调用setState(),只会触发一次重新渲染
import React from 'react'; import { createRoot } from 'react-dom/client'; class App1 extends React.Component { state = { count: 1 } handelClick = () => { //此处,更新state //注意:异步更新数据的 this.setState({ count: this.state.count + 1 }) console.log('count', this.state.count);//1 } render() { console.log('render'); return ( <div> <h1>计数器:{this.state.count}</h1> <button onClick={this.handelClick}>+1</button> </div> ) } } const container = document.getElementById('root'); const root = createRoot(container); root.render(<App1 />);
setstate推荐语法
this.setState((state, props) => { return { count: state.count + 1 } })
第二个参数的使用
场景:在状态更新(页面完成重新渲染)后立即执行某个操作
handelClick = () => { this.setState((state, props) => { return { count: state.count + 1 } }, () => { console.log('状态更新完成', this.state.count);//2 console.log(document.getElementById('title').innerText); document.title = "更新标题" + this.state.count } ) console.log('count', this.state.count);//1 }
JSX语法的转化过程
JSX仅仅是createElement方法的简化语法
JSX语法被@babel/preset-react插件编译为createElement()方法
React元素:是一个对象,用来描述你希望在屏幕上看到的内容
组件更新机制
setState的作用
- 修改state
- 更新组件(UI)
过程:父组件重新渲染时,也会重新渲染子组件。但只会渲染当前组件子树(当前组件及其所有子组件)
组件性能优化
1.减轻state
只存储和组件渲染相关的数据(比如:count/列表数据/loading等)
注意:不用做渲染的数据不要放在state中,比如定时器id等
对于这种需要在多个方法中使用到的数据,应该放在this中
2.避免不必要的重新渲染
组件更新机制:父组件更新会引起子组件也被更新
问题:子组件没有任何变化时也会重新渲染
如何避免不必要的重新渲染呢?
解决方式:使用钩子函数shouldComponentUpdate(nextProps,nextState)
作用:通过返回值决定该组件是否重新渲染,返回true表示重新渲染,false表示不重新渲染
触发实际:更新阶段的钩子函数,组件重新渲染前执行,render执行前执行
class App extends React.Component { state = { count: 0 } handleClick = () => { this.setState((state) => { return { count: state.count + 1 } }) } shouldComponentUpdate = (nextProps, nextState) => { //返回false,阻止组件重新渲染 // return false //最新的状态 console.log('最新的count', nextState); //更新前的状态 console.log('更新前的状态', this.state); return true } //钩子函数 render() { console.log('组件更新了'); return ( <div> <h1>计数器:{this.state.count}</h1> <button onClick={this.handleClick}>+1</button> </div> ) } } const container = document.getElementById('root') const root = createRoot(container) root.render(<App />)
案例:随机数案例
//随机数 class App extends React.Component { state = { count: 0 } handleClick = () => { this.setState(() => { return { count: Math.floor(Math.random() * 3) } }) } //因为两次生成的随机数可能相同,所以,此时不需要重新渲染 shouldComponentUpdate(nextProps, nextState) { console.log('最新状态:', nextState, '当前状态:', this.state); if (nextState.count === this.state.count) { return false } return true } render() { console.log('render'); return ( <div> <h1>随机数:{this.state.count}</h1> <button onClick={this.handleClick}>重新生成</button> </div> ) } } const container = document.getElementById('root') const root = createRoot(container) root.render(<App />)
//随机数 class App extends React.Component { state = { count: 0 } handleClick = () => { this.setState(() => { return { count: Math.floor(Math.random() * 3) } }) } //因为两次生成的随机数可能相同,所以,此时不需要重新渲染 // shouldComponentUpdate(nextProps, nextState) { // console.log('最新状态:', nextState, '当前状态:', this.state); // if (nextState.count === this.state.count) { // return false // } // return true // } render() { // console.log('render'); return ( <div> <NumberBox count={this.state.count} /> <button onClick={this.handleClick}>重新生成</button> </div> ) } } class NumberBox extends React.Component { shouldComponentUpdate(nextProps) { console.log('最新的状态', nextProps, '当前状态', this.props); if (nextProps.count === this.props.count) { return false } return true } render() { console.log('render'); return <h1>随机数:{this.props.count}</h1> } } const container = document.getElementById('root') const root = createRoot(container) root.render(<App />)
纯组件
PureComponent与React.Component功能相似
区别:PureComponent内部自动实现了shouldComponentUpdate钩子,不需要手动比较
原理:纯组件内部通过分别对比前后两次props和state的值,来决定是否重新渲染组件。
//随机数 class App extends React.PureComponent { state = { count: 0 } handleClick = () => { this.setState(() => { return { count: Math.floor(Math.random() * 3) } }) } render() { console.log('render'); return ( <div> <h1>随机数:{this.state.count}</h1> <button onClick={this.handleClick}>重新生成</button> </div> ) } } const container = document.getElementById('root') const root = createRoot(container) root.render(<App />)
经代码验证,随机数案例的两种情况在PureComponent下可以完成自动阻止渲染
纯组件内部对比
shallow compare(浅层对比)
对于值类型来说,比较两个值是否相同(直接赋值)
对于引用类型来说,只比较对象的引用(地址)是否相同
注意:state或props中属性值为引用类型时,应该创建新数据,不要直接修改原数据
//随机数 class App extends React.PureComponent { state = { obj: { count: 0 } } handleClick = () => { const newobj = { ...this.state.obj, count: Math.floor(Math.random() * 3) } this.setState(() => { return { obj: newobj } }) } render() { console.log('render'); return ( <div> <h1>随机数:{this.state.obj.count}</h1> <button onClick={this.handleClick}>重新生成</button> </div> ) } } const container = document.getElementById('root') const root = createRoot(container) root.render(<App />)
虚拟DOM和Diff算法
React更新视图的思想时:只要state变化就重新渲染视图
特点:思路非常清晰
完成部分更新,而不是更新整体
虚拟DOM:本质上是一个JS对象,用来描述你希望在屏幕上看到的内容(UI)
执行过程:
- 初次渲染时,React会根据数据初始state(Model),创建一个虚拟DOM对象(树)
- 根据虚拟DOM生成真正的DOM,渲染到页面中
- 当数据变化后(setState()),重新根据新的数据,创建新的虚拟DO吗对象(树)
- 与上一次得到的虚拟DOM对象,使用Diff算法对比,得到需要更新的内容
- 最终,React只将变化的内容更新到DOM中,重新渲染到页面。
class App extends React.Component { state = { count: 0 } handleClick = () => { this.setState(() => { return { count: Math.floor(Math.random() * 2) } }) } render() { const el = ( <div> <h1>随机数:</h1> <p>{this.state.count}</p> <button onClick={this.handleClick}>重新生成</button> </div> ) console.log(el); return el } } const container = document.getElementById('root') const root = createRoot(container) root.render(<App />)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix