转转转——转自:React 也能“用上” computed属性
文章中的用法不知是否合适,先备份着做为自己思路上的一个指导吧!
前言,关于计算属性
初次见到计算属性一词,是在 Vue 官方文档
《计算属性和侦听器》 一节中,文章中是这样描述计算属性的:
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。
回想我们编写的 React 代码
,是否也在 JSX(render 函数)
中放入了太多的逻辑导致 render
函数过于庞大,难以维护?
React 中的计算属性
说到 React
,我们先看下 Vue
,在 Vue
中,计算属性主要有以下两点特性:
- 计算属性以声明的方式创建依赖关系,依赖的
data
或props
变更会触发重新计算并自动更新。 - 计算属性是基于它们的响应式依赖进行缓存的。
而在 React
中,计算属性也是经常可见,相信各位熟悉 React
的读者都写过类似下面的代码:
import React, { Fragment, Component } from 'react';
class Example extends Component {
constructor(props) {
super(props);
this.state = {
firstName: '',
lastName: '',
};
}
render() {
// 在 render 函数中处理逻辑
const { firstName, lastName } = this.state;
const fullName = `${firstName} ${lastName}`;
return <Fragment>{fullName}</Fragment>;
}
}
在上面的代码里,render 函数
里的 fullName
依赖了 props
中的 firstName
和 lastName
。firstName
或 lastName
变更之后,变量 fullName
都会自动更新。其实现原理是 props 以及 state 的变化会导致 render 函数
调用,进而重新计算衍生值。
虽然能实现计算,但我们还是把计算逻辑放入了 render 函数
导致了它的臃肿,这并不优雅。更好的做法是把计算逻辑抽出来,简化 render 函数
逻辑:
class Example extends Component {
constructor(props) {
super(props);
this.state = {
firstName: '',
lastName: '',
};
}
// 把 render 中的逻辑抽成函数,减少render函数的臃肿
renderFullName() {
const { firstName, lastName } = this.state;
return `${firstName} ${lastName}`;
}
render() {
const fullName = this.renderFullName();
return <Fragment>{fullName}</Fragment>;
}
}
如果你对 Vue
很了解,你肯定知道其 computed
计算属性,底层是使用了getter
,只不过是对象的 getter
。那么在 React
中,我们也可以使用类的 getter
来实现计算属性:
class Example extends Component {
constructor(props) {
super(props);
this.state = {
firstName: '',
lastName: '',
};
}
// 通过getter而不是函数形式,减少变量
get fullName() {
const { firstName, lastName } = this.state;
return `${firstName} ${lastName}`;
}
render() {
return <Fragment>{this.fullName}</Fragment>;
}
}
进一步,使用 memoization
优化计算属性
上文有提到在 Vue
中计算属性对比函数执行,会有缓存,减少计算。因为计算属性只有在它的相关依赖发生改变时才会重新求值。
这就意味着只要 firstName
和 lastName
还没有发生改变,多次访问 fullName
计算属性会立即返回之前的计算结果,而不必再次执行函数。
对比之下,React 的 getter
是否也有缓存这个优势??? 答案是:没有。React 中的 getter
并没有做缓存优化!
不过不用失望,我们可以使用记忆化技术(memoization
)来优化我们的计算属性,达到和 Vue
中计算属性一样的效果。我们需要在项目中引入 memoize-one
库,代码如下:
import memoize from 'memoize-one';
import React, { Fragment, Component } from 'react';
class Example extends Component {
constructor(props) {
super(props);
this.state = {
firstName: '',
lastName: '',
};
}
// 如果和上次参数一样,`memoize-one` 会重复使用上一次的值。
getFullName = memoize(
(firstName, lastName) => `${firstName} ${lastName}`
);
get fullName() {
return this.getFullName(this.state.firstName, this.state.lastName);
}
render() {
return <Fragment>{this.fullName}</Fragment>;
}
}
再进一步,使用 React Hooks
优化计算属性
上文在 React
中使用了 memoize-one
库实现了类似 Vue
计算属性(computed
)的效果 —— 基于依赖缓存计算结果。得益于React 16.8 新推出的 Hooks 特性
,我们可以对逻辑进行更优雅的封装,对 Hooks
还不够了解的小伙伴可以先阅读我们团队另一篇文章 《看完这篇,你也能把 React Hooks 玩出花》
此处,我们需要用到 useMemo
。官方对 useMemo
的介绍在 这里,详情请移步查看。简单的说,就是我们传入一个 回调函数 和一个 依赖列表,React 会在依赖列表中的值变化时,调用这个回调函数,并将回调函数返回的结果进行缓存:
import React, { useState, useMemo } from 'react';
function Example(props) {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
// 使用 useMemo 函数缓存计算过程
const renderFullName = useMemo(() => `${firstName} ${lastName}`, [
firstName,
lastName,
]);
return <div>{renderFullName}</div>;
}
网友评论