Fragment
react组件中只能有一个根组件.
之前使用div包裹的方式会给html结构增加很多无用的层级为了解决这个问题,可以使用React.Fragment
function Hello(){
return (
// 渲染到页面之后,这个div就是一个多余的
<div>
<h1>fragment</h1>
<p>hello react</p>
</div>
)
}
// 将上面的写法,修改为下面的写法
function Hello(){
return (
// 这样就只会渲染h1和p
<React.Fragment>
<h1>fragment</h1>
<p>hello react</p>
</React.Fragment>
)
}
// 简写形式
function Hello(){
return (
// 这是React.Fragment的简写形式
<>
<h1>fragment</h1>
<p>hello react</p>
</>
)
}
React性能优化
-
减轻state 跟页面渲染无关的数据,不要放在state中(比如定时器的id.不要放在state中)
- 可以有效减少页面重新渲染次数
-
shouldComponentUpdate (减轻不必要的重新渲染)
-
**组件更新的机制: **父组件重新渲染时,也会重新渲染子组件
-
如何避免不必要的重新渲染呢?
-
解决方式:使用钩子函数 shouldComponentUpdate(nextProps, nextState)
-
作用:通过返回值决定该组件是否重新渲染,返回 true 表示重新渲染,false 表示不重新渲染
-
触发时机:更新阶段的钩子函数,组件重新渲染前执行 (shouldComponentUpdate => render)
// 父组件 class Far extends Component { state = { num: 0 } // 返回min~max之间的随机整数(包含min和max) getRandomIntInclusive(min, max) { min = Math.ceil(min) max = Math.floor(max) return Math.floor(Math.random() * (max - min + 1)) + min //含最大值,含最小值 } handle = () => { let num = this.getRandomIntInclusive(1, 3) this.setState({ num }) } //有效减少组件渲染次数 shouldComponentUpdate(props, state) { // this.state 原来的state // state 最新的state return this.state.num !== state.num } render() { console.log('父组件渲染了') return ( <div> <h1 onClick={this.handle}>Far组件</h1> <p>{this.state.num}</p> <Son num={this.state.num}></Son> </div> ) } } // 不管子组件有没有用到父组件的数据,只要父组件重新渲染了,子组件就会跟着渲染 // 子组件 class Son extends Component { //有效减少组件渲染次数 shouldComponentUpdate(props, state) { // this.props 原来的props // props 最新的props return props.num !== this.props.num } render() { console.log('Son组件渲染了') return ( <div> <h1>Son组件</h1> <p>{this.props.num}</p> </div> ) } }
-
-
纯组件 pureComponent
-
PureComponent 与 React.Component 功能相似,直接替换即可
-
区别:PureComponent 内部自动实现了 shouldComponentUpdate 钩子,不需要手动比较
-
原理:纯组件内部通过分别 对比 前后两次 props 和 state 的值,来决定是否重新渲染组件
-
注意: 纯组件内部的对比是 shallow compare(浅层对比)
- 对于引用类型来说:只比较对象的引用(地址)是否相同
- 所以,state 或 props 中属性值为引用类型时,应该创建新数据,不要直接修改原数据
class Far extends PureComponent { state = { obj: { num: 0 } } getRandomIntInclusive(min, max) { min = Math.ceil(min) max = Math.floor(max) return Math.floor(Math.random() * (max - min + 1)) + min //含最大值,含最小值 } handle = () => { // 错误的写法 // 重新设置的obj和上一次的obj是同一个. // 组件不会重新渲染了 // let num = this.getRandomIntInclusive(1, 3) // let obj = this.state.obj 或者 let {obj} = this.state // obj.num = num // 正确的写法: let num = this.getRandomIntInclusive(1, 3) // 创建一个新的对象 let obj = { num } console.log(num) this.setState({ obj }) } render() { console.log('父组件渲染了') return ( <div> <h1 onClick={this.handle}>Far组件</h1> <p>{this.state.obj.num}</p> <Son num={this.state.obj.num}></Son> </div> ) } }
-
React.forwardRef(不常用.可以通过props传递)
函数组件不可以添加ref属性
作用: 将函数组件内部的元素,交给父组件使用
// 实现:
// App组件
const ref = React.createRef()
class App extends React.Component {
componentDidMount() {
// 在app组件中,通过ref获取到了button的Dom对象
console.log(ref.current)
}
render() {
return (
<div>
// 使用React.forwardRef包装后的FancyButton就可以添加ref属性了
<FancyButton ref={ref}>Click me!</FancyButton>
</div>
)
}
}
// FancyButton
const FancyButton = React.forwardRef((props, ref) => {
console.log(props, ref)
return (
<button ref={ref} className='FancyButton'>
{props.children}
</button>
)
})
// 你可以直接获取 DOM button 的 ref:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
Portal
Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案
class App extends React.Component {
render() {
return (
<div>
<h1>App组件</h1>
// 在app组件中使用了portal组件
// 但是渲染dom的时候,portal的内容不在这个div中
<Portal></Portal>
</div>
)
}
}
export default class Portal extends Component {
constructor() {
super()
this.container = document.createElement('div')
}
componentDidMount() {
document.body.appendChild(this.container)
}
render() {
let node = (
<div>
<div className='modal'>模态框</div>
<button>按钮</button>
</div>
)
// 当前portal组件不直接返回元素,而是返回一个portal
// react底层会将node的内容,渲染到this.container中
return ReactDOM.createPortal(node, this.container)
}
componentWillUnmount() {
document.body.removeChild(this.container)
}
}