React组件惰性渲染浅析

2,918 阅读2分钟

什么是惰性渲染

惰性渲染其实是一种渲染优化方式之一,简单来说一个React组件,比如Drawer抽屉组件,开始是抽屉是不显示的,也就是说visible这个prop如果初始为false的话,那么在render里就return null,也就是说这个抽屉组件就不渲染,dom树中就没有对应的结构。然后当你点击一个按钮设置visible属性为true时,此时才渲染抽屉组件,然后你又点击按钮,置visible为false,此时组件隐藏,但是dom中仍然存在,这样一个过程的好处在于初始加载时可以大大减少渲染的负担,提交页面加载速度,下面就通过Antd的Drawer源码一探究竟

源码解析

其实仔细想想就可以发现我们只需一个变量记录该组件是不是第一次加载即可,如果是第一次加载就return null,否则就渲染组件,后续的显隐就通过display:none来切换即可,但是Drawer组件做法有区别

首先我们找到Drawer的代码, 查看index.ts,发现引用的是DrawerWrapper组件,直接跳到render方法查看,其实是一个Portal组件,可以猜到Portal组件是封装了React原生的Portal用法,必须用portal因为需要把Drawer挂在body上

return (
      <Portal
        visible={open}
        forceRender={$forceRender}
        getContainer={getContainer}
        wrapperClassName={wrapperClassName}
      >
        {({ visible, afterClose, ...rest }: IChildProps) => (
          // react 15,componentWillUnmount 时 Portal 返回 afterClose, visible.
          <Child
            {...props}
            {...rest}
            open={visible !== undefined ? visible : open}
            afterVisibleChange={afterClose !== undefined ? afterClose : props.afterVisibleChange}
            handler={handler}
            onClose={this.onClose}
            onHandleClick={this.onHandleClick}
          />
        )}
      </Portal>
    );

这里的visible属性的open就是通过用户传入Drawer组件的visible这个prop衍生出来的,控制Portal的显示隐藏,这里并没有惰性渲染的踪迹,那么继续深入Portal的代码,在开头发现Portal是rc-util里的,其实antd所有组件都是封装自rc开头的一系列组件

import Portal from 'rc-util/lib/PortalWrapper';

找到PortalWrapper组件的render,真相大白

render() {
    const { children, forceRender, visible } = this.props;
    let portal = null;
    const childProps = {
      getOpenCount: () => openCount,
      getContainer: this.getContainer,
      switchScrollingEffect: this.switchScrollingEffect,
    };
    
    // 省略部分代码
    
    if (forceRender || visible || this._component) {
      portal = (
        <Portal getContainer={this.getContainer} ref={this.savePortal}>
          {children(childProps)}
        </Portal>
      );
    }
    return portal;
  }

这里可以看到用了一个portal变量,初始visible为false的话,直接走最后的return portal,返回null,不渲染任何东西,然后visible一旦设为true,则走if分支,里面的关键在于ref引用 那个savePortal

savePortal = c => {
    // Warning: don't rename _component
    // https://github.com/react-component/util/pull/65#discussion_r352407916
    this._component = c;
  };

这里一旦渲染组件,则保存Portal的引用到_component这个私有变量中,然后visible再次为false,则还是走if分支,因为this._component不是null,所以达到了惰性渲染的目的