前段时间React的16版本发布了,采用了MIT开源许可证,新增了一些新的特性。react
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是对核心算法的一次从新实现”,后续再深刻学习