十分钟带你快速了解React16新特性


Posted in Javascript onNovember 10, 2017

前段时间React的16版本发布了,采用了MIT开源许可证,新增了一些新的特性。

  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方法新增返回类型

在React 16中,render方法支持直接返回string,number,boolean,null,portal,以及fragments(带有key属性的数组),这可以在一定程度上减少页面的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将组件渲染到当前组件树之外

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>
  );
 }
}

效果如图:

十分钟带你快速了解React16新特性

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是对核心算法的一次重新实现”,后续再深入学习。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
User Scripts: Video Download by User Scripts
May 14 Javascript
用JS将搜索的关键字高亮显示实现代码
Nov 08 Javascript
jQuery1.9.1源码分析系列(十六)ajax之ajax框架
Dec 04 Javascript
使用JQuery实现智能表单验证功能
Mar 08 Javascript
一些实用性较高的js方法
Apr 19 Javascript
JS实现选定指定HTML元素对象中指定文本内容功能示例
Feb 13 Javascript
js中如何完美的解析数据
Mar 18 Javascript
浅谈vue 单文件探索
Sep 05 Javascript
JavaScript 性能提升之路(推荐)
Apr 10 Javascript
node省市区三级数据性能测评实例分析
Nov 06 Javascript
微信小程序开发搜索功能实现(前端+后端+数据库)
Mar 04 Javascript
uniapp实现横向滚动选择日期
Oct 21 Javascript
JS实现移动端整屏滑动的实例代码
Nov 10 #Javascript
Three.js加载外部模型的教程详解
Nov 10 #Javascript
vue2导航根据路由传值,而改变导航内容的实例
Nov 10 #Javascript
three.js加载obj模型的实例代码
Nov 10 #Javascript
vue router-link传参以及参数的使用实例
Nov 10 #Javascript
vue-router2.0 组件之间传参及获取动态参数的方法
Nov 10 #Javascript
js+html获取系统当前时间
Nov 10 #Javascript
You might like
function.inc.php超越php
2006/12/09 PHP
PHP数据库万能引擎类adodb配置使用以及实例集锦
2014/06/12 PHP
PHP获取文件相对路径的方法
2015/02/26 PHP
php通过获取头信息判断图片类型的方法
2015/06/26 PHP
php实现往pdf中加数字签名操作示例【附源码下载】
2018/08/07 PHP
php ajax数据传输和响应方法
2018/08/21 PHP
php微信公众号开发之图片回复
2018/10/20 PHP
有效的捕获JavaScript焦点的方法小结
2009/10/08 Javascript
Jquery ajaxStart()与ajaxStop()方法(实例讲解)
2013/12/18 Javascript
用jquery修复在iframe下的页面锚点失效问题
2014/08/22 Javascript
Javascript编写2048小游戏
2015/07/07 Javascript
JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)
2016/12/14 Javascript
jQuery使用JSONP实现跨域获取数据的三种方法详解
2017/05/04 jQuery
使用canvas进行图像编辑的实例
2017/08/29 Javascript
javaScript 连接打印机,打印小票的实例
2017/12/29 Javascript
vue scroller返回页面记住滚动位置的实例代码
2018/01/29 Javascript
Vue修改项目启动端口号方法
2019/11/07 Javascript
JS闭包原理及其使用场景解析
2020/12/03 Javascript
[00:48]完美“圣”典2016风云人物:xiao8宣传片
2016/11/30 DOTA
[11:42]2018DOTA2国际邀请赛寻真——OG卷土重来
2018/08/17 DOTA
跟老齐学Python之类的细节
2014/10/13 Python
用Python进行TCP网络编程的教程
2015/04/29 Python
python 判断矩阵中每行非零个数的方法
2019/01/26 Python
python爬取Ajax动态加载网页过程解析
2019/09/05 Python
浅析python redis的连接及相关操作
2019/11/07 Python
详解在python操作数据库中游标的使用方法
2019/11/12 Python
k-means 聚类算法与Python实现代码
2020/06/01 Python
jupyter notebook远程访问不了的问题解决方法
2021/01/11 Python
澳大利亚男士西服品牌:M.J.Bale
2018/02/06 全球购物
英国最好的温室之家:Greenhouses Direct
2019/07/13 全球购物
Goodee官方商店:迷你投影仪
2021/03/15 全球购物
JAVA的事件委托机制和垃圾回收机制
2014/09/07 面试题
高一地理教学反思
2014/01/18 职场文书
《奇妙的国际互联网》 教学反思
2014/02/25 职场文书
2014年师德承诺书
2014/05/23 职场文书
2015年法院工作总结范文
2015/04/28 职场文书