React 日常使用/优化机制
React 渲染/更新机制
- 渲染流程
JSX->虚拟DOM -> 真实DOM
- 更新机制
- 更新流程
- 同层节点之间互相比较 不会跨节点比较
- 不同类型节点 产生不同的树结构(如果同层父节点有差异 会直接节点替换)
- 定义key 来指定哪些节点在不同的渲染保持稳定
- key的优化
- 在最后位置插入数据 -> 有无key意义不大
- 在前面插入数据 -> 如果没有key的话 所有的li节点都会改变
- 在拥有key 做修改时候
React 使用 key 来匹配原有树上的子元素以及最新树上的子元素: 在下面这种场景下,key为111和222的元素仅仅进行位移,不需要进行任何的修改; 将key为333的元素插入到最前面的位置即可;
- key 的注意事项
- key是唯一的
- key不要使用随机数(随机数在下一次render时候 会重新生成)
- 使用index作为key 对性能没有优化
SCU性能优化
- render函数
state中变量有被修改 这就会导致render重新执行。对于没有依赖于修改的变量来说 它们不需要被重新渲染&diff比较。这就回造成性能较低,可以进行如下优化
- shouldComponentUpdate 生命周期(简称SCU)
- 这个方法有两个参数 newProps(最新的props属性) newState(最新的state属性) 根据返回的boolean 来判断是否触发render函数
// shouldComponentUpdate(nextProps, newState) {
// 因为此时还没有更新state的值 所以this.state还是未修改的
// if (this.state.message !== newState.message || this.state.counter !== newState.counter) {
// return true
// }
// return false
// }
- 将class继承与PureComponent 其内部是对state和props进行一个浅层次的对比(只比较第一层)
- 在函数组件中 没有继承和生命周期的。可以使用memo函数给他包裹起来
import { memo } from "react"
const Profile = memo(function(props) {
return <h2>Profile: {props.message}</h2>
})
- 不可变数据力量 在使用pureComponent 时候 不要去修改它原来的state 需要重新赋值。因为对象是引用类型 如果你直接修改其本身值 在内部它进行对比 就会因为new & old 是在一个内存空间内。然后不进行修改&&渲染,如果给他重新赋值给一个新的变量 在内存的角度来看 这就会创建一个新的内存空间 (字符串不需要 因为你直接改变字符串 就相当于改变了内存空间)
books: [
{ name: "你不知道JS", price: 99, count: 1 },
{ name: "JS高级程序设计", price: 88, count: 1 },
{ name: "React高级设计", price: 78, count: 2 },
{ name: "Vue高级设计", price: 95, count: 3 },
],
addNewBook() {
const newBook = { name: "Angular高级设计", price: 88, count: 1 }
// 1.直接修改原有的state, 重新设置一遍
// 在PureComponent是不能引入重新渲染(re-render)
this.state.books.push(newBook)
this.setState({ books: this.state.books })
// 2.赋值一份books, 在新的books中修改, 设置新的books
const books = [...this.state.books]
books.push(newBook)
this.setState({ books: books })
}
addBookCount(index) {
// this.state.books[index].count++
const books = [...this.state.books]
books[index].count++
this.setState({ books: books })
}
在它内部进行浅层比较 其实就是进行了shallowEqual()
!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
function shallowEqual(objA: mixed, objB: mixed): boolean {
if (Object.is(objA, objB)) {
return true;
}
if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false;
}
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
// 如果两个对象长度不一样的话 直接渲染
if (keysA.length !== keysB.length) {
return false;
}
for (let i = 0; i < keysA.length; i++) {
if (
// 比较a的key 是否在objB中
!hasOwnProperty.call(objB, keysA[i]) ||
// A对象和B对象是否是一个内存空间
!Object.is(objA[keysA[i]], objB[keysA[i]])
) {
return false;
}
}
return true;
}
function shallowCompare(instance, nextProps, nextState) {
return (
!shallowEqual(instance.props, nextProps) ||
!shallowEqual(instance.state, nextState)
);
}
通过ref 获取到dom属性
- 直接给元素绑定ref (不建议使用 )
- 通过createRef 给ref绑定值
- ref的参数 通过回调函数绑定ref
- 给组件class类组件 添加ref 可以获取到类组件的组件实例
import React, { PureComponent, createRef } from 'react'
export class App extends PureComponent {
constructor() {
super()
this.state = {
}
this.titleRef = createRef()
this.titleEl = null
}
getNativeDOM() {
// 1.方式一: 在React元素上绑定一个ref字符串
// console.log(this.refs.why)
// 2.方式二: 提前创建好ref对象, createRef(), 将创建出来的对象绑定到元素
// console.log(this.titleRef.current)
// 3.方式三: 传入一个回调函数, 在对应的元素被渲染之后, 回调函数被执行, 并且将元素传入
console.log(this.titleEl)
}
render() {
return (
<div>
<h2 ref="why">Hello World</h2>
<h2 ref={this.titleRef}>你好啊,李银河</h2>
<h2 ref={el => this.titleEl = el}>你好啊, 师姐</h2>
<button onClick={e => this.getNativeDOM()}>获取DOM</button>
</div>
)
}
}
- 函数组件没有组件实例 如果想获取到函数组件内部的元素 需要使用forwardRef函数包裹
import React, { PureComponent, createRef, forwardRef } from 'react'
const HelloWorld = forwardRef(function(props, ref) {
return (
<div>
<h1 ref={ref}>Hello World</h1>
<p>哈哈哈</p>
</div>
)
})
export class App extends PureComponent {
constructor() {
super()
this.hwRef = createRef()
}
getComponent() {
console.log(this.hwRef.current)
}
render() {
return (
<div>
<HelloWorld ref={this.hwRef}/>
<button onClick={e => this.getComponent()}>获取组件实例</button>
</div>
)
}
}
export default App
受控组件
- 在原生组件中(input/select) react中没有vue的双向绑定 所以如果想要获取到输入的值 就得通过onChange事件。如果想给他一个初始值 就需要绑定value属性。如果给了value 那它就是一个受控组件 就需要onChange 来修改value的
import React, { PureComponent } from 'react'
export class App extends PureComponent {
constructor() {
super()
this.state = {
username: "coderwhy"
}
}
inputChange(event) {
console.log("inputChange:", event.target.value)
this.setState({ username: event.target.value })
}
render() {
const { username } = this.state
return (
<div>
{/* 受控组件 */}
<input type="checkbox" value={username} onChange={e => this.inputChange(e)}/>
{/* 非受控组件 */}
<input type="text" />
<h2>username: {username}</h2>
</div>
)
}
}
export default App
转载自:https://juejin.cn/post/7213561124519526456