严格意义来说,这种方式是按需加载,只加载用到的js文件。
此处主要介绍使用动态导入(通过模块中的内联函数调用来分离代码)的懒加载。这种动态代码拆分的方式是webpack提供并推荐选择的方式。其原理是使用符合 ECMAScript 提案 的 import()
语法 来实现动态导入。
import() 调用会在内部用到 promises。如果在旧版本浏览器中使用 import(),记得使用一个 polyfill 库(例如 es6-promise 或 promise-polyfill),来 shim Promise。
例如:通过 dynamic import(动态导入) lodash 来分离出一个 chunk:
function getComponent() {
return import(/* webpackChunkName: "lodash" */ 'lodash').then(({ default: _ }) => {
var element = document.createElement('div');
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
return element;
}).catch(error => 'An error occurred while loading the component');
}
getComponent().then(component => {
document.body.appendChild(component);
})
当调用 ES6 模块的 import()
方法(引入模块)时,必须指向模块的 .default
值,因为它才是 promise 被处理后返回的实际的 module
对象。原因是从 webpack v4 开始,在 import CommonJS 模块时,不会再将导入模块解析为 module.exports
的值,而是为 CommonJS 模块创建一个 artificial namespace object(人工命名空间对象),关于其背后原因的更多信息,请阅读 webpack 4: import() 和 CommonJs。
在注释中使用了webpackChunkName。这样会将拆分出来的 bundle 命名为 lodash.bundle.js
,而不是 [id].bundle.js
。
由于 import()
会返回一个 promise,因此它可以和 async
函数一起使用。但是,需要使用像 Babel 这样的预处理器和 Syntax Dynamic Import Babel Plugin。下面是通过async 函数简化的代码:
async function getComponent() {
var element = document.createElement('div');
const { default: _ } = await import(/* webpackChunkName: "lodash" */ 'lodash');
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
return element;
}
getComponent().then(component => {
document.body.appendChild(component);
});
React.lazy函数将动态引入(dynamic import)当作一个常规组件来渲染。
例如:动态引入OtherComponent组件:
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
当这个组件被渲染时,将自动加载包含OtherComponent的bundle。
使用React.lazy时,传入一个调用动态import()的函数。这个函数必须返回一个Promise,它解析一个包含React组件的一个默认导出(default export)的模块。
如果在MyComponent渲染时尚未加载包含OtherComponent的模块,我们必须在等待加载时显示一些后备内容—— 例如加载指示符。 这是使用Suspense组件完成的。
fallback 属性接受任何 React 元素。可以将Suspense组件放在懒加载组件上方的任何位置,甚至可以使用单个Suspense组件包裹多个懒加载的组件。
建议从路由开始处进行代码拆分。以下是使用React Router 和 React.lazy 从路由拆分代码的示例:
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import React, { Suspense, lazy } from 'react';
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>
);
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"));
关于懒加载,除了以上两种方法之外,还可以使用插件的方式或者直接使用原生的 js 方式来实现。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。