React最新16.9,支持組件性能評估!

概述

React團隊在8月9日發佈了最新的16.9版本,本文簡單介紹一下在新版本中都有哪些變動和須要注意的地方。javascript

關鍵變動以下:html

  • 在16.9版本中使用componentWillMountcomponentWillReceivePropscomponentWillUpdate將會收到React發出的警告。
  • 爲大型React應用提供React.Profiler以進行性能評估
  • 使用javascript:形式的url,React將拋出warning,而且這種寫法在將來的主要版本中會被禁止。
  • 廢棄 Factory 組件
  • 用於測試的 act()方法正式支持異步

Unsafe 生命週期

在16.3版本時,React團隊就討論過這三個生命週期潛在的問題,而且在16.3版本中將加入UNSAFE_前綴做爲他們的別名,按照當時定下的計劃,將會在16.9中拋出warning,而且在17.0的大版本中完全移除componentWillMount這三個生命週期。java

  • componentWillMount → UNSAFE_componentWillMount
  • componentWillReceiveProps → UNSAFE_componentWillReceiveProps
  • componentWillUpdate → UNSAFE_componentWillUpdate
對於老項目來講意味着什麼?

其實沒什麼太大的影響,官方保證即使在17.0中,使用UNSAFE_的生命週期也能夠正常使用,也只是生命週期函數名字變動了而已。想要在老項目升級時避免拋出warning,能夠手動變動函數名。固然官方爲也可使用官方提供的工具codemod來一鍵變動:react

cd your_project
npx react-codemod rename-unsafe-lifecycles
複製代碼

開發團隊也能夠在項目中加入嚴格模式(Strict Mode)<React.StrictMode>來禁止使用這類有潛在風險的生命週期。git

使用React.Profiler進行性能評估

在此次React 16.9更新中,提供了一種經過編程的方式來收集測量的代碼的方式,<React.Profiler>一般在大型的React項目中會使用到。github

它能夠做爲一個節點添加在React應用的任意一處,而且能評估React 應用程序渲染的頻率以及渲染的 「成本」。其目的是幫助標識應用程序中渲染緩慢的部分,並可能會更易於進行 memoization 等優化web

render(
  <App>
    <Profiler id="Navigation" onRender={callback}>
      <Navigation {...props} />
    </Profiler>
    <Main {...props} />
  </App>
);
複製代碼

Profiler能夠在多處使用,也能夠嵌套使用。它接受兩個參數id和onRender,onRender會在React更新的commit階段,也就是內部更新的最後一個階段,在這個階段React會將全部的更新變現,反饋到DOM上去。在onRender觸發時也會帶回來一些關於本次更新的性能參數:編程

  • id, 用於區分多個Pofiler,由props傳入
  • phase, 值爲 "mount" 或者 "update" ,表示當前組件樹是第一次掛載(mount)仍是處於更新週期(update)
  • actualDuration, 當前組件樹更新所花費的時間,使用了一些組件的緩存方法例如React.memo能夠看到較爲明顯的減小
  • baseDuration, 初始掛載組件樹的時間,能夠理解爲沒有任何優化狀況下的渲染所花費的時間
  • startTime, 本輪更新的初始時間戳
  • commitTime, 本輪更新的結束時間戳(到達commit階段截止)
  • interactions 本輪更新的調度堆棧

有了如上組件更新的回調信息,咱們能夠更加精細地判斷使用的優化方法所帶來的收益。數組

須要注意的是Profiler即使是一個輕量級的組件,可是依然會有性能和計算開銷,不推薦在生產環境使用。瀏覽器

用於測試的 act()方法正式支持異步

react官方提供了一個用於測試組件的內置庫react-dom/test-utils,爲了更好地在測試環境模仿瀏覽器和用戶的真實行爲以及應社區的意願爲背景下,官方團隊賦予act()異步調用和集中處理state變動的能力。 在之前的版本中,act()中寫異步代碼(異步狀態更新)將會拋出以下警告

An update to SomeComponent inside a test was not wrapped in act(...).

在 React 16.9 中, act() 也支持了異步函數, 而且可使用await

await act(async () => {
  // ...
});
複製代碼

React團隊是很是推薦你們爲本身組件提供測試用例的,在這篇文章中提供了一些測試技巧和應用場景以及使用act()的地方,也包括對hooks的測試場景,好比測試一個hook的事件:

import React, { useState } from "react";

export default function Toggle(props) {
  const [state, setState] = useState(false);
  return (
    <button onClick={() => { setState(previousState => !previousState); props.onChange(!state); }} data-testid="toggle" > {state === true ? "Turn off" : "Turn on"} </button>
  );
}
複製代碼

測試用例以下

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";

import Toggle from "./toggle";

let container = null;
beforeEach(() => {
  // setup a DOM element as a render target
  container = document.createElement("div");
  // container *must* be attached to document so events work correctly.
  document.body.appendChild(container);
});

afterEach(() => {
  // cleanup on exiting
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});

it("changes value when clicked", () => {
  const onChange = jest.fn();
  act(() => {
    render(<Toggle onChange={onChange} />, container); }); // get a hold of the button element, and trigger some clicks on it const button = document.querySelector("[data-testid=toggle]"); expect(button.innerHTML).toBe("Turn off"); act(() => { button.dispatchEvent(new MouseEvent("click", { bubbles: true })); }); expect(onChange).toHaveBeenCalledTimes(1); expect(button.innerHTML).toBe("Turn on"); act(() => { for (let i = 0; i < 5; i++) { button.dispatchEvent(new MouseEvent("click", { bubbles: true })); } }); expect(onChange).toHaveBeenCalledTimes(6); expect(button.innerHTML).toBe("Turn on"); }); 複製代碼

這些示例採用了原生 DOM API,但也可使用 React Testing Library來減小樣板代碼。它的許多方法已經經過 act() 進行了實現

棄用 javascript: 形式的不安全 URL

a標籤的href若是使用javascript:的寫法,在16.9版本中繼續使用這種寫法React將會拋出警告

const userProfile = {
  website: "javascript: alert('you got hacked')",
};
// This will now warn:
<a href={userProfile.website}>Profile</a>
複製代碼

而且該寫法將會在將來的主要版本中會拋出錯誤,也就是將會禁止這種易產生安全漏洞的寫法

廢棄 Factory 組件

在 Babel尚未成爲 JavaScript Class 的主流編譯工具之前,能夠在React中採用"factory" 的寫法來建立組件,該組件使用 render 方法返回一個對象

function FactoryComponent() {
  return { render() { return <div />; } } } 複製代碼

這種方式使人迷惑,由於它看起來像函數組件 ,然而它並非。 React支持它會致使庫變大且變慢。所以,在 16.9 中正在棄用此模式,並在遇到警告時輸出警告。若是項目中依賴了此組件,能夠經過添加 FactoryComponent.prototype=React.Component.prototype 來作兼容。

changlog

React

  • 提供 <React.Profiler> API 實現以編程的方式進行性能評估。(@bvaughn in #15172)

  • 刪除 unstable_ConcurrentMode 以支持 unstable_createRoot。(@acdlite in #15532)

React DOM

  • 棄用以 UNSAFE_* 開頭的舊生命週期方法。(@bvaughn in #15186 and @threepointone in #16103)

  • 棄用 javascript: 形式的 URL。(@sebmarkbage in #15047)

  • 棄用不經常使用的 "module pattern" (factory) 組件。(@sebmarkbage in #15145)

  • 在video 組件上添加對 disablePictureInPicture 屬性的支持。(@eek in #15334)

  • 爲 embed 添加對 onLoad 事件的支持。(@cherniavskii in #15614)

  • 爲在 DevTools 中編輯 useState 的 state 提供支持。(@bvaughn in #14906)

  • 爲在 DevTools 中切換 Suspense 提供支持。(@gaearon in #15232)

  • 當 setState 在 useEffect 中循環調用時,發出警告。(@gaearon in #15180)

  • 修復內存泄露。(@paulshen in #16115)

  • 修復 Suspense 包裹的組件中使用 findDOMNode 發生崩潰的問題。(@acdlite in #15312)

  • 修復由於刷新太晚而致使 pending effect 的狀況。(@acdlite in #15650)

  • 修復警告信息中不正確的參數順序。(@brickspert in #15345)

  • 修復當存在 !important 樣式時,隱藏 Suspense 降級節點的問題。(@acdlite in #15861 and #15882)

  • 提升 hydration 的性能。(@bmeurer in #15998)

React DOM Server

  • 修復 camelCase 自定義 CSS 屬性名稱的錯誤輸出。(@bedakb in #16167)

  • React Test Utilities and Test Renderer

  • 添加 act(async()=>...) 來測試異步狀態更新。(@threepointone in #14853)

  • 添加對不一樣渲染器嵌套 act 的支持。(@threepointone in #16039 and #16042)

  • 在嚴格模式下,若是反作用函數在 act 以外被調用,就會發出警告。(@threepointone in #15763 and #16041)

  • 當在錯誤的渲染器中使用 act 時發出警告。(@threepointone in #15756)


關注【IVWEB社區】公衆號獲取每週最新文章,通往人生之巔!

相關文章
相關標籤/搜索