行者小高 發表於 2020-10-15 15:40:00

如何实现 React 模块动态导入

<h1 id="如何实现-react-模块动态导入">如何实现 React 模块动态导入</h1>
<blockquote>
<p>React 模块动态导入</p>
</blockquote>
<p><img src="https://img2020.cnblogs.com/blog/740516/202010/740516-20201015160702452-1553151693.png" alt="" loading="lazy"></p>
<h2 id="代码分割">代码分割</h2>
<blockquote>
<p>webpack &amp; code splitting</p>
</blockquote>
<p>https://reactjs.org/docs/code-splitting.html</p>
<p>https://zh-hans.reactjs.org/docs/code-splitting.html</p>
<blockquote>
<p>Code-Splitting 可以创建多个可在运行时动态加载的包</p>
</blockquote>
<p>https://webpack.js.org/guides/code-splitting/</p>
<p>https://rollupjs.org/guide/en/#code-splitting</p>
<p>https://github.com/browserify/factor-bundle</p>
<blockquote>
<p>虽然您并未减少应用程序中的全部代码量,但避免了加载用户可能永远不需要的代码,并减少了初始加载过程中所需的代码量。</p>
</blockquote>
<pre><code class="language-js">

</code></pre>
<p>https://create-react-app.dev/docs/code-splitting/</p>
<pre><code class="language-js">

</code></pre>
<p>https://nextjs.org/docs/advanced-features/dynamic-import</p>
<p>React.lazy and Suspense are not yet available for server-side rendering.</p>
<blockquote>
<p>code-splitting &amp; server-side rendering</p>
</blockquote>
<p>https://github.com/gregberge/loadable-components</p>
<p>https://loadable-components.com/docs/server-side-rendering/</p>
<h2 id="reactlazy">React.lazy</h2>
<p>React.lazy 函数让你可以可以像导入将常规组件一样的渲染一个动态导入。</p>
<pre><code class="language-js">import OtherComponent from './OtherComponent';

</code></pre>
<pre><code class="language-js">// React.lazy
const OtherComponent = React.lazy(() =&gt; import('./OtherComponent'));

</code></pre>
<p>首次呈现此组件时,它将自动加载包含OtherComponent的捆绑包。</p>
<p>React.lazy 采用了必须调用动态 import()的函数。<br>
这必须返回一个 Promise,该 Promise 解析为一个带有默认导出的模块,该模块包含一个 React组件。</p>
<p>然后,应该将懒惰的组件呈现在Suspense组件中,这使我们可以在等待懒惰的组件加载时显示一些后备内容(例如加载指示符)。</p>
<pre><code class="language-js">import React, { Suspense } from 'react';

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

function MyComponent() {
return (
    &lt;div&gt;
      &lt;Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
      &lt;OtherComponent /&gt;
      &lt;/Suspense&gt;
    &lt;/div&gt;
);
}

</code></pre>
<p>fallback prop支持在等待组件加载时接受要渲染的任何React元素</p>
<p>您可以将 Suspense 组件放置在 lazy 组件上方的任何位置</p>
<p>您甚至可以用一个 Suspense 组件包装多个惰性组件。</p>
<pre><code class="language-js">
import React, { Suspense } from 'react';

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

const AnotherComponent = React.lazy(() =&gt; import('./AnotherComponent'));

function MyComponent() {
return (
    &lt;div&gt;
      &lt;Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
      &lt;section&gt;
          &lt;OtherComponent /&gt;
          &lt;AnotherComponent /&gt;
      &lt;/section&gt;
      &lt;/Suspense&gt;
    &lt;/div&gt;
);
}

</code></pre>
<h2 id="error-boundaries">Error boundaries</h2>
<blockquote>
<p>错误边界</p>
</blockquote>
<p>如果另一个模块无法加载(例如,由于网络故障),它将触发错误</p>
<p>您可以处理这些错误,以显示良好的用户体验,并通过错误边界管理恢复</p>
<p>创建错误边界后,您可以在惰性组件上方的任何位置使用它来在出现网络错误时显示错误状态。</p>
<pre><code class="language-js">import React, { Suspense } from 'react';

import MyErrorBoundary from './MyErrorBoundary';

const OtherComponent = React.lazy(() =&gt; import('./OtherComponent'));
const AnotherComponent = React.lazy(() =&gt; import('./AnotherComponent'));

const MyComponent = () =&gt; (
&lt;div&gt;
    &lt;MyErrorBoundary&gt;
      &lt;Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
      &lt;section&gt;
          &lt;OtherComponent /&gt;
          &lt;AnotherComponent /&gt;
      &lt;/section&gt;
      &lt;/Suspense&gt;
    &lt;/MyErrorBoundary&gt;
&lt;/div&gt;
);

</code></pre>
<p>https://reactjs.org/docs/error-boundaries.html</p>
<pre><code class="language-js">

</code></pre>
<h2 id="route-based-code-splitting">Route-based code splitting</h2>
<p>基于路由的代码拆分</p>
<blockquote>
<p>React Router &amp; React.lazy</p>
</blockquote>
<p>确定在应用程序中的何处引入代码拆分可能有些棘手<br>
您要确保选择的位置能够平均拆分捆绑包,但不会破坏用户体验</p>
<p>路线是一个不错的起点<br>
网络上的大多数人习惯了页面过渡,需要花费一些时间来加载<br>
您还倾向于一次重新渲染整个页面,因此您的用户不太可能同时与页面上的其他元素进行交互</p>
<p>这是一个示例,说明如何使用带有 React.lazy 的 React Router 等库将基于路由的代码拆分为您的应用。</p>
<pre><code class="language-js">import React, {
Suspense,
lazy,
} from 'react';

import {
BrowserRouter as Router,
Route,
Switch,
} from 'react-router-dom';

const Home = lazy(() =&gt; import('./routes/Home'));
const About = lazy(() =&gt; import('./routes/About'));

const App = () =&gt; (
&lt;Router&gt;
    &lt;Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
      &lt;Switch&gt;
      &lt;Route exact path="/" component={Home}/&gt;
      &lt;Route path="/about" component={About}/&gt;
      &lt;/Switch&gt;
    &lt;/Suspense&gt;
&lt;/Router&gt;
);

</code></pre>
<p>https://reactjs.org/docs/code-splitting.html#route-based-code-splitting</p>
<blockquote>
<p>react-router</p>
</blockquote>
<p>https://reacttraining.com/react-router/</p>
<h2 id="named-exports">Named Exports</h2>
<blockquote>
<p>命名的导出</p>
</blockquote>
<p>React.lazy 当前仅支持默认导出</p>
<p>如果要导入的模块使用命名的导出,则可以创建一个中间模块,将其重新导出为默认模块</p>
<p>这样可以确保摇树不停,并且不会拉扯未使用的组件</p>
<pre><code class="language-js">
// ManyComponents.js
export const MyComponent = /* ... */;
export const MyUnusedComponent = /* ... */;
</code></pre>
<pre><code class="language-js">// MyComponent.js
// 中间模块, 导出为默认模块
export { MyComponent as default } from "./ManyComponents.js";

</code></pre>
<pre><code class="language-js">// MyApp.js
import React, { lazy } from 'react';
const MyComponent = lazy(() =&gt; import("./MyComponent.js"));
</code></pre>
<hr>
<h2 id="webpack">webpack</h2>
<p>https://webpack.js.org/guides/code-splitting/</p>
<pre><code class="language-js">
module.exports = {
entry: {
    main: './src/app.js',
},
output: {
    // `filename` provides a template for naming your bundles (remember to use ``)
    filename: '.bundle.js',
    // `chunkFilename` provides a template for naming code-split bundles (optional)
    chunkFilename: '.bundle.js',
    // `path` is the folder where Webpack will place your bundles
    path: './dist',
    // `publicPath` is where Webpack will load your bundles from (optional)
    publicPath: 'dist/'
}
};

</code></pre>
<p>https://gist.github.com/gaearon/ca6e803f5c604d37468b0091d9959269</p>
<h2 id="webpack--magic-comments">webpack &amp; magic-comments</h2>
<p>https://webpack.js.org/api/module-methods/#magic-comments</p>
<p>https://webpack.docschina.org/api/module-methods/#magic-comments</p>
<pre><code class="language-js">
// Single target
import(
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
/* webpackExports: ["default", "named"] */
'module'
);

// Multiple possible targets
import(
/* webpackInclude: /\.json$/ */
/* webpackExclude: /\.noimport\.json$/ */
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
/* webpackPrefetch: true */
/* webpackPreload: true */
`./locale/${language}`
);
import(/* webpackIgnore: true */ 'ignored-module.js');

</code></pre>
<h2 id="babel">babel</h2>
<p>https://babeljs.io/</p>
<p>https://classic.yarnpkg.com/en/package/@babel/plugin-syntax-dynamic-import</p>
<pre><code class="language-sh">$ yarn add -D @babel/plugin-syntax-dynamic-import

</code></pre>
<p>https://babeljs.io/docs/en/babel-plugin-syntax-dynamic-import</p>
<pre><code class="language-js">$ npm i -D @babel/plugin-syntax-dynamic-import
</code></pre>
<pre><code class="language-js">
{
"plugins": ["@babel/plugin-syntax-dynamic-import"]
}

</code></pre>
<pre><code class="language-js">
// webpack config
const config = {
entry: [
    "core-js/modules/es.promise",
    "core-js/modules/es.array.iterator",
    path.resolve(__dirname, "src/main.js"),
],
// ...
};
</code></pre>
<pre><code class="language-js">// or

// src/main.js
import "core-js/modules/es.promise";
import "core-js/modules/es.array.iterator";

// ...

</code></pre>
<p>https://babeljs.io/blog/2019/07/03/7.5.0#dynamic-import-9552httpsgithubcombabelbabelpull9552-and-10109httpsgithubcombabelbabelpull10109</p>
<p><img src="https://img2020.cnblogs.com/blog/740516/202010/740516-20201015163550129-2075102277.png" alt="" loading="lazy"></p>
<p>https://www.npmjs.com/package/babel-plugin-dynamic-import-node</p>
<p>https://github.com/airbnb/babel-plugin-dynamic-import-node</p>
<pre><code class="language-sh">$ yarn add -D babel-plugin-dynamic-import-node

</code></pre>
<p>.babelrc</p>
<pre><code class="language-js">
{
"plugins": ["dynamic-import-node"]
}

</code></pre>
<h2 id="dynamic-import">dynamic import</h2>
<p>https://webpack.js.org/guides/code-splitting/#dynamic-imports</p>
<pre><code class="language-js">import("./emoji-component").then(emoji =&gt; {
// 使用 promise
console.log(emoji));
});

</code></pre>
<h2 id="切换路由">切换路由</h2>
<p>react-router</p>
<h2 id="lazy-load">lazy-load</h2>
<p>延迟加载 / 懒加载</p>
<h2 id="code-splitting--dynamic-import">code splitting &amp; dynamic import</h2>
<blockquote>
<p>import()类似函数的形式将模块名称作为参数,并返回一个Promise,该Promise始终解析为模块的名称空间对象</p>
</blockquote>
<p>https://github.com/tc39/proposal-dynamic-import</p>
<p>http://2ality.com/2017/01/import-operator.html#loading-code-on-demand</p>
<p>https://create-react-app.dev/docs/code-splitting/</p>
<pre><code class="language-js">const moduleA = `ESM (code splitting &amp; dynamic import)`;

export { moduleA };

</code></pre>
<p>这将使 moduleA.js 及其所有唯一依赖项成为单独的块,仅在用户单击“加载”按钮后才加载</p>
<pre><code class="language-js">import React, { Component } from 'react';

class App extends Component {
handleClick = () =&gt; {
    import('./moduleA')
      .then(({ moduleA }) =&gt; {
      // Use moduleA
      })
      .catch(err =&gt; {
      // Handle failure
      });
};
render() {
    return (
      &lt;div&gt;
      &lt;button onClick={this.handleClick}&gt;Load&lt;/button&gt;
      &lt;/div&gt;
    );
}
}
export default App;

</code></pre>
<blockquote>
<p>async / await &amp; promise</p>
</blockquote>
<p>如果愿意,还可以将其与 async / await 语法一起使用</p>
<pre><code class="language-js">// async / await

async handleClick = () =&gt; {
   const moduleA = await import('./moduleA');

   moduleA.then(({ moduleA }) =&gt; {
      // Use moduleA
    })
    .catch(err =&gt; {
      // Handle failure
   });
};


</code></pre>
<blockquote>
<p>chunks</p>
</blockquote>
<p>https://create-react-app.dev/docs/production-build</p>
<h2 id="refs">refs</h2>
<hr>
<div>

</div>
<hr>
<blockquote style="display: flex; flex-flow: column; align-items: center; justify-content: center; text-align: center; border: none">
<h3><strong><span style="font-size: 16pt; color: rgba(0, 255, 0, 1)">©xgqfrms 2012-<span data-uid="copyright-aside">2020</span></span></strong>
<p><span style="font-size: 18pt; color: rgba(0, 255, 0, 1)"><strong>www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!</strong></span></p>
</h3></blockquote>
<hr>


</div>
<div id="MySignature" role="contentinfo">
    <div style="display: flex; flex-flow: column nowrap; align-items: center; justify-content: center;">
<p>本文首发于博客园,作者:xgqfrms,原文链接:https://www.cnblogs.com/xgqfrms/p/13820944.html</p>
<p style="color: red; font-size: 23px; margin-top: 5px; margin-botom: 5px;">未经授权禁止转载,违者必究!</P>
</div>
<hr/><br><br>
来源:https://www.cnblogs.com/xgqfrms/p/13820944.html
頁: [1]
查看完整版本: 如何实现 React 模块动态导入