React 16的新特性

前段時間React的16版本發佈了,採用了MIT開源許可證,新增了一些新的特性。react

  1. Error Boundary
  2. render方法新增返回類型
  3. Portals
  4. 支持自定義DOM屬性
  5. setState傳入null時不會再觸發更新
  6. 更好的服務器端渲染
  7. 新的打包策略
  8. ...

1. 使用Error Boundary處理錯誤組件算法

以前,一旦某個組件發生錯誤,整個組件樹將會從根節點被unmount下來。React 16修復了這一點,引入了Error Boundary的概念,中文譯爲「錯誤邊界」,當某個組件發生錯誤時,咱們能夠經過Error Boundary捕獲到錯誤並對錯誤作優雅處理,如使用Error Boundary提供的內容替代錯誤組件。Error Boundary能夠看做是一種特殊的React組件,新增了componentDidCatch這個生命週期函數,它能夠捕獲自身及子樹上的錯誤並對錯誤作優雅處理,包括上報錯誤日誌、展現出錯提示,而不是卸載整個組件樹。(注:它並不能捕獲runtime全部的錯誤,好比組件回調事件裏的錯誤,能夠把它想象成傳統的try-catch語句)數組

//最佳實踐:將ErrorBoundary抽象爲一個公用的組件類

import React, { Component } from 'react'

export default class ErrorBoundary extends Component {
  constructor(props) {
    super(props)
    this.state = { hasError: false }
  }
  componentDidCatch(err, info) {
    this.setState({ hasError: true })
    //sendErrorReport(err,info)
  }
  render(){
    if(this.state.hasError){
      return <div>Something went wrong!</div>
    }
    return this.props.children
  }
}

咱們能夠在容易出錯的組件外使用ErrorBoundary將它包裹起來,以下服務器

//使用方式
render(){
  return (
    <div>
      <ErrorBoundary>
        <Profile user={this.state.user} />
      </ErrorBoundary>
      <button onClick={this.onClick}>Update</button>
    </div>
  )
}

若是Profile組件發生錯誤,將會使用ErrorBoundary提供的<div>Something went wrong</div>代替它,而不會引發整個組件樹的卸載。架構

2. render方法新增返回類型app

在React 16中,render方法支持直接返回string,number,boolean,null,portal,以及fragments(帶有key屬性的數組),這能夠在必定程度上減小頁面的DOM層級。dom

//string
render(){
  return 'hello,world'
}

//number
render(){
  return 12345
}

//boolean
render(){
  return isTrue?true:false
}

//null
render(){
  return null
}

//fragments,未加key標識符,控制檯會出現warning
render(){
  return [
    <div>hello</div>,
    <span>world</span>,
    <p>oh</p>
  ]
}

以上各類類型如今都可以直接在render中返回,不須要再在外層包裹一層容器元素,不過在返回的數組類型中,須要在每一個元素上加一個惟一且不變的key值,不然控制檯會報一個warning。函數

3.使用createPortal將組件渲染到當前組件樹以外post

Portals機制提供了一種最直接的方式能夠把一個子組件渲染到父組件渲染的DOM樹以外。默認狀況下,React組件樹和DOM樹是徹底對應的,所以對於一些Modal,Overlay之類的組件,一般是將它們放在頂層,但邏輯上它們可能只是屬於某個子組件,不利於組件的代碼組織。經過使用createPortal,咱們能夠將組件渲染到咱們想要的任意DOM節點中,但該組件依然處在React的父組件以內。帶來的一個特性就是,在子組件產生的event依然能夠被React父組件捕獲,但在DOM結構中,它卻不是你的父組件。對於組件組織,代碼切割來講,這是一個很好的屬性。性能

//實現一個簡易蒙層效果,抽象出一個通用的Overlay組件
import React, { Component } from 'react';
import ReactDOM from 'react-dom';

export default class Overlay extends Component {
  constructor(props) {
    super(props);
    this.container = document.createElement('div');
    document.body.appendChild(this.container);
  }
  componentWillUnmount() {
    document.body.removeChild(this.container);
  }
  render() {
    return ReactDOM.createPortal(
      <div className='overlay'>
        <span className='overlay-close' onClick={this.props.onClose}>×</span>
        {this.props.children}
      </div>,
      this.container
    )
  }
}
//該組件對應的樣式以下
.overlay{
  box-sizing:border-box;
  position: fixed;
  top:50%;
  left:50%;
  width:260px;
  height:200px;
  margin-left:-130px;
  margin-top:-100px;
  padding:10px;
  background-color: #fff;
  outline: rgba(0,0,0,.5) solid 9999px;
}
.overlay-close{
  position: absolute;
  top:10px;
  right:10px;
  color:red;
  cursor: pointer;
}

使用方式以下:

class App extends Component {
 constructor(props) {
  super(props);
  this.state = {
   overlayActive: false
  }
  this.closeOverlay = this.closeOverlay.bind(this);
  this.showOverlay = this.showOverlay.bind(this);
 }
 closeOverlay() {
  this.setState({ overlayActive: false })
 }
 showOverlay() {
  this.setState({ overlayActive: true })
 }
 render() {
  return (
   <div className="App">
    <div>hello world!</div>
    {this.state.overlayActive &&
     <Overlay onClose={this.closeOverlay}>overlay content</Overlay>}
    <button onClick={this.showOverlay}>show</button>
   </div>
  );
 }
}

效果如圖:

4.支持自定義DOM屬性

在以前的版本中,React會忽略沒法識別的HTML和SVG屬性,自定義屬性只能經過data-*形式添加,如今它會把這些屬性直接傳遞給DOM(這個改動讓React能夠去掉屬性白名單,從而減小了文件大小),不過有些寫法仍然是無效的。如DOM傳遞的自定義屬性是函數類型或event handler時,依然會被React忽略。

//錯誤寫法
render(){
  return(
    <div a={()=>{}} onclick={this.showOverlay}></div>
  )
)
//Warning: Invalid event handler property `onclick`. Did you mean `onClick`?
//Warning: Invalid value for prop `a` on <div> tag. Either remove it from the element, or pass a string or number value to keep it in the DOM.

如今class和tabindex等屬性能夠被傳遞給DOM,但依然會報一個Warning,建議使用標準的駝峯式className,tabIndex等。

5.setState傳入null時不會再觸發更新

好比在一個選擇城市的函數中,當點擊某個城市時,newValue的值可能發生改變,也多是點擊了原來的城市,值沒有變化,返回null則能夠直接避免觸發更新,不會引發重複渲染,不須要在shouldComponentUpdate函數裏面去判斷。

selectCity(e){
  const newValue = e.target.value;
  this.setState((state)=>{
    if(state.city===newValue){
      return null;
    }
    return {city:newValue}
  })
)

注意:如今setState回調(第二個參數)會在componentDidMount/componentDidUpdate後當即觸發,而不是等到全部組件渲染完成以後。

6.更好的服務器端渲染

React 16的SSR被徹底重寫,新的實現很是快,接近3倍性能於React 15,如今提供一種流模式streaming,能夠更快地把渲染的字節發送到客戶端。另外,React 16在hydrating(注:指在客戶端基於服務器返回的HTML再次從新渲染)方面也表現的更好,React 16再也不要求客戶端的初始渲染徹底和服務器返回的渲染結果一致,而是儘可能重用已經存在的DOM元素。不會再有checksum(標記驗證)!並對不一致發出警告。通常來講,在服務器和客戶端渲染不一樣的內容是不建議的,但這樣作在某些狀況下也是有用的(好比,生成timestamp)。

7.新的打包策略

新的打包策略中去掉了process.env檢查。

React 16的體積比上個版本減少了32%(30% post-gzip),文件尺寸的減少一部分要歸功於打包方法的改變。

react is 5.3 kb (2.2 kb gzipped), down from 20.7 kb (6.9 kb gzipped).
react-dom is 103.7 kb (32.6 kb gzipped), down from 141 kb (42.9 kb gzipped).
react + react-dom is 109 kb (34.8 kb gzipped), down from 161.7 kb (49.8 kb gzipped).

寫在最後,React 16採用了新的核心架構React Fiber。官方解釋是「React Fiber是對核心算法的一次從新實現」,後續再深刻學習

相關文章
相關標籤/搜索