如何实现 React 模块动态导入
React 模块动态导入
代码分割
webpack & code splitting
https://reactjs.org/docs/code-splitting.html
https://zh-hans.reactjs.org/docs/code-splitting.html
Code-Splitting 可以创建多个可在运行时动态加载的包
https://webpack.js.org/guides/code-splitting/
https://rollupjs.org/guide/en/#code-splitting
https://github.com/browserify/factor-bundle
虽然您并未减少应用程序中的全部代码量,但避免了加载用户可能永远不需要的代码,并减少了初始加载过程中所需的代码量。
https://create-react-app.dev/docs/code-splitting/
https://nextjs.org/docs/advanced-features/dynamic-import
React.lazy and Suspense are not yet available for server-side rendering.
code-splitting & server-side rendering
https://github.com/gregberge/loadable-components
https://loadable-components.com/docs/server-side-rendering/
React.lazy
React.lazy 函数让你可以可以像导入将常规组件一样的渲染一个动态导入。
import OtherComponent from './OtherComponent';
// React.lazy
const OtherComponent = React.lazy(() => import('./OtherComponent'));
首次呈现此组件时,它将自动加载包含OtherComponent的捆绑包。
React.lazy 采用了必须调用动态 import()的函数。
这必须返回一个 Promise,该 Promise 解析为一个带有默认导出的模块,该模块包含一个 React组件。
然后,应该将懒惰的组件呈现在Suspense组件中,这使我们可以在等待懒惰的组件加载时显示一些后备内容(例如加载指示符)。
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
fallback prop 支持在等待组件加载时接受要渲染的任何React元素
您可以将 Suspense 组件放置在 lazy 组件上方的任何位置
您甚至可以用一个 Suspense 组件包装多个惰性组件。
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<section>
<OtherComponent />
<AnotherComponent />
</section>
</Suspense>
</div>
);
}
Error boundaries
错误边界
如果另一个模块无法加载(例如,由于网络故障),它将触发错误
您可以处理这些错误,以显示良好的用户体验,并通过错误边界管理恢复
创建错误边界后,您可以在惰性组件上方的任何位置使用它来在出现网络错误时显示错误状态。
import React, { Suspense } from 'react';
import MyErrorBoundary from './MyErrorBoundary';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
const MyComponent = () => (
<div>
<MyErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<section>
<OtherComponent />
<AnotherComponent />
</section>
</Suspense>
</MyErrorBoundary>
</div>
);
https://reactjs.org/docs/error-boundaries.html
Route-based code splitting
基于路由的代码拆分
React Router & React.lazy
确定在应用程序中的何处引入代码拆分可能有些棘手
您要确保选择的位置能够平均拆分捆绑包,但不会破坏用户体验
路线是一个不错的起点
网络上的大多数人习惯了页面过渡,需要花费一些时间来加载
您还倾向于一次重新渲染整个页面,因此您的用户不太可能同时与页面上的其他元素进行交互
这是一个示例,说明如何使用带有 React.lazy 的 React Router 等库将基于路由的代码拆分为您的应用。
import React, {
Suspense,
lazy,
} from 'react';
import {
BrowserRouter as Router,
Route,
Switch,
} from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
</Switch>
</Suspense>
</Router>
);
https://reactjs.org/docs/code-splitting.html#route-based-code-splitting
react-router
https://reacttraining.com/react-router/
Named Exports
命名的导出
React.lazy 当前仅支持默认导出
如果要导入的模块使用命名的导出,则可以创建一个中间模块,将其重新导出为默认模块
这样可以确保摇树不停,并且不会拉扯未使用的组件
// ManyComponents.js
export const MyComponent = /* ... */;
export const MyUnusedComponent = /* ... */;
// MyComponent.js
// 中间模块, 导出为默认模块
export { MyComponent as default } from "./ManyComponents.js";
// MyApp.js
import React, { lazy } from 'react';
const MyComponent = lazy(() => import("./MyComponent.js"));
webpack
https://webpack.js.org/guides/code-splitting/
module.exports = {
entry: {
main: './src/app.js',
},
output: {
// `filename` provides a template for naming your bundles (remember to use `[name]`)
filename: '[name].bundle.js',
// `chunkFilename` provides a template for naming code-split bundles (optional)
chunkFilename: '[name].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/'
}
};
https://gist.github.com/gaearon/ca6e803f5c604d37468b0091d9959269
webpack & magic-comments
https://webpack.js.org/api/module-methods/#magic-comments
https://webpack.docschina.org/api/module-methods/#magic-comments
// 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');
babel
https://classic.yarnpkg.com/en/package/@babel/plugin-syntax-dynamic-import
$ yarn add -D @babel/plugin-syntax-dynamic-import
https://babeljs.io/docs/en/babel-plugin-syntax-dynamic-import
$ npm i -D @babel/plugin-syntax-dynamic-import
{
"plugins": ["@babel/plugin-syntax-dynamic-import"]
}
// webpack config
const config = {
entry: [
"core-js/modules/es.promise",
"core-js/modules/es.array.iterator",
path.resolve(__dirname, "src/main.js"),
],
// ...
};
// or
// src/main.js
import "core-js/modules/es.promise";
import "core-js/modules/es.array.iterator";
// ...
https://www.npmjs.com/package/babel-plugin-dynamic-import-node
https://github.com/airbnb/babel-plugin-dynamic-import-node
$ yarn add -D babel-plugin-dynamic-import-node
.babelrc
{
"plugins": ["dynamic-import-node"]
}
dynamic import
https://webpack.js.org/guides/code-splitting/#dynamic-imports
import("./emoji-component").then(emoji => {
// 使用 promise
console.log(emoji));
});
切换路由
react-router
lazy-load
延迟加载 / 懒加载
code splitting & dynamic import
import()类似函数的形式将模块名称作为参数,并返回一个Promise,该Promise始终解析为模块的名称空间对象
https://github.com/tc39/proposal-dynamic-import
http://2ality.com/2017/01/import-operator.html#loading-code-on-demand
https://create-react-app.dev/docs/code-splitting/
const moduleA = `ESM (code splitting & dynamic import)`;
export { moduleA };
这将使 moduleA.js 及其所有唯一依赖项成为单独的块,仅在用户单击“加载”按钮后才加载
import React, { Component } from 'react';
class App extends Component {
handleClick = () => {
import('./moduleA')
.then(({ moduleA }) => {
// Use moduleA
})
.catch(err => {
// Handle failure
});
};
render() {
return (
<div>
<button onClick={this.handleClick}>Load</button>
</div>
);
}
}
export default App;
async / await & promise
如果愿意,还可以将其与 async / await 语法一起使用
// async / await
async handleClick = () => {
const moduleA = await import('./moduleA');
moduleA.then(({ moduleA }) => {
// Use moduleA
})
.catch(err => {
// Handle failure
});
};
chunks
https://create-react-app.dev/docs/production-build
refs
©xgqfrms 2012-2020
www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!