React特性Hooks,Suspense,lazy,memo解析

news/2024/7/24 6:46:45 标签: React, Hooks, lazy, suspense, memo

1.memo

其实react.memo的实现很简单,就几行代码。

export default function memo<Props>(
  type: React$ElementType,
  compare?: (oldProps: Props, newProps: Props) => boolean,
) {
  if (__DEV__) {
    if (!isValidElementType(type)) {
      warningWithoutStack(
        false,
        'memo: The first argument must be a component. Instead ' +
          'received: %s',
        type === null ? 'null' : typeof type,
      );
    }
  }
  return {
    $$typeof: REACT_MEMO_TYPE,
    type,
    compare: compare === undefined ? null : compare,
  };
}

可以看到,最终返回的是一个对象,这个对象带有一些标志属性,在react Fiber的过程中会做相应的处理。

ReactFiberBeginWork.js 7中可以看到:

if (updateExpirationTime < renderExpirationTime) {
    // This will be the props with resolved defaultProps,
    // unlike current.memoizedProps which will be the unresolved ones.
    const prevProps = currentChild.memoizedProps;
    // 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,
      );
    }
  }

根据传入的compare函数比较prevPropsnextProps,最终决定生成对象,并影响渲染效果。

其实在这之前,早已经有一个生命周期函数实现了相同的功能。他就是shouldComponentUpdate

之所以再增加这个memo,也是react团队一直在秉承的信念。那就是让一切变得更加函数式

通过一个例子来看看memo如何使用。

先创建一个简单组件SubComponent

const SubComponent = props => 
  <>
    i am {props.name}. hi~
  </>

调用React.memo创建memo组件

const Memo = React.memo(SubComponent, (prevProps, nextProps) => 
  prevProps.name === nextProps.name
);

在页面上调用memo

<div className="App">
  <Memo name={name} />
</div>

memo接收两个参数,一个是组件,一个是函数。这个函数就是定义了memo需不需要render的钩子。

比较前一次的props跟当前props,返回true表示不需要render。

也就是传给Memo的name不变时,不会触发SubComponent的render函数。

当前页面上的SubComponent还是之前的,并没有重新渲染。这也是为啥叫memo的原因吧。

2.lazy and suspense

React.lazy 用于做Code-Splitting,代码拆分。类似于按需加载,渲染的时候才加载代码。

用法如下:

import React, {lazy} from 'react';
const OtherComponent = lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <OtherComponent />
    </div>
  );
}

lazy(() => import('./OtherComponent'))使用es6的import()返回一个promise,类似于:

lazy(() => new Promise(resolve =>
  setTimeout(() =>
    resolve(
      // 模拟ES Module
      {
        // 模拟export default 
        default: function render() {
          return <div>Other Component</div>
        }
      }
    ),
    3000
  )
));

React.lazy的提出是一种更优雅的条件渲染解决方案。

之所以说他更优雅,是因为他将条件渲染的优化提升到了框架层。

这里我们引出suspense

当我们组件未渲染完成,需要loading时,可以这么写:

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <OtherComponent />
      </Suspense>
    </div>
  );
}

在我们的业务场景中,OtherComponent可以代表多个条件渲染组件,我们全部加载完成才取消loding。

只要promise没执行到resolve,suspense都会返回fallback中的loading。

代码简洁,loading可提升至祖先组件,易聚合。相当优雅的解决了条件渲染。

关于suspense的异步渲染原理有篇文章写的很好,感兴趣的在文末查看。

3.hooks(重点介绍useEffect)

hooks提出有一段时间了,dan也一直在推广,并且表示很快加入react正式版本。

关于一些介绍,直接看官网会更好。

hooks常用api有:useState、useEffect、useContext、useReducer、useRef等。

主要操作一下useEffect,用处很大。举一反三。

all is function,没了component,自然也没了各种生命周期函数,此时useEffect登场。

下面通过一个组件实例来说明。

影像组件,功能有:前端加水印、实现拖拽。

大致实现如下:

class ImageModal extends Component {
  constructor(props) {
    ...
  }

  componentDidMount() {
    // 画水印、注册拖拽事件逻辑
    // 以及其他的image处理相关逻辑
  }

  componentDidUpdate(nextProps, prevProps) {
    if (nextProps.cur !== prevProps.cur) {
      // 切换时重置状态(比如 旋转角度、大小等)逻辑
      // image特有逻辑
    }
  }

  render() {
    return <>
      ...
      <img ... />
    </img>
  }
}

ImageModal负责渲染图片modal,现在有另一个modal用来渲染html模板。

命名为HtmlModal,HtmlModal接受后端返回的html,经过处理后内嵌在网页中。

同样要求加水印、拖拽的功能等。

也就是image跟html有部分逻辑相同有部分不相同。

基于这个考虑,再写一个组件。

同理实现如下:

class HtmlModal extends Component {
  constructor(props) {
    ...
  }

  componentDidMount() {
    // 画水印、注册拖拽事件逻辑
    // 以及其他的html处理相关逻辑
  }

  componentDidUpdate(nextProps, prevProps) {
    if (nextProps.cur !== prevProps.cur) {
      // 切换时重置状态(比如 旋转角度、大小等)逻辑
      // html特有逻辑
    }
  }

  render() {
    return <>
      ...
      <div dangerouslySetInnerHTML={{ __html: ... }}></div>
    </img>
  }
}

可以看到HtmlModalImageModalcomponentDidMountcomponentDidUpdate周期中有不少逻辑是相同的。

如果我们使用useEffect的话,可以怎么实现这个复用和分离呢?来看看。

export function useMoveEffect() {
  // 第二个参数传了固定值 [] 
  // 相当于 componentDidMount
  useEffect(() => {
    // 实现拖拽逻辑
  }, []);
}

export function useDrawMarkEffect(cur) {
  useEffect(() => {
    // 实现水印逻辑
  }, []);
}

export function useResetEffect(cur); {
  // 第二个参数传了固定值 [ cur ] 
  // 相当于 componentDidUpdate 比较 cur
  useEffect(() => {
    // 实现重置逻辑
  }, [ cur ]);
}

function useOtherImageEffect(...) {
  useEffect(() => {
    // 实现image特有逻辑
  }, [ ... ]);
}

function ImageModal (props) {
  // 细分 Effect,方便复用
  useMoveEffect();
  useDrawMarkEffect();
  useResetEffect(props.cur);
  ...

  useOtherImageEffect(...);

  return <>
    ...
    <img ... />
  </img>
  
}

ok,有了上面的梳理和useEffect重构,我们来编写HtmlModal:

import { useMoveEffect, useDrawMarkEffect, useResetEffect } from './imageModal'

function useOtherHtmlEffect(...) {
  useEffect(() => {
    // 实现html特有逻辑
  }, [ ... ]);
}

function HtmlModal (props) {
  // 细分 Effect,方便复用
  useMoveEffect();
  useDrawMarkEffect();
  useResetEffect(props.cur);
  ...

  useOtherHtmlEffect(...);

  return <>
    ...
    <img ... />
  </img>
  
}

以上,实现了生命周期中重复逻辑的复用。以后无论新增什么modal,都可以复用逻辑,摆脱了 ctr c/ ctr v

从而组件变得更小、代码变得简洁,提升编程体验。


http://www.niftyadmin.cn/n/719381.html

相关文章

extjs6中grid里放置图片

1.加黑体的是实现代码&#xff0c;在view中操作 /*** Created by Wwei on 2017/7/1.*/ Ext.define(Admin.view.userpanoram.UserPanoram, {extend: Ext.Panel,xtype: userpanoram,title: 我的工作室,requires: [Admin.view.userpanoram.UserPanoramController,Ext.button.Butto…

白话经典算法系列之五 归并排序的实现

归并排序是建立在归并操作上的一种有效的排序算法。该算法是採用分治法&#xff08;Divide and Conquer&#xff09;的一个很典型的应用。首先考虑下怎样将将二个有序数列合并。这个很easy&#xff0c;仅仅要从比較二个数列的第一个数&#xff0c;谁小就先取谁&#xff0c;取了…

JS实现的随机乱撞的彩色圆球特效代码

<!doctype html> <html lang"en"> <head><meta charset"UTF-8"><title>HTML5学习第5天[乱撞的球]</title><style>body{font-family: 微软雅黑; }body,h1{margin:0;}canvas{display:block;margin-left: auto;mar…

Python入门系列——第5篇

输入和格式说明符 在前面&#xff0c;我们所写过的所有程序中&#xff0c;没有出现过输入。现在&#xff0c;我们来学习一下输入函数。好了&#xff0c;具体请看代码。#python3 print("Hello") your_answer input() print("How old are you?") your_age …

来自MSDN的细菌觅食优化算法

http://msdn.microsoft.com/zh-cn/magazine/hh882453.aspx

React 性能优化技巧(纯干货)

本文篇幅较长&#xff0c;将从 编译阶段 -> 路由阶段 -> 渲染阶段 -> 细节优化 -> 状态管理 -> 海量数据源&#xff0c;长列表渲染 方向分别加以探讨。 一 不能输在起跑线上&#xff0c;优化babel配置,webpack配置为项 1 真实项目中痛点 当我们用create-rea…

Python基础-生成器

列表生成式&#xff1a;[f(x) for i in range(k)]&#xff1a;其中k是常数,f(x)是一个关于i的常数使用函数实现生成器&#xff1a;def fib(max):n,a,b 0,0,1while n<max:yield b #关键 yield具有保持值&#xff0c;并通过生成器的__next__()方法返回a,b b,abn 1return…

kbone-多端统一开发工具(小程序)

kbone 是一个致力于微信小程序和 Web 端同构的解决方案。 简介 微信小程序的底层模型和 Web 端不同&#xff0c;我们想直接把 Web 端的代码挪到小程序环境内执行是不可能的。kbone 的诞生就是为了解决这个问题&#xff0c;它实现了一个适配器&#xff0c;在适配层里模拟出了浏…