乾坤大挪移!React 也能 「用上」 computed 屬性

原創不易,但願能關注下咱們,再順手點個贊~~

本文首發於政採雲前端團隊博客: 乾坤大挪移!React 也能 「用上」 computed 屬性javascript

不二.png

前言,關於計算屬性

初次見到計算屬性一詞,是在 Vue 官方文檔 《計算屬性和偵聽器》 一節中,文章中是這樣描述計算屬性的:html

模板內的表達式很是便利,可是設計它們的初衷是用於簡單運算的。在模板中放入太多的邏輯會讓模板太重且難以維護。前端

回想咱們編寫的 React 代碼,是否也在 JSX(render 函數)中放入了太多的邏輯致使 render 函數過於龐大,難以維護?vue

React 中的計算屬性

說到 React 以前,咱們先看下 Vue,在 Vue 中,計算屬性主要有如下兩點特性:java

  1. 計算屬性以聲明的方式建立依賴關係,依賴的 data 或 props 變動會觸發從新計算並自動更新。
  2. 計算屬性是基於它們的響應式依賴進行緩存的。

而在 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 中的 firstNamelastNamefirstNamelastName 變動以後,變量 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>;
  }
}
複製代碼

進一步,使用 memoization 優化計算屬性

上文有提到在 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 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>;
}
複製代碼

總結

本文介紹了在 React 中如何實現相似 Vue 計算屬性(computed)的效果 —— 基於依賴緩存計算結果,實現邏輯計算與視圖渲染的解耦,下降 render 函數的複雜度。

從業務開發角度來說,Vue 提供的 API 極大地提升了開發效率。React 雖然在某些場景下,沒有官方的同類原生 API 支持,但得益於活躍的社區,工做中遇到的問題總能找到解決方案。且在摸索這些解決方案的同時,咱們還能學習到諸多經典的編程思想,幫助咱們更合理的運用框架,用技術解決業務問題。

招賢納士

招人,前端,隸屬政採雲前端大團隊(ZooTeam),50 餘個小夥伴正等你加入一塊兒浪~ 若是你想改變一直被事折騰,但願開始能折騰事;若是你想改變一直被告誡須要多些想法,卻無從破局;若是你想改變你有能力去作成那個結果,卻不須要你;若是你想改變你想作成的事須要一個團隊去支撐,但沒你帶人的位置;若是你想改變「5年工做時間3年工做經驗」;若是你想改變原本悟性不錯,但老是有那一層窗戶紙的模糊… 若是你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的本身。若是你但願參與到隨着業務騰飛的過程,親手參與一個有着深刻的業務理解、完善的技術體系、技術創造價值、影響力外溢的前端團隊的成長曆程,我以爲咱們該聊聊。任什麼時候間,等着你寫點什麼,發給ZooTeam@cai-inc.com

推薦閱讀

前端工程實踐之可視化搭建系統(一)

寫給前端工程師的 Serverless 入門

自動化 Web 性能優化分析方案

相關文章
相關標籤/搜索