React 16 新特性全解(中)

blank

React 16 新特性全解(中)

前言

這篇文章主要介紹v16.4~ v16.6的特性,如果你關注Hooks,可以直接看React 16新特性全解(下)

目錄

  • v16.4
  1. 新增指針事件
  2. fix生命週期函數
  • v16.5
  1. 提供新的調試工具
  • v16.6
  1. memo
  2. lazy
  3. suspense
  4. 簡化contextType
  5. 增加getDerivedStateFromError

16.4

新增指針事件

新增了對對指針設備(例如鼠標,觸控筆,或者手指觸摸)觸發的dom事件:

  • onPointerDown
  • onPointerMove
  • onPointerEnter
  • onPointerLeave
  • onPointerOver
  • onPointerOut

等等

但是這個事件僅支持那些支持指針事件的瀏覽器,比如目前最新版本的Chrome,Firefox,Edge IE瀏覽器)。但是如果你的應用程序真的依賴這些事件,可以使用第三方的polyfill。因為React團隊對了避免增大react的bundle size,所以沒有放進去。

demo時刻

這裡展示的是一個鼠標的拖拽功能,可以自己try try。

fix生命週期函數- getDerivedStateFromProps

React 16.0 ~ React 16.3 :

生命週期的圖如下:

blank

這裡我們可以看到getDerivedStateFromProps這個生命週期函數有個問題就是在Updating階段的時候,無論使用setState還是forceUpdate,都不調用getDerivedStateFromProps。

餵,這明顯有問題啊? !那我在updating階段都沒辦法監聽到props的改變來搞事情了。

React團隊還是很快意識到了這個問題的。所以在這個版本,他們fix了這個問題,新的圖長這樣:

blank

很簡單,就是fix了之前Updating階段用setState,forceUpdate調用不到getDerivedStateFromProps這個問題。

blank

v16.4.2

這個小版本的發布主要是為了fix一個服務端的XSS漏洞,我本來不想講,但是看大家那麼好學,還是提一下把。

呃,別告訴我你還不知道什麼是XSS漏洞,拜託趕緊現在立刻馬上去學一下,這個必會。一個簡單的例子就是用戶輸入了你不想讓他輸入的內容,尤其帶有JS html標籤的,給你的網站帶來了麻煩。

這個XSS具體場景是這樣的:

letprops={};// 注意:这里props的属性依靠用户输入
props[userProvidedData]="hello";letelement=<div{...props}/>;lethtml=ReactDOMServer.renderToString(element);

此時,用戶輸入:

letuserProvidedData='></div><script>alert("hi")</script>';

那麼最終生成的html就會變成:

<div></div><script>alert("hi")</script>

這就是我們常見的XSS攻擊。

但是現實中我們dom元素屬性需要依賴用戶輸入的場景非常的少,所以對於大部分應用來說沒有影響,最重要的是意味著對大部分開發者都沒有影響,這樣我們就不用擔心要半夜起來改代碼,還是可以的。

blank

v16.5 React Profiler

這個版本提供了對新的Profiler DevTools插件的支持。這個插件就厲害了,可以通過收集每個組件的渲染耗時,來幫助我們找到ReactApp的渲染瓶頸,並且整個界面更加清爽清晰,簡直是開發者的福利。

話不多說,下面來講解下如何使用:

  1. 首先安裝React DevTools插件

如果你的React版本已經升到16.5以上,那麼你的DevTools的界面會變成這樣: 打開第二個tab。

blank

2.點擊中間這個button開始記錄

blank

3.接著操作你想要記錄的操作,完事之後點stop

blank

4.看結果圖

在講解之前,先普及一些知識。知道Fiber的同學應該都了解,現在React渲染過程分為兩個階段:

一、render階段

這個階段主要是對比,有那些DOM節點需要更新。

二、commit階段

將第一階段收集的訊息更新到真實節點,完成之後會調用componentDidMount跟componentDidUpdate。

blank

接著看結果圖。

圖示一的地方顯示的是每一次commit的耗時,其中黑色表示當前選中的commit,可以左右移動來選擇,其中柱子越高說明這次的commit耗時約多。

圖示二表示的是這次commit發生在第幾S,它的render階段耗時多少。

圖示三表示的是這次commit裡每一個組件的耗時。由圖中我們可以看到Router耗時最多,達到18.4ms。而耗時主要來源於Nav 跟Route組件。如果你去點擊每個組件,還可以在右邊看到這個組件此時的state跟props,那就可以很清晰的知道這個組件此刻在渲染什麼。

blank

小技巧

再說最後一個小技巧,你可以選中某個組件,然後點擊右上角兩次相鄰的commit,這樣你就知道是哪個state的改變引發了這次的re-render。比如下面的例子,就是scrollOffset的變化導致整個App的變化。

blank
blank

以上就是一些基本使用,關於實際例子的演示,還挺多的,可以單獨寫一篇文章了。有時間的話在給大家介紹。

v16.6

memo

React 15 :如果你想阻止組件的重複渲染,在class component裡可以使用PureComponent, shouldComponentUpdate來幫助你。但是如果你是function component,對不起,沒有這個功能, 只能每次都重新渲染。

React 16 :為了全面擁抱function component,React團隊寫了memo來幫助function component實現這個阻止重複渲染的功能。

demo

在這個demo可以看到,如果沒有memo,每次點擊button控制台都會打印render表示重新渲染。但是加了memo之後,就不會了。

lazy、suspense

lazy需要跟Suspence配合使用,所以這裡放在一起介紹。

lazy實際上是幫助我們實現代碼分割的功能,使用過webpack的同學都知道,webpack也有這個功能。那為什麼他們都要做這個功能呢?

其實是這樣的:由於有些內容,我們並不一定要在首屏展示,所以這些資源我們沒有必要一開始就要去獲取,那麼這些資源就可以動態獲取。這樣的話,相當於把不需要首屏展示的代碼分割出來,減少首屏代碼的體積,提升性能。

demo

在這個demo中,有3個tab。但是我們的首屏只需要先展示一個,所以其他的可以動態引入。這里以動態引入B為例:

<!--importBfrom"./B";-->// 需要用到的时候才加载进来,当然还有预加载更好
constB=lazy(()=>import("./B"));

但是這樣的話,一開始就可以點擊B,會報錯。因為需要當組件還在加載渲染的時候,需要一個place holder防止組件還沒加載完畢的時候可以有東西顯示給用戶。

這時候Suspence得作用就出來了。 Suspence 很像Error Boundary,不同的是Error Boundary是用來捕獲錯誤,顯示相應的callback組件。而Suspence是用來捕獲還沒有加載好的組件,並暫停渲染,顯示相應的callback。

我們給B加上Suspense 就搞定了。

<Suspensefallback={<div>Loading...</div>}><TabPanel><B/></TabPanel></Suspense>

跟Error Boundary一樣,Suspence也有一個放置位置的問題,是整個包裹在App外,還是只是給單獨的組件包裹?

我的選擇與Error Boundary依舊一致,應該將其包裹在子組件外面,因為這樣當某個組件沒有加載好的時候,不會影響到整個App都顯示一個loading的標識。

注意:

  1. SSR不支持lazy這個特性。
  2. Lazy 必須搭配Suspence使用,否則會報錯

進一步優化:

這裡我們在進一步思考一個點,目前我們的B組件是需要用到的時候才加載。萬一這個組件需要獲取數據,使得他顯示比較慢,就會顯示loading,導致我們用戶體驗比較差呢。所以我們可否在瀏覽器閒著的時候預加載這些即將要用到資源?

答案是可以的,React團隊也在做這件事情。但是這個API目前為止還沒有Ready,大家先知道這個事情好了~

簡化static contextType

簡化獲取context的方式,之前需要用一個在外層包裹一個<Consumer> ,如下:

// Theme context, default to light themeconstThemeContext=React.createContext('light');// Signed-in user contextconstUserContext=React.createContext({name:'Guest',});classAppextendsReact.Component{render(){const{signedInUser,theme}=this.props;// App component that provides initial context valuesreturn(<ThemeContext.Providervalue={theme}><UserContext.Providervalue={signedInUser}><Layout/></UserContext.Provider></ThemeContext.Provider>);}}functionLayout(){return(<div><Sidebar/><Content/></div>);}// A component may consume multiple contexts//同時如果是function component用ConsumerfunctionContent(){return(<ThemeContext.Consumer>{theme=>(<UserContext.Consumer>{user=>(<ProfilePageuser={user}theme={theme}/>)}</UserContext.Consumer>)}</ThemeContext.Consumer>);}

現在可以直接通過this.context獲取。

classMyClassextendsReact.Component{staticcontextType=MyContext;componentDidMount(){letvalue=this.context;/* perform a side-effect at mount using the value of MyContext */}componentDidUpdate(){letvalue=this.context;/* ... */}componentWillUnmount(){letvalue=this.context;/* ... */}render(){letvalue=this.context;/* render something based on the value of MyContext */}}MyClass.contextType=MyContext;

新增static getDerivedStateFromError

v16.3這個版本里,React 除了Error Boundaries來捕獲錯誤,裡面主要是使用了componentDidCatch來捕獲錯誤。但是它是在錯誤已經發生之後並且render函數被調用之後,才會被調用。也就是說如果一個組件出現的錯誤,在調用componentDidCatch之前只能返回null給用戶。
而getDerivedStateFromError 可以在render函數之嵌捕獲到錯誤,所以它更適合寫用來顯示fallback UI的邏輯。

注意事項: componentDidCatch,getDerivedStateFromError都無法捕獲服務端的錯誤,但是React團隊正在努力支持SSR。

改進前的ErrorBoundary:

classErrorBoundaryextendsReact.Component{state={hasError:false};componentDidCatch(error,info){this.setState({hasError:false})logErrorToMyService(error,info);}render(){if(this.state.hasError){// You can render any custom fallback UIreturn<h1>Somethingwentwrong.</h1>;}returnthis.props.children;}

改進後的ErrorBoundary(推薦寫法):

classErrorBoundaryextendsReact.Component{state={hasError:false};staticgetDerivedStateFromError(error){// Update state so the next render will show the fallback UI.//更新state所以下次render可以立刻顯示fallback UIreturn{hasError:true};}componentDidCatch(error,info){// You can also log the error to an error reporting servicelogErrorToMyService(error,info);}render(){if(this.state.hasError){// You can render any custom fallback UIreturn<h1>Somethingwentwrong.</h1>;}returnthis.props.children;}

以上就是v16.4 ~ v16.6的全部內容,考慮到大家應該比較累了,Hooks又是比較大的特性,所以還是分到下一篇吧~

本文對你有幫助的話,點個贊吧~

What do you think?

Written by marketer

blank

淺談ES6中迭代器和生成器

blank

React新特性全解(下)– 一文讀懂Hooks