欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React懶加載實(shí)現(xiàn)原理深入分析

 更新時(shí)間:2022年11月16日 17:05:24   作者:xiaofeng123aazz  
懶加載意思是不會(huì)預(yù)加載,而是需要使用某段代碼,某個(gè)組件或者某張圖片時(shí),才加載他們(延遲加載),這篇文章主要介紹了React懶加載實(shí)現(xiàn)原理

1.代碼分割

(1)為什么要進(jìn)行代碼分割?

現(xiàn)在前端項(xiàng)目基本都采用打包技術(shù),比如 Webpack,JS邏輯代碼打包后會(huì)產(chǎn)生一個(gè) bundle.js 文件,而隨著我們引用的第三方庫越來越多或業(yè)務(wù)邏輯代碼越來越復(fù)雜,相應(yīng)打包好的 bundle.js 文件體積就會(huì)越來越大,因?yàn)樾枰日?qǐng)求加載資源之后,才會(huì)渲染頁面,這就會(huì)嚴(yán)重影響到頁面的首屏加載。

而為了解決這樣的問題,避免大體積的代碼包,我們則可以通過技術(shù)手段對(duì)代碼包進(jìn)行分割,能夠創(chuàng)建多個(gè)包并在運(yùn)行時(shí)動(dòng)態(tài)地加載?,F(xiàn)在像 Webpack、 Browserify等打包器都支持代碼分割技術(shù)。

(2)什么時(shí)候應(yīng)該考慮進(jìn)行代碼分割?

這里舉一個(gè)平時(shí)開發(fā)中可能會(huì)遇到的場(chǎng)景,比如某個(gè)體積相對(duì)比較大的第三方庫或插件(比如JS版的PDF預(yù)覽庫)只在單頁應(yīng)用(SPA)的某一個(gè)不是首頁的頁面使用了,這種情況就可以考慮代碼分割,增加首屏的加載速度。

2.React的懶加載

示例代碼:

import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>        <OtherComponent />
      </Suspense>
    </div>
  );
}

如上代碼中,通過 import()、React.lazySuspense 共同一起實(shí)現(xiàn)了 React 的懶加載,也就是我們常說了運(yùn)行時(shí)動(dòng)態(tài)加載,即 OtherComponent 組件文件被拆分打包為一個(gè)新的包(bundle)文件,并且只會(huì)在 OtherComponent 組件渲染時(shí),才會(huì)被下載到本地。

那么上述中的代碼拆分以及動(dòng)態(tài)加載究竟是如何實(shí)現(xiàn)的呢?讓我們來一起探究其原理是怎樣的。

import() 原理

import() 函數(shù)是由TS39提出的一種動(dòng)態(tài)加載模塊的規(guī)范實(shí)現(xiàn),其返回是一個(gè) promise。在瀏覽器宿主環(huán)境中一個(gè)import()的參考實(shí)現(xiàn)如下:

function import(url) {
  return new Promise((resolve, reject) => {
    const script = document.createElement("script");
    const tempGlobal = "__tempModuleLoadingVariable" + Math.random().toString(32).substring(2);
    script.type = "module";
    script.textContent = `import * as m from "${url}"; window.${tempGlobal} = m;`;
    script.onload = () => {
      resolve(window[tempGlobal]);
      delete window[tempGlobal];
      script.remove();
    };
    script.onerror = () => {
      reject(new Error("Failed to load module script with URL " + url));
      delete window[tempGlobal];
      script.remove();
    };
    document.documentElement.appendChild(script);
  });
}

當(dāng) Webpack 解析到該import()語法時(shí),會(huì)自動(dòng)進(jìn)行代碼分割。

React.lazy 原理

以下 React 源碼基于 16.8.0 版本

React.lazy 的源碼實(shí)現(xiàn)如下:

export function lazy<T, R>(ctor: () => Thenable<T, R>): LazyComponent<T> {
  let lazyType = {
    ?typeof: REACT_LAZY_TYPE,
    _ctor: ctor,
    // React uses these fields to store the result.
    _status: -1,
    _result: null,
  };
  return lazyType;
}

可以看到其返回了一個(gè) LazyComponent 對(duì)象。

而對(duì)于 LazyComponent 對(duì)象的解析:

...
case LazyComponent: {
  const elementType = workInProgress.elementType;
  return mountLazyComponent(
    current,
    workInProgress,
    elementType,
    updateExpirationTime,
    renderExpirationTime,
  );
}
...
...
case LazyComponent: {
  const elementType = workInProgress.elementType;
  return mountLazyComponent(
    current,
    workInProgress,
    elementType,
    updateExpirationTime,
    renderExpirationTime,
  );
}
...
// Pending = 0, Resolved = 1, Rejected = 2
export function readLazyComponentType<T>(lazyComponent: LazyComponent<T>): T {
  const status = lazyComponent._status;
  const result = lazyComponent._result;
  switch (status) {
    case Resolved: {
      const Component: T = result;
      return Component;
    }
    case Rejected: {
      const error: mixed = result;
      throw error;
    }
    case Pending: {
      const thenable: Thenable<T, mixed> = result;
      throw thenable;
    }
    default: { // lazyComponent 首次被渲染
      lazyComponent._status = Pending;
      const ctor = lazyComponent._ctor;
      const thenable = ctor();
      thenable.then(
        moduleObject => {
          if (lazyComponent._status === Pending) {
            const defaultExport = moduleObject.default;
            lazyComponent._status = Resolved;
            lazyComponent._result = defaultExport;
          }
        },
        error => {
          if (lazyComponent._status === Pending) {
            lazyComponent._status = Rejected;
            lazyComponent._result = error;
          }
        },
      );
      // Handle synchronous thenables.
      switch (lazyComponent._status) {
        case Resolved:
          return lazyComponent._result;
        case Rejected:
          throw lazyComponent._result;
      }
      lazyComponent._result = thenable;
      throw thenable;
    }
  }
}

參考React實(shí)戰(zhàn)視頻講解:進(jìn)入學(xué)習(xí)

注:如果 readLazyComponentType 函數(shù)多次處理同一個(gè) lazyComponent,則可能進(jìn)入Pending、Rejected等 case 中。

從上述代碼中可以看出,對(duì)于最初 React.lazy() 所返回的 LazyComponent 對(duì)象,其 _status 默認(rèn)是 -1,所以首次渲染時(shí),會(huì)進(jìn)入 readLazyComponentType 函數(shù)中的 default 的邏輯,這里才會(huì)真正異步執(zhí)行 import(url)操作,由于并未等待,隨后會(huì)檢查模塊是否 Resolved,如果已經(jīng)Resolved了(已經(jīng)加載完畢)則直接返回moduleObject.default(動(dòng)態(tài)加載的模塊的默認(rèn)導(dǎo)出),否則將通過 throw 將 thenable 拋出到上層。

為什么要 throw 它?這就要涉及到 Suspense 的工作原理,我們接著往下分析。

Suspense 原理

由于 React 捕獲異常并處理的代碼邏輯比較多,這里就不貼源碼,感興趣可以去看 throwException 中的邏輯,其中就包含了如何處理捕獲的異常。簡(jiǎn)單描述一下處理過程,React 捕獲到異常之后,會(huì)判斷異常是不是一個(gè) thenable,如果是則會(huì)找到 SuspenseComponent ,如果 thenable 處于 pending 狀態(tài),則會(huì)將其 children 都渲染成 fallback 的值,一旦 thenable 被 resolve 則 SuspenseComponent 的子組件會(huì)重新渲染一次。

為了便于理解,我們也可以用 componentDidCatch 實(shí)現(xiàn)一個(gè)自己的 Suspense 組件,如下:

class Suspense extends React.Component {
  state = {
    promise: null
  }
  componentDidCatch(err) {
    // 判斷 err 是否是 thenable
    if (err !== null && typeof err === 'object' && typeof err.then === 'function') {
      this.setState({ promise: err }, () => {
        err.then(() => {
          this.setState({
            promise: null
          })
        })
      })
    }
  }
  render() {
    const { fallback, children } = this.props
    const { promise } = this.state
    return <>{ promise ? fallback : children }</>
  }
}

小結(jié)

至此,我們分析完了 React 的懶加載原理。簡(jiǎn)單來說,React利用 React.lazyimport()實(shí)現(xiàn)了渲染時(shí)的動(dòng)態(tài)加載 ,并利用Suspense來處理異步加載資源時(shí)頁面應(yīng)該如何顯示的問題。

到此這篇關(guān)于React懶加載實(shí)現(xiàn)原理深入分析的文章就介紹到這了,更多相關(guān)React懶加載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解React自定義Hook

    詳解React自定義Hook

    在React項(xiàng)目中,我們經(jīng)常會(huì)使用到React自帶的幾個(gè)內(nèi)置Hooks,如 useState,useContext和useEffect。雖然在React中找不到這些 Hooks,但React提供了非常靈活的方式讓你為自己的需求來創(chuàng)建自己的自定義Hooks,想了解更多的,歡迎閱讀本文
    2023-04-04
  • 比ant更豐富Modal組件功能實(shí)現(xiàn)示例詳解

    比ant更豐富Modal組件功能實(shí)現(xiàn)示例詳解

    這篇文章主要為大家介紹了比ant更豐富Modal組件功能實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • React?高階組件與Render?Props優(yōu)缺點(diǎn)詳解

    React?高階組件與Render?Props優(yōu)缺點(diǎn)詳解

    這篇文章主要weidajai?介紹了React?高階組件與Render?Props優(yōu)缺點(diǎn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • React Hooks 實(shí)現(xiàn)和由來以及解決的問題詳解

    React Hooks 實(shí)現(xiàn)和由來以及解決的問題詳解

    這篇文章主要介紹了React Hooks 實(shí)現(xiàn)和由來以及解決的問題詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • react中事件處理與柯里化的實(shí)現(xiàn)

    react中事件處理與柯里化的實(shí)現(xiàn)

    本文主要介紹了react中事件處理與柯里化的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • React報(bào)錯(cuò)之組件不能作為JSX組件使用的解決方法

    React報(bào)錯(cuò)之組件不能作為JSX組件使用的解決方法

    本文主要介紹了React報(bào)錯(cuò)之組件不能作為JSX組件使用的解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • 利用React實(shí)現(xiàn)一個(gè)有點(diǎn)意思的電梯小程序

    利用React實(shí)現(xiàn)一個(gè)有點(diǎn)意思的電梯小程序

    這篇文章主要為大家詳解介紹了如何利用React實(shí)現(xiàn)一個(gè)有點(diǎn)意思的電梯小程序,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下
    2022-08-08
  • react中Hooks的理解和用法小結(jié)

    react中Hooks的理解和用法小結(jié)

    Hook是 React 16.8 的新增特性,它可以讓你在不編寫class的情況下使用state以及其他的React特性,這篇文章主要介紹了react中Hooks的理解和用法,需要的朋友可以參考下
    2023-05-05
  • 深入掌握 react的 setState的工作機(jī)制

    深入掌握 react的 setState的工作機(jī)制

    本篇文章主要介紹了深入掌握 react的 setState的工作機(jī)制,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-09-09
  • create-react-app全家桶router?mobx全局安裝配置

    create-react-app全家桶router?mobx全局安裝配置

    這篇文章主要為大家介紹了create-react-app全家桶router?mobx全局安裝配置,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06

最新評(píng)論