初次见到计算属性一词,是在 Vue 官方文档 《计算属性和侦听器》 一节中,文章中是这样描述计算属性的:javascript
模板内的表达式很是便利,可是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板太重且难以维护。
回想咱们编写的 React 代码,是否也在 JSX(render 函数)中放入了太多的逻辑致使 render
函数过于庞大,难以维护?html
说到 React 以前,咱们先看下 Vue,在 Vue 中,计算属性主要有如下两点特性:前端
而在 React 中,计算属性也是常常可见,相信各位熟悉 React 的读者都写过相似下面的代码:vue
import React, { Fragment, Component } from 'react'; class Example extends Component { 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 函数调用,进而从新计算衍生值。java
虽然能实现计算,但咱们仍是把计算逻辑放入了 render 函数致使了它的臃肿,这并不优雅。更好的作法是把计算逻辑抽出来,简化 render 函数逻辑:react
class Example extends Component { 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 { state = { firstName: '', lastName: '', }; // 经过getter而不是函数形式,减小变量 get fullName() { const { firstName, lastName } = this.state; return `${firstName} ${lastName}`; } render() { return <Fragment>{this.fullName}</Fragment>; } }
上文有提到在 Vue 中计算属性对比函数执行,会有缓存,减小计算。由于计算属性只有在它的相关依赖发生改变时才会从新求值。缓存
这就意味着只要 firstName 和 lastName 尚未发生改变,屡次访问 fullName 计算属性会当即返回以前的计算结果,而没必要再次执行函数。框架
对比之下,React 的 getter 是否也有缓存这个优点??? 答案是:没有。React 中的 getter 并无作缓存优化!ide
不过不用失望,咱们可使用记忆化技术(memoization)来优化咱们的计算属性,达到和 Vue 中计算属性同样的效果。咱们须要在项目中引入 memoize-one 库,代码以下:
import memoize from 'memoize-one'; import React, { Fragment, Component } from 'react'; class Example extends Component { 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 中使用了 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>; }
本文介绍了在 React 中如何实现相似 Vue 计算属性(computed)的效果 —— 基于依赖缓存计算结果,实现逻辑计算与视图渲染的解耦,下降 render 函数的复杂度。
从业务开发角度来说,Vue 提供的 API 极大地提升了开发效率。React 虽然在某些场景下,没有官方的同类原生 API 支持,但得益于活跃的社区,工做中遇到的问题总能找到解决方案。且在摸索这些解决方案的同时,咱们还能学习到诸多经典的编程思想,帮助咱们更合理的运用框架,用技术解决业务问题。
招人,前端,隶属政采云前端大团队(ZooTeam),50 余个小伙伴正等你加入一块儿浪~ 若是你想改变一直被事折腾,但愿开始能折腾事;若是你想改变一直被告诫须要多些想法,却无从破局;若是你想改变你有能力去作成那个结果,却不须要你;若是你想改变你想作成的事须要一个团队去支撑,但没你带人的位置;若是你想改变既定的节奏,将会是“5年工做时间3年工做经验”;若是你想改变原本悟性不错,但老是有那一层窗户纸的模糊… 若是你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的本身。若是你但愿参与到随着业务腾飞的过程,亲手参与一个有着深刻的业务理解、完善的技术体系、技术创造价值、影响力外溢的前端团队的成长历程,我以为咱们该聊聊。任什么时候间,等着你写点什么,发给 ZooTeam@cai-inc.com