王英英 發表於 2020-9-21 16:00:00

React 入门(7): 动态加载组件

<h1 id="import异步加载模块">import()异步加载模块</h1>
<p>在webpack中, 调用import()函数可以将依赖模块进行切割, 打包为非入口点文件, 这是通过Promise+ajax完成的. 请求路径是相对路径, 对于单页应用来说没有问题.<br>
非入口点文件的命名由webpack.config.output.chunkFilename(可以定义路径, 使用和变量)以及Magic Comment(定义变量)共同确定.</p>
<h1 id="reactlazy函数">React.lazy()函数</h1>
<p>React.lazy() 允许你定义一个动态加载的组件。这有助于缩减 bundle 的体积,并延迟加载在初次渲染时未用到的组件。</p>
<pre><code>// 这个组件是动态加载的
const SomeComponent = React.lazy(() =&gt; import('./SomeComponent'));
</code></pre>
<p>渲染 lazy 组件依赖该组件渲染树上层的 &lt;React.Suspense&gt; 组件。这是指定加载指示器(loading indicator)的方式。<br>
使用 React.lazy 的动态引入特性需要 JS 环境支持 Promise。在 IE11 及以下版本的浏览器中需要通过引入 polyfill 来使用该特性。</p>
<p>! 不支持服务端渲染。</p>
<h2 id="例子----lazy加载组件">例子 -- lazy加载组件</h2>
<pre><code>import { Component, lazy, Suspense } from 'react';
import css from './style.css';

/** 异步组件使用lazy()函数加载, 传递一个使用import()函数的Promise异步方法, 该方法最终返回import()函数的结果 */
const AsyncComponent = lazy(() =&gt; {
    return new Promise((resolve, reject) =&gt; {
      import('./AsyncComponent').then(AsyncComponent =&gt; {
            console.log('加载完毕, 延迟传送');
            setTimeout(() =&gt; {
                console.log('传送');
                resolve(AsyncComponent);
            }, 5000);
      });
    });
});

export default (
    &lt;div id={css.app}&gt;
      &lt;Suspense fallback={&lt;h1&gt;加载中&lt;/h1&gt;}&gt;
            &lt;AsyncComponent&gt;&lt;/AsyncComponent&gt;
      &lt;/Suspense&gt;
    &lt;/div&gt;
);
</code></pre>
<h1 id="suspense组件">Suspense组件</h1>
<p>React关注DOM和事件, 数据更新更是重要, 因此, 我们来看Suspense组件如何在异步操作与UI更新之间建起桥梁.</p>
<p>代码分割: https://zh-hans.reactjs.org/docs/code-splitting.html#reactlazy<br>
Suspense: https://zh-hans.reactjs.org/docs/concurrent-mode-suspense.html<br>
Suspense还用于异步数据的获取:<br>
官方示例: https://codesandbox.io/s/frosty-hermann-bztrp?file=/src/fakeApi.js</p>
<h1 id="抛出promise-使用throw关键字陷入react内核">抛出Promise: 使用throw关键字陷入React内核</h1>
<p>在执行异步操作的过程中, 我们只需要在Promise未完成状态时将该Promise抛出到React核心即可:</p>
<pre><code>function fetchName() {
    console.log('尝试联网获取用户名...');
    throw new Promise(()=&gt; {
      console.log('抛出一个永久pending状态的Promise');
    });
}

function FunctionComponent(props) {
    console.log('尝试获取用户名并渲染UI...');
    return &lt;h2&gt;用户名: {fetchName()}&lt;/h2&gt;
}

export default (
    &lt;div id={css.app}&gt;
      &lt;Suspense fallback={&lt;h1&gt;正在联网获取用户名...&lt;/h1&gt;}&gt;
            &lt;FunctionComponent/&gt;
      &lt;/Suspense&gt;
    &lt;/div&gt;
);
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/967043/202009/967043-20200928173837889-43298369.png" alt="" loading="lazy"></p>
<p>Promise的拒绝状态会导致组件立即重新渲染, 并可能不断重复:</p>
<pre><code>function fetchName() {
    console.log('尝试联网获取用户名...');
    throw new Promise((_, reject)=&gt; {
      console.log('抛出一个拒绝状态的Promise');
      reject();
    });
}
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/967043/202009/967043-20200928174823325-1401304940.png" alt="" loading="lazy"></p>
<p>抛出其它非Promise异常会被React重新抛出, 导致页面报错.<br>
如果一个抛出的Promise结束了成为success状态, 那么它之后应该返回相应的结果, 而不是再次抛出Promise, 因为success状态的Promise(这里可能是全部的Promise结束后再调用)会导致方法组件再次被调用以渲染元素.</p>
<pre><code>import { Component, lazy, Suspense, createElement, useRef } from 'react';
import css from './style.css';

// 单例
let fetchNameByInternet = () =&gt; new Promise(resolve =&gt; {
   console.log('网络请求开始了, 将于4秒后完成');
   setTimeout(() =&gt; resolve('develon'), 4000);
});

let resolved = false; // 标志网络请求是否已完成
let name = "Don't get the Name"; // 存储从网络获取的用户名

function fetchName() {
    console.log('尝试联网获取用户名...');
    if (resolved) {
      console.log(`获取到数据: "${name}" !`);
      return name;
    }
    throw fetchNameByInternet().then(network_name =&gt; {
      resolved = true;
      name = network_name;
    });
}

function FunctionComponent(props) {
    console.log('方法组件被调用, 尝试获取用户名并渲染UI...');
    let name = fetchName(); // 此处会抛出异常, 不可进行捕获, 从而就像CPU中断指令一样陷入React内核
    console.log('方法组件继续执行, 开始渲染元素'); // 整个方法结束, 只有当Promise成功之后, 才会再次调用该方法组件, 所以说这些异步操作是有顺序的
    return &lt;h2&gt;用户名: {name}&lt;/h2&gt;;
}

export default (
    &lt;div id={css.app}&gt;
      &lt;Suspense fallback={&lt;h1&gt;正在联网获取用户名...&lt;/h1&gt;}&gt;
            &lt;FunctionComponent/&gt;
      &lt;/Suspense&gt;
    &lt;/div&gt;
);
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/967043/202009/967043-20200930111905005-1340178106.png" alt="" loading="lazy"></p>
<h1 id="end">END</h1><br><br>
来源:https://www.cnblogs.com/develon/p/13706136.html
頁: [1]
查看完整版本: React 入门(7): 动态加载组件