精讀《React 的多態性》

1 引言

本週精讀的文章是:surprising-polymorphism-in-react-applications,看看做者是如何解釋這個多態性含義的。javascript

讀完文章才發現,文章標題改成 Redux 的多態性更穩當,由於整篇文章都在說 Redux,而 Redux 使用場景不侷限於 React。java

2 概述

Redux immutable 特性可能產生瀏覽器沒法優化的性能問題,也就是瀏覽器沒法作 shapes 優化,也就是上一篇精讀《JS 引擎基礎之 Shapes and Inline Caches》 裏提到的。node

先看看普通的 redux 的 reducer:react

const todo = (state = {}, action) => {
  switch (action.type) {
    case "ADD_TODO":
      return {
        id: action.id,
        text: action.text,
        completed: false
      };
    case "TOGGLE_TODO":
      if (state.id !== action.id) {
        return state;
      }

      return Object.assign({}, state, {
        completed: !state.completed
      });

    default:
      return state;
  }
};

咱們簡化一下使用場景,假設基於這個 reducer todo,生成了兩個新 store s1 s2git

const s1 = todo(
  {},
  {
    type: "ADD_TODO",
    id: 1,
    text: "Finish blog post"
  }
);

const s2 = todo(s1, {
  type: "TOGGLE_TODO",
  id: 1
});

看上去很常見,也的確如此,咱們每次 dispatch 都會根據 reducer 生成新的 store 樹,並且是一個新的對象。然而對 js 引擎而言,這樣的代碼可能作不了 Shapes 優化(關於 Shapes 優化建議閱讀上一期精讀 Shapes 優化),也就是最須要作優化的全局 store,在生成新 store 時沒法被瀏覽器優化,這個問題很容易被忽視,但的確影響不小。es6

至於爲何會阻止 js 引擎的 shapes 優化,看下面的代碼:github

// transition-trees.js
let a = {x:1, y:2, z:3};

let b = {};
b.x = 1;
b.y = 2;
b.z = 3;

console.log("a is", a);
console.log("b is", b);
console.log("a and b have same map:", %HaveSameMap(a, b));

經過 node --allow-natives-syntax test.js 執行,經過調用 node 原生函數 %HaveSameMap 判斷這種狀況下 ab 是否共享一個 shape(v8 引擎的 Shape 實現稱爲 Map)。typescript

image

結果是 false,也就是 js 引擎沒法對 a b 作 Shapes 優化,這是由於 ab 對象初始化的方式不一樣。redux

一樣,在 Redux 代碼中經常使用的 Object.assign 也有這個問題:數組

image

由於新的對象以 {} 空對象做爲最初狀態,js 引擎會爲新對象建立 Empty Shape,這與原對象的 Shape 必定不一樣。

順帶一提 es6 的解構語法也存在一樣的問題,由於 babel 將解構最終解析爲 Object.assign

image

對這種尷尬的狀況,做者的建議是對全部對象賦值時都是用 Object.assign 以保證 js 引擎能夠作 Shapes 優化:

let a = Object.assign({}, {x:1, y:2, z:3});

let b = Object.assign({}, a);

console.log("a is", a);
console.log("b is", b);
console.log("a and b have same map:", %HaveSameMap(a, b)); // true

3 精讀

這篇文章須要與上一篇 精讀《JS 引擎基礎之 Shapes and Inline Caches》 連起來看容易理解。

做者描述的性能問題是引擎級別的 Shapes 優化問題,讀過上篇精讀就很容易知道,只有相同初始化方式的對象才被 js 引擎作優化,而 Redux 頻繁生成的 immutable 全局 store 是否能被優化呢?答案是「每每不能」,由於 immutable 賦值問題,咱們每每採用 Object.assign 或者解構方式賦值,這種方式產生的新對象與原對象的 Shape 不一樣,致使 Shape 沒法複用。

這裏解釋一下疑惑,爲何說 immutable 對象之間也要優化呢?這不是兩個不一樣的引用嗎?這是由於 js 引擎級別的 Shapes 優化就是針對不一樣引用的對象,將對象的結構:Shape 與數據分離開,這樣能夠大幅優化存儲效率,對數組也同樣,上一篇精讀有詳細介紹。

因此筆者更推薦使用好比 immutable-js 這種庫操做 immutable 對象,而不是 Object.assign,由於封裝庫內部是可能經過統一對象初始化方式利用 js 引擎進行優化的。

4 總結

原文提到的多態是指多個相同結構對象,被拆分紅了多個 Shape;而單態是指這些對象能夠被一個 Shape 複用。

筆者之前也經歷過從 Object.assign 到 Immutablejs 庫,最後又回到解構新語法的經歷,以爲在層級不深狀況下解構語法能夠代替 Immutablejs 庫。

經過最近兩篇精讀的分析,咱們須要從新思考這樣作帶來的優缺點,由於在 js 環境中,Object.assign 的優化效率比 Immutablejs 庫更低。

最後,也徹底不必如今就開始重構,由於這只是 js 運行環境中很小一部分影響因素,好比爲了引入 Immutablejs 讓你的網絡延時增長了 100%?因此僅在有必要的時候優化它。

5 更多討論

討論地址是: 精讀《React 的多態性》 · Issue #92 · dt-fe/weekly

若是你想參與討論,請點擊這裏,每週都有新的主題,週末或週一發佈。

相關文章
相關標籤/搜索