十分钟带你快速了解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 相关文章推荐
Javascript 继承实现例子
Aug 12 Javascript
jQuery EasyUI API 中文文档 - DataGrid数据表格
Nov 17 Javascript
js 控制图片大小核心讲解
Oct 09 Javascript
原生javascript实现DIV拖拽并计算重复面积
Jan 02 Javascript
jQuery 1.9.1源码分析系列(十)事件系统之绑定事件
Nov 19 Javascript
小白谈谈对JS原型链的理解
May 03 Javascript
Vue.js组件tab实现选项卡切换
Mar 23 Javascript
Iphone手机、安卓手机浏览器控制默认缩放大小的方法总结(附代码)
Aug 18 Javascript
vue router 跳转后回到顶部的实例
Aug 31 Javascript
JavaScript禁用右键单击优缺点分析
Jan 20 Javascript
layui当点击文本框时弹出选择框,显示选择内容的例子
Sep 02 Javascript
RxJS在TypeScript中的简单使用详解
Apr 13 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
PHP 图片文件上传实现代码
2010/12/29 PHP
PHP中获取文件扩展名的N种方法小结
2012/02/27 PHP
php过滤html标记属性类用法实例
2014/09/23 PHP
PHP模板引擎smarty详细介绍
2015/05/26 PHP
PHP常见字符串操作函数与用法总结
2019/03/04 PHP
php设计模式之观察者模式定义与用法经典示例
2019/09/19 PHP
基于ThinkPHP删除目录及目录文件函数
2020/10/28 PHP
一段批量给页面上的控件赋值js
2010/06/19 Javascript
Javascript检查图片大小不要让大图片撑破页面
2014/11/04 Javascript
JavaScript中Function()函数的使用教程
2015/06/04 Javascript
js实现不提交表单获取单选按钮值的方法
2015/08/21 Javascript
jquery淡入淡出效果简单实例
2016/01/14 Javascript
非常棒的jQuery图片轮播效果
2016/04/17 Javascript
学习Angular中作用域需要注意的坑
2016/08/17 Javascript
基于Bootstrap的Metronic框架实现条码和二维码的生成及打印处理操作
2016/08/29 Javascript
javascript滚轮事件基础实例讲解(37)
2017/02/14 Javascript
JS实现下拉菜单列表与登录注册弹窗效果
2017/08/10 Javascript
js实现扫雷小程序的示例代码
2017/09/27 Javascript
微信小程序自定义prompt组件步骤详解
2018/06/12 Javascript
详解原生JS动态添加和删除类
2019/03/26 Javascript
解决vue-cli 打包后自定义动画未执行的问题
2019/11/12 Javascript
Vue发布订阅模式实现过程图解
2020/04/30 Javascript
Python实现多线程抓取网页功能实例详解
2017/06/08 Python
python字符串的方法与操作大全
2018/01/30 Python
使用python爬取抖音视频列表信息
2019/07/15 Python
Python RabbitMQ实现简单的进程间通信示例
2020/07/02 Python
修复iPhone的safari浏览器上submit按钮圆角bug
2012/12/24 HTML / CSS
VisionPros美国站:加拿大在线隐形眼镜和眼镜零售商
2020/02/11 全球购物
几道Web/Ajax的面试题
2016/11/05 面试题
XMLHttpRequest对象在IE和Firefox中创建方式有没有不同
2016/03/23 面试题
什么是方法的重载
2013/06/24 面试题
《大作家的小老师》教学反思
2014/04/16 职场文书
小学三好学生事迹材料
2014/08/15 职场文书
幼儿园教师考核评语
2014/12/31 职场文书
2019职场单身人才调研报告:互联网行业单身比例最高
2019/08/07 职场文书
浅析Python中的套接字编程
2021/06/22 Python