《手写mini React》Suspense 和 lazy

232 阅读1分钟

当涉及到手写 React 的 Suspenselazy 功能时,你通常需要理解这两个概念是如何协同工作的。Suspense 是一种用于处理异步加载的组件的加载状态的机制,而 lazy 则是一种使组件能够被按需加载的工具。

下面是一个简单的示例,展示如何手动实现类似于 Suspenselazy 的功能:

// 自定义的实现 Suspense 组件
class CustomSuspense extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isPending: true, // 是否处于等待状态
      error: null,    // 异步加载时的错误
    };
  }

  componentDidMount() {
    // 当组件加载完成后,开始执行异步加载操作
    this.props.promise
      .then(() => {
        // 异步加载成功,设置 isPending 为 false
        this.setState({ isPending: false });
      })
      .catch(error => {
        // 捕获异步加载的错误,设置 isPending 为 false,并存储错误信息
        this.setState({ isPending: false, error });
      });
  }

  render() {
    if (this.state.isPending) {
      // 如果正在等待加载完成
      if (this.props.fallback) {
        // 如果传递了自定义的 fallback 组件,则显示它
        return this.props.fallback;
      }
      return "Loading..."; // 默认加载提示
    } else if (this.state.error) {
      // 如果加载过程中出现了错误
      return `Error: ${this.state.error.message}`; // 显示错误消息
    } else {
      // 如果加载成功并且没有错误,渲染子组件
      return this.props.children; // 加载完成后渲染子组件
    }
  }
}

// 自定义的实现 lazy 函数
function customLazy(importFunction) {
  let component;
  let error;

  const promise = importFunction()
    .then(module => {
      component = module.default || module; // 获取导入的组件
    })
    .catch(err => {
      error = err; // 捕获导入错误
    });

  return props => {
    if (error) {
      throw error; // 抛出导入错误
    }
    if (!component) {
      throw promise; // 抛出未加载完成的 promise
    }
    return React.createElement(component, props); // 创建组件
  };
}

// 使用示例
const LazyComponent = customLazy(() => import("./LazyComponent"));

function App() {
  return (
    <div>
      <h1>Hello Suspense and Lazy</h1>
      <CustomSuspense promise={LazyComponent}>
        <LazyComponent />
      </CustomSuspense>
    </div>
  );
}

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

在这个示例中,手动实现了一个简化版的 CustomSuspensecustomLazy,以展示它们的基本工作原理。实际的 React 实现更加复杂且健壮,但这个例子可以帮助你理解这些概念的核心思想。在现实应用中,你通常会使用 React 的内置 Suspenselazy 来处理异步加载和懒加载组件。

上面的示例是使用类组件实现的。以下是相同功能的代码,但使用函数组件和 React Hooks 进行实现:

import React, { useState, useEffect } from 'react';

// 自定义的实现 Suspense 组件
function CustomSuspense({ promise, fallback, children }) {
  const [isPending, setIsPending] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    promise
      .then(() => {
        setIsPending(false);
      })
      .catch(err => {
        setIsPending(false);
        setError(err);
      });
  }, [promise]);

  if (isPending) {
    if (fallback) {
      return fallback;
    }
    return "Loading..."; // 默认加载提示
  } else if (error) {
    return `Error: ${error.message}`; // 显示错误消息
  } else {
    return children; // 加载完成后渲染子组件
  }
}

// 自定义的实现 lazy 函数
function customLazy(importFunction) {
  let component;
  let error;

  const promise = importFunction()
    .then(module => {
      component = module.default || module; // 获取导入的组件
    })
    .catch(err => {
      error = err; // 捕获导入错误
    });

  return props => {
    if (error) {
      throw error; // 抛出导入错误
    }
    if (!component) {
      throw promise; // 抛出未加载完成的 promise
    }
    return React.createElement(component, props); // 创建组件
  };
}

// 使用示例
const LazyComponent = customLazy(() => import("./LazyComponent"));

function App() {
  return (
    <div>
      <h1>Hello Suspense and Lazy</h1>
      <CustomSuspense promise={LazyComponent}>
        <LazyComponent />
      </CustomSuspense>
    </div>
  );
}

export default App;

这个版本的代码使用了函数组件和 useStateuseEffect 等 React Hooks 来实现相同的功能。函数组件加上 Hooks 是一种更现代、更简洁的写法,能够更好地处理组件的状态和副作用。希望这个函数组件版本的示例对你有所帮助!