如何實現 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 發布文章使用:只允許注冊用戶才可以訪問!