React教程(十三) : 性能优化 - Lazy, suspense & package size

由于篇幅原因,再开一个性能优化的帖子,讲一下怎么做lazy loading和code split
先介绍一个VS Code插件,可以查看import package的大小。 插件名:Import Cost

import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
const Home = lazy(() => import('./Home'));
const About = lazy(() => import('./About'));

const LazyDemo = () => {
    return (
        <Router>
            <Suspense fallback={<div>Loading...</div>}>
                <ul>
                    <li><Link to='/'>Home</Link></li>
                    <li><Link to='/about'>About</Link></li>
                </ul>
                <Switch>
                    <Route exact path="/" component={Home} />
                    <Route path="/about" component={About} />
                </Switch>
            </Suspense>
        </Router>
    )
}

export default LazyDemo;

以上代码示例说明了Lazy + Suspense是如何工作的。

代码示例见:https://github.com/992990831/modernization/tree/main/performance-tuning

这个示例非常简单,但有个小问题。 其中的Home、About组件会被webpack独立打包(code split),但由于其非常小,Loading效果根本看不出。 怎么才能设置最小loading时间呢?
答:Lazy中的import返回的是Promise对象,我们可以通过增加一个并行运行的Promise,为该Promise设置最小返回时间,来实现最小loading时间的效果。代码如下:

const Home = lazy(() => import('./Home'));
//const About = lazy(() => import('./About'));
const About = lazy(() => {
    return Promise.all([
      import("./Home"),
      new Promise(resolve => setTimeout(resolve, 3000)) //增加三秒延迟
    ])
    .then(([moduleExports]) => moduleExports);
  });

参考:https://stackoverflow.com/questions/54158994/react-suspense-lazy-delay

上述代码是通过路由(Router)实现延迟加载+代码拆分。 下面的示例演示了怎么在代码中实现lazy。


import React, { Suspense, lazy, useState } from 'react';

const data = [1, 2, 3, 4, 5, 6];

const DynamicLazyDemo = () => {
    const [lazyComponent, setLazyComponent] = useState(<></>);

    async function loadComponent(id: number) {
        // const LazyDemo = await lazy(() =>
        //     import(`./SubComponent${id}`)
        //         .catch(() => console.log('Error in importing'))
        // );
        const LazyDemo = await lazy( () => {
            return Promise.all([
                import(`./SubComponent${id}`),
                new Promise(resolve => setTimeout(resolve, 2000))
            ]).then(([moduleExports]) => moduleExports);
        })

        setLazyComponent(<LazyDemo key={id}></LazyDemo>);
    }

    return (
        <>
            {
                data.map(d => {
                    return (
                        <input key={d} type='button' value={`load comopent-${d}`} onClick={() => loadComponent(d)} style={{ marginLeft: '1em' }}></input>
                    );
                })
            }

            <Suspense fallback={<div>延迟2秒加载中</div>}>
                {
                    lazyComponent
                }
            </Suspense>

        </>
    )
}

export default DynamicLazyDemo;

上述代码的运行效果是:出现6个按钮,每个按钮click后,加载对应的子组件。

CRA框架会根据lazy代码中的import部分,把对应组件单独打包:

完整的代码示例:https://github.com/992990831/modernization/tree/main/performance-tuning

posted @ 2020-11-13 11:20  老胡Andy  阅读(286)  评论(0编辑  收藏  举报