一.概览
新增了几个方便的特性:

React.memo:函数式组件也有“shouldComponentUpdate”生命周期了

React.lazy:配合Suspense特性轻松优雅地完成代码拆分(Code-Splitting)

static contextType:class组件可以更容易地访问单一Context

static getDerivedStateFromError():***友好的“componentDidCatch”

其中最重要的是Suspense特性,在之前的React Async Rendering中提到过:

另外,将来会提供一个suspense(挂起)API,允许挂起视图渲染,等待异步操作完成,让loading场景更容易控制,具体见Sneak Peek: Beyond React 16演讲视频里的第2个Demo而现在(v16.6.0,发布于2018/10/23),就是大约8个月之后的“将来”

二.React.memo

const MyComponent = React.memo(function MyComponent(props) {  /* only rerenders if props change */});

还有个可选的compare参数:

function MyComponent(props) {  /* render using props */}function areEqual(prevProps, nextProps) {  /*  return true if passing nextProps to render would return  the same result as passing prevProps to render,  otherwise return false  */}export default React.memo(MyComponent, areEqual);

类似于PureComponent的高阶组件,包一层memo,就能让普通函数式组件拥有PureComponent的性能优势:

React.Component doesn’t implement shouldComponentUpdate(), but React.PureComponent implements it with a shallow prop and state comparison.

内部实现
实现上非常简单:

export default function memo<Props>(  type: React$ElementType,  compare?: (oldProps: Props, newProps: Props) => boolean,) {  return {    $$typeof: REACT_MEMO_TYPE,    type,    compare: compare === undefined ? null : compare,  };}

无非就是外挂式shouldComponentUpdate生命周期,对比class组件:

// 普通class组件class MyClassComponent {  // 没有默认的shouldComponentUpdate,可以手动实现  shouldComponentUpdate(oldProps: Props, newProps: Props): boolean {    return true;  }}// 继承自PureComponent的组件相当于class MyPureComponent {  // 拥有默认shouldComponentUpdate,即shallowEqual  shouldComponentUpdate(oldProps: Props, newProps: Props): boolean {    return shallowEqual(oldProps, newProps);  }}// 函数式组件function render() {  // 函数式组件,不支持shouldComponentUpdate}// Memo组件相当于const MyMemoComponent = {  type: function render() {    // 函数式组件,不支持shouldComponentUpdate  }  // 拥有默认的(挂在外面的)shouldComponentUpdate,即shallowEqual  compare: shallowEqual};

如此这般,就给函数式组件粘了个shouldComponentUpdate上去,接下来的事情猜也能猜到了:

// ref: react-16.6.3/react/packages/react-reconciler/src/ReactFiberBeginWork.jsfunction updateMemoComponent(  current: Fiber | null,  workInProgress: Fiber,  Component: any,  nextProps: any,  updateExpirationTime,  renderExpirationTime: ExpirationTime,): null | Fiber {    // Default to shallow comparison    let compare = Component.compare;    compare = compare !== null ? compare : shallowEqual;    if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) {      return bailoutOnAlreadyFinishedWork(        current,        workInProgress,        renderExpirationTime,      );    }  }}

所以,从实现上来看,React.memo()这个API与memo关系倒不大,实际意义是:函数式组件也有“shouldComponentUpdate”生命周期了

注意,compare默认是shallowEqual,所以React.memo第二个参数compare实际含义是shouldNotComponentUpdate,而不是我们所熟知的相反的那个。API设计上确实有些迷惑,非要引入一个相反的东西:

Unlike the shouldComponentUpdate() method on class components, this is the inverse from shouldComponentUpdate.

P.S.RFC定稿过程中第二个参数确实备受争议(equal, arePropsEqual, arePropsDifferent, renderOnDifference, isEqual, shouldUpdate...等10000个以内),具体见React.memo()

手动实现个memo?
话说回来,这样一个高阶组件其实不难实现:

function memo(render, shouldNotComponentUpdate = shallowEqual) {  let oldProps, rendered;  return function(newProps) {    if (!shouldNotComponentUpdate(oldProps, newProps)) {      rendered = render(newProps);      oldProps = newProps;    }    return rendered;  }}

手动实现的这个盗版与官方版本功能上等价(甚至性能也不相上下),所以又一个锦上添花的东西

三.React.lazy: Code-Splitting with Suspense
相当漂亮的特性,篇幅限制(此处删掉了276行),暂不展开

四.static contextType
v16.3推出了新Context API:

const ThemeContext = React.createContext('light');class ThemeProvider extends React.Component {  state = {theme: 'light'};  render() {    return (      <ThemeContext.Provider value={this.state.theme}>        {this.props.children}      </ThemeContext.Provider>    );  }}class ThemedButton extends React.Component {  render() {    return (      // 这一部分看起来很麻烦,读个context而已      <ThemeContext.Consumer>        {theme => <Button theme={theme} />}      </ThemeContext.Consumer>    );  }}

为了让class组件访问Context数据方便一些,新增了static contextType特性:

class ThemedButton extends React.Component {  static contextType = ThemeContext;  render() {    let theme = this.context;    return (      // 喧嚣停止了      <Button theme={theme} />    );  }}

其中contextType(注意,之前那个旧的多个s,叫contextTypes)只支持React.createContext()返回类型,翻新了旧Context API的this.context(变成单一值了,之前是对象)

用法上不那么变态了,但只支持访问单一Context值。要访问一堆Context值的话,只能用上面看起来很麻烦的那种方式:

// A component may consume multiple contextsfunction Content() {  return (    // 。。。。    <ThemeContext.Consumer>      {theme => (        <UserContext.Consumer>          {user => (            <ProfilePage user={user} theme={theme} />          )}        </UserContext.Consumer>      )}    </ThemeContext.Consumer>  );}

五.static getDerivedStateFromError()
static getDerivedStateFromError(error)
又一个错误处理API:

class ErrorBoundary extends React.Component {  constructor(props) {    super(props);    this.state = { hasError: false };  }  static getDerivedStateFromError(error) {    // Update state so the next render will show the fallback UI.    return { hasError: true };  }  render() {    if (this.state.hasError) {      // You can render any custom fallback UI      return <h1>Something went wrong.</h1>;    }    return this.props.children;   }}

用法与v16.0的componentDidCatch(error, info)非常相像:

class ErrorBoundary extends React.Component {  componentDidCatch(error, info) {    // Display fallback UI    this.setState({ hasError: true });    // You can also log the error to an error reporting service    logErrorToMyService(error, info);  }}

二者都会在子树渲染出错后触发,但触发时机上存在微妙的差异:

static getDerivedStateFromError:在render阶段触发,不允许含有副作用(否则多次执行会出问题)

componentDidCatch:在commit阶段触发,因此允许含有副作用(如logErrorToMyService)

前者的触发时机足够早,所以能够多做一些补救措施,比如避免null ref引发连锁错误

另一个区别是Did系列生命周期(如componentDidCatch)不支持***,而getDerivedStateFromError从设计上就考虑到了***(目前v16.6.3还不支持,但说了会支持)

目前这两个API在功能上是有重叠的,都可以在子树出错之后通过改变state来做UI降级,但后续会细分各自的职责:

static getDerivedStateFromError:专做UI降级

componentDidCatch:专做错误上报

六.过时API
又两个API要被打入冷宫:

ReactDOM.findDOMNode():性能原因以及设计上的问题,建议换用ref forwarding

旧Context API:性能及实现方面的原因,建议换用新Context API

P.S.暂时还能用,但将来版本会去掉,可以借助StrictMode完成迁移

七.总结
函数式组件也迎来了“shouldComponentUpdate”,还有漂亮的Code-Splitting支持,以及缓解Context Consumer繁琐之痛的补丁API,和职责清晰的UI层兜底方案

13种React组件
v16.6新增了几类组件(REACT_MEMO_TYPE、REACT_LAZY_TYPE、REACT_SUSPENSE_TYPE),细数一下,竟然有这么多了:

REACT_ELEMENT_TYPE:普通React组件类型,如<MyComponent />

REACT_PORTAL_TYPE:Protals组件,ReactDOM.createPortal()

REACT_FRAGMENT_TYPE:Fragment虚拟组件,<></>或<React.Fragment></React.Fragment>或[,]

REACT_STRICT_MODE_TYPE:带过时API检查的严格模式组件,<React.StrictMode>

REACT_PROFILER_TYPE:用来开启组件范围性能分析,见Profiler RFC,目前还是实验性API,<React.unstable_Profiler>稳定之后会变成<React.Profiler>

REACT_PROVIDER_TYPE:Context数据的生产者Context.Provider,<React.createContext(defaultValue).Provider>

REACT_CONTEXT_TYPE:Context数据的消费者Context.Consumer,<React.createContext(defaultValue).Consumer>

REACT_ASYNC_MODE_TYPE:开启异步特性的异步模式组件,过时了,换用REACT_CONCURRENT_MODE_TYPE

REACT_CONCURRENT_MODE_TYPE:用来开启异步特性,暂时还没放出来,处于Demo阶段,<React.unstable_ConcurrentMode>稳定之后会变成<React.ConcurrentMode>

REACT_FORWARD_REF_TYPE:向下传递Ref的组件,React.forwardRef()

REACT_SUSPENSE_TYPE:组件范围延迟渲染,<Suspense fallback={<MyLoadingComponent>}>

REACT_MEMO_TYPE:类似于PureComponent的高阶组件,React.memo()

REACT_LAZY_TYPE:动态引入的组件,React.lazy()

曾几何时,v15-只有1种REACT_ELEMENT_TYPE……

更多相关文章

  1. SpringMVC源码分析:一个request请求的完整流程和各组件介绍
  2. 几款代码高亮组件的体验,说不定你以后会用到
  3. 帆软报表自定义函数-取json数据
  4. 函数和递归
  5. java的getClass()函数
  6. 函数的学习
  7. java多线程(3)Thread构造函数解析
  8. JavaScript 测试教程–part 3:测试 props,挂载函数和快照测试[每日
  9. JavaScript测试教程–part 4:模拟 API 调用和模拟 React 组件交互

随机推荐

  1. 关于AndroidStudio中提示cannot resolve
  2. eclipse转用android studio——常用快捷
  3. Android多窗口分屏(原生方法)
  4. JS唤醒Android APP(包括在外部浏览器和We
  5. Android中如何有效的加载图片
  6. SmartRefreshLayout集成笔记,实现下拉刷新
  7. Android Camera进行拍照
  8. Android设置屏幕旋转后保存数据
  9. Android中的一些小知识点
  10. android 打开系统相册得到路径 上传图片