前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >快速了解React 16新特性

快速了解React 16新特性

作者头像
江米小枣
发布2020-06-16 16:52:39
1.2K0
发布2020-06-16 16:52:39
举报
文章被收录于专栏:云前端云前端

前一段时间React v16.0发布了,作为react骚年,我们当然要关注版本更新之后,react新增了哪些特性呢?

支持render返回数组和字符串

  • 在以前,一个组件的 render 方法中如果要返回多个 element ,必须使用一个元素将它们包裹起来。这样可能会导致很多不必要的嵌套。
  • 现在,可以通过render方法返回一个数组,或者字符串:
代码语言:javascript
复制
 //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>
 ]
}

全新的内部架构

  • React 16 采用了React Fiber 的全新架构。React Fiber是什么东西呢?官方的解释是“React Fiber是对核心算法的一次重新实现”。 react加载或者更新组件的过程是同步进行的,所以当组件树比较庞大的时候,问题就出现了。 假设更新一个组件需要1毫秒,有200个组件需要更新的话,那就需要200毫秒,因为更新过程是同步的一层组件套一层组件,逐渐深入的,所以在这200毫秒内浏览器的主线程都被更新操作占用,如果此时用户想要点开一个下拉框或者往input元素里输入点什么,浏览器都不会有任何反应。这样的用户体验非常不好。 一个调用链很长并且计算量很大的任务的调用栈会如下图:
  • react Fiber 是如何解决同步操作时间过长的问题的呢?答案就是——分片。把一个耗时很长的任务分成很多小片,即让更新过程碎片化,每执行完一段任务,就交回控制权。这时react会检查有没有优先级更高的任务要做,如果有那就去执行,没有的话就继续更新。 有了分片之后,更新过程的调用栈如下图所示,中间每一个波谷代表深入某个分片的执行过程,每个波峰就是一个分片执行结束交还控制权的时机。
  • react Fiber看起来很厉害的样子,如果要用的话,还是有一些问题需要考虑的: 由于整个更新任务被分成多个分片,每个分片的执行时间很短,那么任务很有可能被打断,需要重新执行,所以某些生命周期函数在一次加载和更新过程中可能会被多次调用,那么依赖生命周期实现的业务逻辑很可能会受到影响。
  • 如果想要了解更多Fiber的信息,可以去看这个视频(https://www.youtube.com/watch?v=ZCuYPiUIONs)(需要翻墙)。

新增API:ReactDOM.createPortal

  • 在一般的 React 结构中,组件的嵌套关系和渲染出来的 DOM 的嵌套关系是一致的(子组件渲染出的 DOM 一定是在父组件渲染出的 DOM 的内部的)。Portals提供了一种方法,将子节点呈现在父组件的DOM层次结构之外的DOM节点中。
代码语言:javascript
复制
    ReactDOM.createPortal(child, container)
  • Portals的典型应用就是当父组件有overflo:hidden或者z-index样式,但是子组件需要显示在父组件之外,例如:对话框,提示框等。
代码语言:javascript
复制
import React from "react";
import {createPortal} from 'react-dom';
import style from './app.css';class Modal extends React.Component{   constructor() {
       super(...arguments);       const doc = window.document;
       this.node = doc.createElement('div');
       doc.body.appendChild(this.node);
   }   render() {
       // React does *not* create a new div. It renders the children into `divNode`.
       // `divNode` is any valid DOM node, regardless of its location in the DOM.
       // console.log(this.props.children,"this.props.children")
       return createPortal(
           this.props.children, //塞进传送门的JSX
           this.node //传送门的另一端DOM node
       );
   }   componentWillUnmount() {
       window.document.body.removeChild(this.node);
   }
}class App extends React.Component{
   constructor(props) {
       super(props);
       this.state = {showModal: false};
       
       this.handleShow = this.handleShow.bind(this);
       this.handleHide = this.handleHide.bind(this);
   }   handleShow() {
       this.setState({showModal: true});
   }
   
   handleHide() {
       this.setState({showModal: false});
   }   render(){
       // Show a Modal on click.
       // (In a real app, don't forget to use ARIA attributes
       // for accessibility!)
       const modal = this.state.showModal ? (
           <Modal>
               <div className={style.modal}>
                   <div className={style.innerCon}>
                       <p>这是一个模态框<p/>
                       <button onClick={this.handleHide}>关闭modal</button>
                   </div>
               </div>
           </Modal>
       ) : null;         return(
             <div className={style.app}>
               <p className={style.header}>
               This div has overflow: hidden.
               <button onClick={this.handleShow}>打开modal</button>
               {modal}
               </p>
             </div>
       )
   }
}ReactDOM.render(<App />,  document.getElementById("#root"));}

更好的错误处理机制

  • 之前的版本在渲染网页过程中,如果发生了运行时错误,那整个React框架就会处于一种被破坏的状态。为了解决这一问题,React 16引入了一个“error boundary”的新概念。
  • Error boundaries 是React组件,可以在其子组件树中的任何位置捕获JavaScript错误,记录这些错误,并显示备用UI而不是崩溃的组件树。可以把错误边界看成是一种类似于try-catch语句的机制,只不过是由React组件来实现的。
  • 这里涉及到一种新的生命周期函数叫componentDidCatch(error, info)。无论什么样的类组件,只要定义了这个函数,就成为了一个错误边界。
代码语言:javascript
复制
class ErrorBoundary extends React.Component {
 constructor(props) {
   super(props);
   this.state = { error: null, errorInfo: null };
 }
 
 componentDidCatch(error, errorInfo) {
   // Catch errors in any components below and re-render with error message
   this.setState({
     error: error,
     errorInfo: errorInfo
   })
   // You can also log error messages to an error reporting service here
 }
 
 render() {
   if (this.state.errorInfo) {
     // Error path
     return (
       <div>
         <h2>Something went wrong.</h2>
         <details style={{ whiteSpace: 'pre-wrap' }}>
           {this.state.error && this.state.error.toString()}
           <br />
           {this.state.errorInfo.componentStack}
         </details>
       </div>
     );
   }
   // Normally, just render children
   return this.props.children;
 }  
}class BuggyCounter extends React.Component {
 constructor(props) {
   super(props);
   this.state = { counter: 0 };
   this.handleClick = this.handleClick.bind(this);
 }
 
 handleClick() {
   this.setState(({counter}) => ({
     counter: counter + 1
   }));
 }
 
 render() {
   if (this.state.counter === 5) {
     // Simulate a JS error
     throw new Error('I crashed!');
   }
   return <h1 onClick={this.handleClick}>{this.state.counter}</h1>;
 }
}function App() {
 return (
   <div>
     <p>
       <b>
         This is an example of error boundaries in React 16.
         <br /><br />
         Click on the numbers to increase the counters.
         <br />
         The counter is programmed to throw when it reaches 5. This simulates a JavaScript error in a component.
       </b>
     </p>
     <hr />
     <ErrorBoundary>
       <p>These two counters are inside the same error boundary. If one crashes, the error boundary will replace both of them.</p>
       <BuggyCounter />
     </ErrorBoundary>
     <hr />
   </div>
 );
}ReactDOM.render(
 <App />,
 document.getElementById('root')
);

支持自定义DOM属性

  • 之前,React会忽略未知的DOM属性,自定义属性只能通过data-*形式添加。如果使用React无法识别的属性编写JSX,则React将跳过它。现在它会把这些属性直接传递给DOM(这个改动让React可以去掉属性白名单,从而减少了文件大小),不过有些写法仍然是无效的。如DOM传递的自定义属性是函数类型或event handler时,依然会被React忽略。
代码语言:javascript
复制
function MyComponent() {
   return(
       <div mycustomattribute="something" />
       <div tabIndex={0 / 0} />
       <div myData={this.state}></div>
       <div className={function() {}} />
       <div className={false} />
   )
}
代码语言:javascript
复制
// react 15
<div ></div>
<div ></div>
<div ></div>// react 16
<div mycustomattribute="something" tabindex="-1"></div>
<div mydata="[object Object]"></div>
<div tabindex="NaN"></div>

生命周期和setState

  • 现在,如果在一个生命周期的方法中使用ReactDOM.render() 和ReactDOM.unstable_renderIntoContainer() 则会返回null。为了应对这一点,开发者可以使用portals或者refs。
  • componentDidUpdate生命周期不会再返回prevContext 参数。
  • setState为空将不会再触发更新。开发者可以在更新函数中决定是否需要重新渲染。并且在render中直接调用setState总是可以导致更新。setState回调(第二个参数)在componentDidMount / componentDidUpdate后会立即触发,而不是在所有组件渲染完成之后。

打包

  • react/lib/* 和 react-dom/lib/*已经被移除了。即使在CommonJS环境中,React和ReactDOM都会被提前编译为一个单独的文件(“flat bundles”)。如果开发者之前依赖于非文件记录的内部构建,而他们现在又失效了,请在此issue下告知我们您的具体情况,我们会尝试为您提出迁移方案。
  • react-with-addons.js构建也被移除了。所有兼容性扩展在npm上分开发布,也有单文件浏览版本供开发者参阅。
  • 在15.x版本中引入的deprecations现在从核心包中移除了。React.createClass 现在改为 create-react-class, React.PropTypes改为 prop-types, React.DOM 改为 react-dom-factories, react-addons-test-utils 改为 react-dom/test-utils, and shallow renderer 改为 react-test-renderer/shallow.参见15.5.0 和15.6.0的博客,那里有介绍如何迁移代码并自动化codemods。
  • 单文件浏览构建的名称和路径发生了变化,为的是突出开发和发布的不同。比如:react/dist/react.js → react/umd/react.development.js react/dist/react.min.js → react/umd/react.production.min.js react-dom/dist/react-dom.js → react-dom/umd/react-dom.development.js react-dom/dist/react-dom.min.js → react-dom/umd/react-dom.production.min.

基于 MIT 协议

现在 React 15.6.2 和 React 16 都是基于 MIT 协议了。

好啦,以上就是React V.16 的更新内容,完整更新日志请查看React v16.0。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-12-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云前端 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 支持render返回数组和字符串
  • 全新的内部架构
  • 新增API:ReactDOM.createPortal
  • 更好的错误处理机制
  • 支持自定义DOM属性
  • 生命周期和setState
  • 打包
  • 基于 MIT 协议
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档