之前讲过,webpack4.0的optimization 下面的splitchunkplugin可以分割异步组件,从而减少过度的首页渲染问题。那么如何写一个异步组件呢?可以从下面几种方法入手:
1、借助es6的import().then,手写一个异步高阶组件

import React from 'react';

export default (getComponent) => {
    return class AsyncComponent extends React.Component {
        static Component = null;
        state = { Component: AsyncComponent.Component };

        componentWillMount() {
            if(!this.Component) {
                let rs = getComponent();
                rs.then(({default: Component}) => {
                    AsyncComponent.Component = Component;
                    this.setState({ Component })
                })
            }
        }

        render() {
            const { Component } = this.state;
            if(Component) {
                return <Component {...this.props} />
            }
            return null
        }
    }
}
;

页面中的使用方法如下:

import React, { useState } from 'react';
import Navigator from '../../layout/navigator/index.jsx';
import AsyncComponent from '../../utils/AsyncComponent.js';
import './index.css';

const mapCom = {
    tab1: () => import('./components/tab1.jsx'),
    tab2: () => import('./components/tab2.jsx'),
    tab3: () => import('./components/tab3.jsx')
}

const Home = function(props) {

    const [tab, setTab] = useState('tab1');
    let TabContent = AsyncComponent(mapCom[tab]);
    return (
        <div>
            <div 
                style={{
                    display: 'flex'
                }}
                onClick={e => {
                    setTab(e.target.className);
                }}
            >
                <div className={tab === "tab1" ? "tab1 active" : "tab1"}>
                    tab1
                </div>
                <div className={tab === "tab2" ? "tab2 active" : "tab2"}>
                    tab2
                </div>
                <div className={tab === "tab3" ? "tab3 active" : "tab3"}>
                    tab3
                </div>
            </div>
            <div>
                {
                    <TabContent />
                }
            </div>
        </div>
    )
}

export default Navigator(Home);

路由里的使用方法如下:

import { UploadOutlined, UserOutlined, VideoCameraOutlined } from '@ant-design/icons';
import AsyncComponent from '../utils/AsyncComponent.js';
const Root = import('../view/root/index.jsx')
const Login = import('../view/login/index.jsx');
const Home = import('../view/home/index.jsx');

const routes = [
    {
        icon: UploadOutlined,
        name: 'root',
        path: '/',
        component: AsyncComponent(() => Root),
    },
    {
        icon: UserOutlined,
        name: 'login',
        path: '/login',
        component: AsyncComponent(() => Login),
    },
    {
        icon: VideoCameraOutlined,
        name: 'home',
        path: '/home',
        component: AsyncComponent(() => Home),
    }
];
 
export default routes

2、使用React 中的suspend lazy模式

import React, { useState, lazy, Suspense } from 'react';
import Navigator from '../../layout/navigator/index.jsx';
import './index.css';

const mapCom = {
    tab1: () => import('./components/tab1.jsx'),
    tab2: () => import('./components/tab2.jsx'),
    tab3: () => import('./components/tab3.jsx')
}

const Home = function(props) {

    const [tab, setTab] = useState('tab1');
    let TabContent = lazy(mapCom[tab]);
    return (
        <Suspense fallback={<div>Loading...</div>}>
            <div>
                <h1>
                    这里我使用的是React lazy 模式下的异步加载
                </h1>
                <div 
                    style={{
                        display: 'flex'
                    }}
                    onClick={e => {
                        setTab(e.target.className);
                    }}
                >
                    <div className={tab === "tab1" ? "tab1 active" : "tab1"}>
                        tab1
                    </div>
                    <div className={tab === "tab2" ? "tab2 active" : "tab2"}>
                        tab2
                    </div>
                    <div className={tab === "tab3" ? "tab3 active" : "tab3"}>
                        tab3
                    </div>
                </div>
                <div>
                    {
                        <TabContent />
                    }
                </div>
            </div>
        </Suspense>
    )
}

export default Navigator(Home);

suspense还有很多其他的用法,可以处理多个异步请求数据之后再渲染组件的情况
看下在dist下面都生成了什么
image.png
很神奇,都是一个分割后的代码,打开其中一个看下,发现是利用了jsonp来加载这些分离的异步组件的
image.png
看下页面的效果吧
下载 (1).gif


杨龙飞
45 声望2 粉丝

喜欢思考,喜欢前端,喜欢交友,喜欢玩