<tbody id="86a2i"></tbody>


<dd id="86a2i"></dd>
<progress id="86a2i"><track id="86a2i"></track></progress>

<dd id="86a2i"></dd>
<em id="86a2i"><ruby id="86a2i"><u id="86a2i"></u></ruby></em>

    <dd id="86a2i"></dd>

    1.代碼分割

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

    現在前端項目基本都采用打包技術,比如 Webpack,JS邏輯代碼打包后會產生一個 bundle.js 文件,而隨著我們引用的第三方庫越來越多或業務邏輯代碼越來越復雜,相應打包好的 bundle.js 文件體積就會越來越大,因為需要先請求加載資源之后,才會渲染頁面,這就會嚴重影響到頁面的首屏加載。

    而為了解決這樣的問題,避免大體積的代碼包,我們則可以通過技術手段對代碼包進行分割,能夠創建多個包并在運行時動態地加載?,F在像 Webpack、 Browserify等打包器都支持代碼分割技術。

    (2)什么時候應該考慮進行代碼分割?

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

    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 共同一起實現了 React 的懶加載,也就是我們常說了運行時動態加載,即 OtherComponent 組件文件被拆分打包為一個新的包(bundle)文件,并且只會在 OtherComponent 組件渲染時,才會被下載到本地。

    那么上述中的代碼拆分以及動態加載究竟是如何實現的呢?讓我們來一起探究其原理是怎樣的。

    import() 原理

    import() 函數是由TS39提出的一種動態加載模塊的規范實現,其返回是一個 promise。在瀏覽器宿主環境中一個import()的參考實現如下:

    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);
      });
    }

    當 Webpack 解析到該import()語法時,會自動進行代碼分割。

    React.lazy 原理

    以下 React 源碼基于 16.8.0 版本

    React.lazy 的源碼實現如下:

    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;
    }

    可以看到其返回了一個 LazyComponent 對象。

    而對于 LazyComponent 對象的解析:

    ...
    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實戰視頻講解:進入學習

    注:如果 readLazyComponentType 函數多次處理同一個 lazyComponent,則可能進入Pending、Rejected等 case 中。

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

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

    Suspense 原理

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

    為了便于理解,我們也可以用 componentDidCatch 實現一個自己的 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 }</>
      }
    }

    小結

    至此,我們分析完了 React 的懶加載原理。簡單來說,React利用 React.lazyimport()實現了渲染時的動態加載 ,并利用Suspense來處理異步加載資源時頁面應該如何顯示的問題。

    原文地址:https://blog.csdn.net/xiaofeng123aazz/article/details/127863683

    相關文章:

    免费一级a片在线播放视频|亚洲娇小性XXXX色|曰本无码毛片道毛片视频清|亚洲一级a片视频免费观看
    <tbody id="86a2i"></tbody>

    
    
    <dd id="86a2i"></dd>
    <progress id="86a2i"><track id="86a2i"></track></progress>

    <dd id="86a2i"></dd>
    <em id="86a2i"><ruby id="86a2i"><u id="86a2i"></u></ruby></em>

      <dd id="86a2i"></dd>