基于webpack4+react 的js懒加载

以下介绍js懒加载的两种方式:
  • webpack4方式
  • React.lazy方式
webpack4方式

严格意义来说,这种方式是按需加载,只加载用到的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方式

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"));
React.lazy 和 Suspense还不支持在服务端的渲染。
React v16.6.0以上版本才支持React.lazy 和 Suspense。
总结

关于懒加载,除了以上两种方法之外,还可以使用插件的方式或者直接使用原生的 js 方式来实现。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券