# React16新特性

React更迭到了16版本,惭愧,接触前端没多久,刚用15来着,出了个react开源协议的问题,差点就弃坑了,还好react果断更到了16,采用了新的MIT开源协议,同时新增了一些特性,其实没多大改变,但是还是稍微了解一下吧。

  • Error Boundary
  • render方法新增返回类型
  • Portals
  • 支持自定义的DOM属性
  • setState传入null不会触发更新
  • 更好的服务器端渲染
  • 新的打包策略
  • ...

# Error Boundary

在以前的版本一旦组件出错,整个组件树都无法正常显示,会从根节点被unmount下来。React16针对这点,引入的Error Boundary的概念。我们可以通过Error Boundary捕获到错误并对错误做优雅处理,使用Error Boundary提供的内容替代错误组件。Error Boundary可以看做一种特殊的React组件,新增了componentDidCatch这个生命周期函数,他可以捕获自身及子树上的错误并对错误进行处理。

import React from 'react'

export default class ErrorBoundary extends Component {
  constructor(props){
    super(props)
    this.state = { hasError: false }
  }
  componentDidCatch(err, info){
    this.setState({hasError: true})
  }
  render(){
    if(this.state.hasError){
      return <div>Something went wrong</div>
    }
    return this.props.children
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

我们可以再容易出错的组件使用ErrorBoundary包裹

// 使用方法
render(){
  return (
    <div>
      <ErrorBoundary>
        <Profile user={this.state.user} />
      </ErrorBoundary>
      <button onClick={this.onClick}>Update</button>
    </div>
  )
}
1
2
3
4
5
6
7
8
9
10
11

# render方法新增返回类型

在React16中,render方法支持返回string,number,boolean, null,portal以及fragment(带有key属性的数组),可以再一定程度上减少DOM层级。

// string
render(){
  return 'hello'
}
// number
render(){
  return 123
}
// boolean
render(){
  return true
}
// null
render(){
  return null
}
// fragments
render(){
  return [
    <div key="1">hello</div>,
    <div key="2">world</div>,
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 使用createPortal将组建渲染到当前组件树之外

Portals机制提供了一种直接的方式可以把一个子组件渲染到父组件渲染的DOM树之外。通过使用createPortal,我们可以将组件渲染到我们想要的任意DOM节点中,但该组件依然处在React的父组件之内。带来的一个特性就是,在子组件产生的event依然可以被React父组件捕获,但在DOM结构中,它却不是你的父组件。对于组件组织,代码切割来说,这是一个很好的属性。

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'>&times;</span>
      </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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 支持自定义DOM属性

在之前的版本中,React会忽略无法识别的HTML和SVG属性,自定义属性只能通过data-*形式添加,现在它会把这些属性直接传递给DOM(这个改动让React去掉属性白名单,从而减少文件大小),不过有些写法依旧是无效的,如DOM传递的自定义属性是函数或event handler,依旧会被React忽略。

render(){
  return (
    <div a={()=>{}} onclick={this.showOverlay}></div>
  )
}
1
2
3
4
5

# setState传入null时不会更新

当不确定是否更新时,把值设置为null可以避免更新

onChange(e){
  let newValue = e.target.value
  if(e.target.value === this.state.city){
    newValue = null
  }
  this.setState({value: newValue})
}
1
2
3
4
5
6
7

现在setState回调会在componentDidMount/componentDidUpdate之后立刻触发,不用等到所有组件渲染完毕。

# 更好的服务器端渲染

React 16的SSR被完全重写,新的实现非常快,接近3倍性能于React 15,现在提供一种流模式streaming,可以更快地把渲染的字节发送到客户端。另外,React 16在hydrating(注:指在客户端基于服务器返回的HTML再次重新渲染)方面也表现的更好,React 16不再要求客户端的初始渲染完全和服务器返回的渲染结果一致,而是尽量重用已经存在的DOM元素。不会再有checksum(标记验证)!并对不一致发出警告。一般来说,在服务器和客户端渲染不同的内容是不建议的,但这样做在某些情况下也是有用的(比如,生成timestamp)。

# 新的打包策略

新的打包策略去掉了process.env检查。
React16的体积比上个版本见笑了32%,文件尺寸的减小一部分归功于打包方法的改变。