原創不易,但願能關注下咱們,再順手點個贊~~ |
本文首發於政採雲前端團隊博客: 乾坤大挪移!React 也能 「用上」 computed 屬性javascript
初次見到計算屬性一詞,是在 Vue 官方文檔 《計算屬性和偵聽器》 一節中,文章中是這樣描述計算屬性的:html
模板內的表達式很是便利,可是設計它們的初衷是用於簡單運算的。在模板中放入太多的邏輯會讓模板太重且難以維護。前端
回想咱們編寫的 React 代碼,是否也在 JSX(render 函數)中放入了太多的邏輯致使 render
函數過於龐大,難以維護?vue
說到 React 以前,咱們先看下 Vue,在 Vue 中,計算屬性主要有如下兩點特性:java
而在 React 中,計算屬性也是常常可見,相信各位熟悉 React 的讀者都寫過相似下面的代碼:react
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 函數調用,進而從新計算衍生值。編程
雖然能實現計算,但咱們仍是把計算邏輯放入了 render 函數致使了它的臃腫,這並不優雅。更好的作法是把計算邏輯抽出來,簡化 render 函數邏輯:緩存
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 中計算屬性對比函數執行,會有緩存,減小計算。由於計算屬性只有在它的相關依賴發生改變時纔會從新求值。markdown
這就意味着只要 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 { 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