React 新版本特性说明

58 篇文章 0 订阅
9 篇文章 0 订阅

1.React 16版本以前渲染一个组件最外层有时要使用到一个无意义的<div>元素作为包裹元素,否则会报错;React 16.2版本以后,新增了react.fragment API,不需要外层有包裹元素时,可以使用<></>或者<Fragment></Fragment>(需要导入import React, { Fragment } from 'react'

jsx
  1. 注释
    // 注释一,推荐
    {/* xxx */}
    // 注释二
    {
    	// xxx
    }
    
  2. 语法
    1. label中的for -> htmlFor
    2. 类名class -> className
    
react vscode 插件
  1. simple react snippets代码补全插件
react 子组件对父组件的传值进行类型校验

PropTypes

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class Test extends Component {

}

Test.propTypes = {
	index: PropTypes.number,
	addr: PropTypes.string,
	addNum: PropTypes.func,
}

export default Test;
componentDidUpdate(preProps, preState, snapshot)
  1. componentDidUpdate(preProps, preState, snapshot)在更新后会立即调用,首次不会调用;第三个参数在组建中使用了getSnapshotBeforeUpdate(),则snapshot值为该函数的返回值,否则为undefined
  2. demo:
    import React, { Suspense } from 'react';
    import ReactDOM from 'react-dom';
    
    const LazyComponent = React.lazy(() => import('./components/Lazy'));
    
    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          greet: 'hello, React 16'
        }
        this.changeState = this.changeState.bind(this);
      }
      getSnapshotBeforeUpdate(prevProps, prevState) {
        return 1;
      }
      componentDidUpdate(preProps, preState, snapshot) {
        console.log('preProps: ', preProps);		// preProps: {}
        console.log('preState: ', preState);		// preState: {greet: "hello, React 16"} 
        console.log('snapshot: ', snapshot);		// 1
      }
      changeState() {
        this.setState({
          greet: 'Changed'
        })
      }
      render() {
        return (
          <Suspense fallback={<div>loading...</div>}>
              <LazyComponent greet={this.state.greet} />
              <button onClick={this.changeState}>change</button>
          </Suspense>
        )
      }
    }
    
    ReactDOM.render(<App/>, document.getElementById('root'))
    
    
React生命周期

在这里插入图片描述

shouldComponentUpdate(nextProps, nextState) {
	if (nextProps.input !== this.props.input) {
		return true;
	} else {
		return false;
	}
}

在这里插入图片描述

  1. 在React16版本中,以下方法被标记为不安全方法,应该避免使用:
    1. UNSAFE_componentWillMount()
    2. UNSAFE_componentWillUpdate()
    3. UNSAFE_componentWillReceiveProps()
  2. React16版本新增的生命周期函数:
    1. static getDerivedStateFromProps() —— 仅用于组件在props变化时更新state;
    2. getSnapshotBeforeUpdate()
    3. static getDerivedStateFromError()
    4. componentDidCatch()
  3. 挂载时生命周期函数调用顺序:
    1. constructor()
    2. static getDerivedStateFromProps()
    3. render()
    4. componentDidMount()
  4. 更新时生命周期函数调用顺序:
    1. static getDerivedStateFromProps()
    2. shouldComponentUpdate()
    3. render()
    4. getSnapShotBeforeUpdate()
    5. componentDidUpdate()
  5. 卸载时
    1. componentWillUnmount()
  6. 错误处理
    1. static getDerivedStateFromError()
    2. componentDidCatch()
  7. constructor()使用:
    不初始化state或者进行方法绑定时,不需要为组件实现构造函数;(不调用时默认会被加一个空的constructor
  8. super使用
    构造函数中的操作不应该具有副作用;
    super仅能用于以下两种方式:
    1. 函数调用super(),通常用于构造函数中;
    2. 访问父类原型链属性或方法:super.propsuper[prop]
      在这里插入图片描述
class Parent {
    constructor() {
        this.name = 'fn';
    }
    getName() {
        return this.name;
    }
}
Parent.prototype.className = 'Parent';

class Child extends Parent {
    constructor(props) {
        super(props);
    }
    getName() {
        return super.getName() + ' from child.'
    }
	getParent() {
		return super.className;
	}
}

let child = new Child();
console.log(child.getName());		// 'fn from child.'
console.log(child.getParent());		// 'Parent'
React.lazy与Suspense

React.lazy()提供了动态加载组件的能力;Suspense是react内置的组件,用于懒加载组件时显示一些站位信息或提示信息(功能类似于loading过渡文字或动画);

// Lazy.js
import React from 'react';

export default props => (
    <div>{ props.greet }</div>
)


// index.js
import React, { Suspense } from 'react';
import ReactDOM from 'react-dom';

const LazyComponent = React.lazy(() => import('./components/Lazy'));

const App = () => {
  return (
    <Suspense fallback={<div>loading...</div>}>
        <LazyComponent greet="hello, react16" />
    </Suspense>
  )
}

ReactDOM.render(<App/>, document.getElementById('root'))

在这里插入图片描述

Refs转发

refs转发允许某些组件接收ref,并允许其向下传递ref;根据官方文档,主要应用包括:

  1. 转发 refs 到 DOM 组件
  2. 在高阶组件中转发 refs
  3. 在 DevTools 中显示自定义名称
/**
* 转发 refs 到 DOM 组件
**/

// RefTrans.js
import React from 'react';

export default React.forwardRef((props, ref) => (
    <button className="fancy-button" ref={ref}>
        {props.children}
    </button>
))


// index.js
import React from 'react';
import FancyBtn from './components/RefTrans';

class App extends React.Component {
	constructor(props) {
		super(props);
		this.ref = React.createRef();
	}
	componentDidMount() {
		console.log('ref: ', this.ref.current)
	}
	render() {
		return (
			<FancyBtn ref={this.ref}>Ref转发</FancyBtn>
		)
	}
}

HOC

  1. 定义:HOC(High-Order Component)即高阶组件,实质是一个函数,参数和返回值均为组件;个人感觉HOC在某些方面和函数柯里化有点像,某种程度上可以用于组件定制化。
  2. 常见的高阶组件:react-redux中的connect是一个高阶函数,内部返回一个高阶组件;
  3. 用途:属性代理、反响继承、渲染劫持等
  4. demo:
    class Btn extends React.Component {
      render() {
        return <button style={{color: this.props.color}}>{this.props.desc}</button>
      }
    }
    
    const customizedBtn = (WrappedComponent, config) => 
      class CustomizedBtn extends React.Component {
        render() {
          let props = {
            ...this.props,
            color: config.color
          }
          return <WrappedComponent {...props} />
        }
      }
    
    // 仿react-redux中的connect函数
    const customizedBtn2 = (config) => (WrappedComponent) => 
      class CustomizedBtn extends React.Component {
        render() {
          let props = {
            ...this.props,
            color: config.color
          }
          return <WrappedComponent {...props} />
        }
      }
    
    const RedPrimaryBtn = customizedBtn(Btn, {color: 'red'});
    const BlackSheepBtn = customizedBtn2({color: 'blue'})(Btn)
    
    在这里插入图片描述
Portals
  1. 意义:Portals允许你将一个子组件渲染到父组件之外的DOM节点上;比如一些弹窗等;
  2. 用法:ReactDOM.createPortal(child, container)——child为需要渲染到外部的子元素(包括react元素、数组或fragments、字符串或数值、布尔类型或null);container为DOM元素;
  3. demo:
    // MyPortal.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    
    
    export default class extends React.Component {
        constructor(props) {
            super(props);
            this.el = document.createElement('div');
        }
        componentDidMount() {
            const portalRoot = document.getElementById('portal')
            portalRoot.appendChild(this.el);
        }
        render() {
            return ReactDOM.createPortal(
                this.props.children,
                this.el
            )
        }
    }
    
    // index.js
    render() {
    	<>
        	<div id="portal"></div>
        	<MyPortal>
          		<span>myportal</span>
        	</MyPortal>
    	</>
    }
    
easy mock网站模拟接口数据,在无后端支持时独立开发,无缝衔接
react-transition-group动画库

包含三个模块:Transition、CSSTransition、TransitionGroup

react hooks

从React 16.8版本开始,新增的React hooks可以让用户在不用创建react class的同时可以使用state等react属性;

React框架分层

  1. Virtual DOM层:构建虚拟DOM树,描述页面长什么样

  2. Reconciler层:负责调用组件生命周期方法,计算DOM diff等;

    Reconciliation:
    The algorithm React uses to diff one tree with another to determine which parts need to be changed

    翻译:Reconciliation是React一种用于计算两个dom tree之间差异并决定哪些部分需要更新的算法
    Reconciliation的核心是:

    • 不同类型的组件会生成不同的DOM Tree,React不会尝试对它们做diff,而是使用新的Dom Tree替换旧的;
    • 列表的diff依赖于key值,key的取值应该是稳定、唯一并可预测的;
  3. Renderer层:负责根据虚拟DOM树和diff patch渲染对应页面;

React Fiber

React Fiber是React 16提出的新概念,是React重构的核心,主要是对Reconciler层的重写(Stack Reconciler ⇒ Fiber Reconciler);

Fiber Reconciler包括两个阶段:render phasecommit phase

  1. render phase可以异步执行,调用的生命周期函数列表:

    	[UNSAFE_]componentWillMount (deprecated)
    	[UNSAFE_]componentWillReceiveProps (deprecated)
    	getDerivedStateFromProps
    	shouldComponentUpdate
    	[UNSAFE_]componentWillUpdate (deprecated)
    	render
    
  2. commit phase通常是同步执行(因为该步骤涉及到真实DOM的更新及UI展现),调用的生命周期函数:

    getSnapshotBeforeUpdate
    componentDidMount
    componentDidUpdate
    compoentWillUnmount
    
    

React 15及以前的版本,在渲染大型组件时,由于其Reconciler层和Renderer层的执行是连续不可间断的;如果遇到大型组件;组件的执行时间有时会非常长,可能会阻塞动画渲染、用户交互等高实时性的任务,出现掉帧等,影响了用户体验;

我们知道,页面的显示其实就是通过一系列数据渲染得到视图,实质就是页面渲染相关的函数调用;
UI = render(data)

React 15及以前的版本,Reconciler的实现是基于内置的调用栈的同步递归模型去遍历Dom Tree;每次更新需要栈中函数全部调用结束,即栈空为止;这个过程是不被打断的,一次可能执行很长时间,阻塞页面渲染,所以会导致掉帧等;

由于调用栈进行视图更新的过程是连续的,为了把React渲染任务切分为增量单元分段执行,React Fiber针对React组件对调用栈进行了部分重写;使其变为一个链表结构;

特点

React Fiber引入的目的是为了增强动画、布局等实时性要求高的任务的稳定性;

  1. 主要特点是增量渲染(incremental rendering)—— 即可以把渲染工作进行切片,并在多帧内的空闲时间完成渲染工作;
  2. 为不同的更新任务分配不同的优先级,根据优先级的高低优先执行;
  3. 任务的暂停与恢复;
  4. 重用已完成的任务;
  5. 终止不再需要的任务;
  6. 当新的任务进来时,旧的任务可以被暂停、终止或重用;如果新任务优先级更高,则挂起旧任务并优先执行新任务;
Fiber node structure

React Fiber的每个节点都是一个对象fiber,其数据结构表示为:

class Node {
    constructor(instance) {
        this.instance = instance;
        this.child = null;
        this.sibling = null;
        this.return = null;
    }
}

区别于React Fiber,fiber是一个组件输入/输出信息的对象,可以看作一个虚拟的stack frame;每个React元素都有一个对应的fiber,它包含以下属性:

  • typekey:用途与组件类似,

    Conceptually, the type is the function (as in v = f(d)) whose execution is being tracked by the stack frame.
    Along with the type, the key is used during reconciliation to determine whether the fiber can be reused.
    翻译: 通常,type是一个函数,该函数的执行用于追踪栈帧;key通常用于在reconciliation阶段判断该fiber是否可以重用;

  • childsibling:用于指向其他fiberschild通常是组件render方法的返回值;sibling是render方法返回的顶层元素(数组形式,此时child为数组首元素;Fiber支持render函数中返回多个元素)

    function Parent() {
      return [<Child1 />, <Child2 />]
    }
    // child是Child1, siblings是Child1和Child2
    
  • return:reference to the parent

  • output:每一个fiber最终都有output,但是output只在一些Host Components(DOM节点)中产生;这些输出最终给Renderer用于应用更新;

举例说明,定义一个组件ClickCounter

class ClickCounter extends React.Component {
    constructor(props) {
        super(props);
        this.state = {count: 0};
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
        this.setState((state) => {
            return {count: state.count + 1};
        });
    }


    render() {
        return [
            <button key="1" onClick={this.handleClick}>Update counter</button>,
            <span key="2">{this.state.count}</span>
        ]
    }
}

这里,ClickCounter组件对应的fiber node的数据结构:

{
    stateNode: new ClickCounter,
    type: ClickCounter,
    alternate: null,
    key: null,
    updateQueue: null,
    memoizedState: {count: 0},
    pendingProps: {},
    memoizedProps: {},
    tag: 1,
    effectTag: 0,
    nextEffect: null
}

spanDOM节点

{
    stateNode: new HTMLSpanElement,
    type: "span",
    alternate: null,
    key: "2",
    updateQueue: null,
    memoizedState: null,
    pendingProps: {children: 0},
    memoizedProps: {children: 0},
    tag: 5,
    effectTag: 0,
    nextEffect: null
}

React Fiber基于新的浏览器API—— requestIdleCallbackrequestAnimationFrame进行实现;
requestIdleCallback调度一个低优先级的函数在浏览器空闲时间执行;
requestAnimationFrame调度一个高优先级的函数在下一个动画帧中执行;


参考文献

  1. react-fiber-architecture
  2. Reconciliation
  3. react basic
  4. Design Principles
  5. fiber-reconciler
  6. The how and why on React’s usage of linked list in Fiber to walk the component’s tree
  7. React生命周期速查表
  8. React.Components
  9. Refs转发
  10. 可渲染的React元素
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Neil-

你们的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值